Spaces:
Sleeping
Sleeping
feat: p test func
Browse files- app.py +145 -72
- requirements.txt +2 -1
app.py
CHANGED
|
@@ -1,86 +1,159 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
import pytexrecon
|
| 3 |
-
import
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
try:
|
| 7 |
-
#
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
# 处理姿态数据
|
| 22 |
-
poses = {}
|
| 23 |
-
for img_file, pose_text in zip(image_files, pose_texts):
|
| 24 |
-
img_name = os.path.basename(img_file.name)
|
| 25 |
-
poses[img_name] = pose_text
|
| 26 |
-
|
| 27 |
-
# 运行纹理重建
|
| 28 |
-
mesh_bytes, texture_bytes = tex.run_texturing(
|
| 29 |
mesh_data=mesh_data,
|
| 30 |
images=images,
|
| 31 |
-
intrinsics=
|
| 32 |
poses=poses,
|
| 33 |
-
texture_resolution=
|
| 34 |
fill_holes=True
|
| 35 |
)
|
| 36 |
-
|
| 37 |
-
#
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
except Exception as e:
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
-
# 创建Gradio界面
|
| 50 |
-
with gr.Blocks(title="TexRecon 纹理重建工具") as demo:
|
| 51 |
-
gr.Markdown("# TexRecon 纹理重建工具")
|
| 52 |
-
gr.Markdown("上传网格模型、图像序列和相机参数,生成带纹理的3D模型")
|
| 53 |
-
|
| 54 |
with gr.Row():
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
)
|
| 83 |
|
|
|
|
|
|
|
| 84 |
if __name__ == "__main__":
|
| 85 |
-
demo.launch()
|
| 86 |
-
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import pytexrecon # 导入你之前的pytexrecon封装
|
| 3 |
+
import numpy as np
|
| 4 |
+
from PIL import Image
|
| 5 |
+
import io
|
| 6 |
+
from typing import Dict, Tuple, Optional
|
| 7 |
|
| 8 |
+
|
| 9 |
+
def generate_test_data() -> Tuple[bytes, Dict[str, bytes], str, Dict[str, str]]:
|
| 10 |
+
"""
|
| 11 |
+
生成TexRecon测试所需的模拟数据(无需真实上传):
|
| 12 |
+
返回:(网格文件二进制, 图像字典, 内参字符串, 姿态字典)
|
| 13 |
+
"""
|
| 14 |
+
# 1. 模拟网格数据:最小化PLY格式文件(一个三角形面,3个顶点)
|
| 15 |
+
ply_content = """ply
|
| 16 |
+
format ascii 1.0
|
| 17 |
+
element vertex 3
|
| 18 |
+
property float x
|
| 19 |
+
property float y
|
| 20 |
+
property float z
|
| 21 |
+
element face 1
|
| 22 |
+
property list uchar int vertex_indices
|
| 23 |
+
end_header
|
| 24 |
+
0.0 0.0 0.0
|
| 25 |
+
1.0 0.0 0.0
|
| 26 |
+
0.5 1.0 0.0
|
| 27 |
+
3 0 1 2
|
| 28 |
+
"""
|
| 29 |
+
mesh_bytes = ply_content.encode("utf-8") # 转为二进制,符合pytexrecon输入要求
|
| 30 |
+
|
| 31 |
+
# 2. 模拟图像数据:2张64x64的纯色图像(红色+蓝色,避免空文件)
|
| 32 |
+
images: Dict[str, bytes] = {}
|
| 33 |
+
# 第一张图:红色
|
| 34 |
+
red_img = Image.new("RGB", (64, 64), color="red")
|
| 35 |
+
red_img_byteio = io.BytesIO()
|
| 36 |
+
red_img.save(red_img_byteio, format="PNG")
|
| 37 |
+
images["test1.png"] = red_img_byteio.getvalue()
|
| 38 |
+
# 第二张图:蓝色
|
| 39 |
+
blue_img = Image.new("RGB", (64, 64), color="blue")
|
| 40 |
+
blue_img_byteio = io.BytesIO()
|
| 41 |
+
blue_img.save(blue_img_byteio, format="PNG")
|
| 42 |
+
images["test2.png"] = blue_img_byteio.getvalue()
|
| 43 |
+
|
| 44 |
+
# 3. 模拟相机内参:简单针孔相机参数(适配64x64图像)
|
| 45 |
+
# 格式:相机ID fx fy cx cy k1 k2 p1 p2 (k1-k2为畸变系数,p1-p2为切向畸变,此处设为0)
|
| 46 |
+
intrinsics = "0 32.0 32.0 32.0 32.0 0.0 0.0 0.0 0.0"
|
| 47 |
+
|
| 48 |
+
# 4. 模拟相机姿态:2个图像对应的简单位姿(单位矩阵,代表相机在原点)
|
| 49 |
+
# 姿态格式:4x4齐次变换矩阵(每行用空格分隔,最后一行固定为0 0 0 1)
|
| 50 |
+
pose_matrix = """1.0 0.0 0.0 0.0
|
| 51 |
+
0.0 1.0 0.0 0.0
|
| 52 |
+
0.0 0.0 1.0 2.0 # 相机在z轴2m处,朝向原点
|
| 53 |
+
0.0 0.0 0.0 1.0"""
|
| 54 |
+
poses: Dict[str, str] = {
|
| 55 |
+
"test1.png": pose_matrix, # 第一张图的姿态
|
| 56 |
+
"test2.png": pose_matrix # 第二张图的姿态(简化用同一姿态)
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
return mesh_bytes, images, intrinsics, poses
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_texrecon_flow(verbose: bool = True) -> Tuple[str, Optional[bytes], Optional[bytes], Optional[Image.Image]]:
|
| 63 |
+
"""
|
| 64 |
+
完整测试pytexrecon流程:生成模拟数据 → 调用TexRecon → 返回结果
|
| 65 |
+
返回:(测试状态, 带纹理模型二进制, 纹理图二进制, 纹理图PIL对象)
|
| 66 |
+
"""
|
| 67 |
try:
|
| 68 |
+
# 步骤1:生成模拟测试数据
|
| 69 |
+
if verbose:
|
| 70 |
+
print("=== 步骤1:生成模拟测试数据 ===")
|
| 71 |
+
mesh_data, images, intrinsics, poses = generate_test_data()
|
| 72 |
+
|
| 73 |
+
# 步骤2:初始化pytexrecon(自动检查/编译TexRecon)
|
| 74 |
+
if verbose:
|
| 75 |
+
print("=== 步骤2:初始化pytexrecon ===")
|
| 76 |
+
texrecon = pytexrecon.TexRecon(verbose=verbose)
|
| 77 |
+
|
| 78 |
+
# 步骤3:调用TexRecon纹理重建(核心测试)
|
| 79 |
+
if verbose:
|
| 80 |
+
print("=== 步骤3:调用TexRecon纹理重建 ===")
|
| 81 |
+
textured_mesh_bytes, texture_bytes = texrecon.run_texturing(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
mesh_data=mesh_data,
|
| 83 |
images=images,
|
| 84 |
+
intrinsics=intrinsics,
|
| 85 |
poses=poses,
|
| 86 |
+
texture_resolution=256, # 小分辨率加速测试
|
| 87 |
fill_holes=True
|
| 88 |
)
|
| 89 |
+
|
| 90 |
+
# 步骤4:处理纹理图(转为PIL对象,方便Gradio显示)
|
| 91 |
+
texture_img = Image.open(io.BytesIO(texture_bytes)) if texture_bytes else None
|
| 92 |
+
|
| 93 |
+
# 步骤5:返回成功结果
|
| 94 |
+
status = (
|
| 95 |
+
"✅ 全流程测试成功!\n"
|
| 96 |
+
"- TexRecon可执行文件正常(已找到/编译)\n"
|
| 97 |
+
"- 模拟数据生成正常\n"
|
| 98 |
+
"- 纹理重建命令执行成功\n"
|
| 99 |
+
"- 已生成带纹理模型和纹理图"
|
| 100 |
+
)
|
| 101 |
+
return status, textured_mesh_bytes, texture_bytes, texture_img
|
| 102 |
+
|
| 103 |
except Exception as e:
|
| 104 |
+
# 捕获所有异常,返回错误信息
|
| 105 |
+
status = f"❌ 测试失败:\n{str(e)}"
|
| 106 |
+
return status, None, None, None
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# 构建Gradio界面:一键测试+结果展示
|
| 110 |
+
with gr.Blocks(title="pytexrecon 模拟测试工具") as demo:
|
| 111 |
+
gr.Markdown("# pytexrecon 全流程模拟测试")
|
| 112 |
+
gr.Markdown("无需上传真实数据,点击按钮即可测试封装是否正常工作(自动生成模拟数据)")
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
with gr.Row():
|
| 115 |
+
# 测试按钮(点击触发全流程)
|
| 116 |
+
test_btn = gr.Button("🚀 开始模拟测试", variant="primary", size="lg")
|
| 117 |
+
|
| 118 |
+
# 结果展示区域
|
| 119 |
+
with gr.Group():
|
| 120 |
+
gr.Markdown("### 测试状态")
|
| 121 |
+
status_box = gr.Textbox(label="状态日志", lines=5, interactive=False)
|
| 122 |
+
|
| 123 |
+
gr.Markdown("### 输出结果预览")
|
| 124 |
+
with gr.Row():
|
| 125 |
+
# 纹理图预览(可视化验证)
|
| 126 |
+
texture_preview = gr.Image(label="生成的纹理图", interactive=False)
|
| 127 |
+
# 带纹理模型下载(验证文件是否正常生成)
|
| 128 |
+
mesh_download = gr.File(label="下载带纹理模型(.obj)", interactive=False)
|
| 129 |
+
# 纹理图下载
|
| 130 |
+
texture_download = gr.File(label="下载纹理图(.png)", interactive=False)
|
| 131 |
+
|
| 132 |
+
# 绑定按钮事件:点击后跑测试,更新结果
|
| 133 |
+
def on_test_click():
|
| 134 |
+
status, mesh_bytes, texture_bytes, texture_img = test_texrecon_flow()
|
| 135 |
+
# 处理下载文件(Gradio需要文件路径或二进制+文件名)
|
| 136 |
+
mesh_file = gr.File.update(
|
| 137 |
+
value=mesh_bytes,
|
| 138 |
+
label="下载带纹理模型(.obj)",
|
| 139 |
+
file_name="test_textured_mesh.obj"
|
| 140 |
+
) if mesh_bytes else gr.File.update(value=None, label="下载带纹理模型(.obj)")
|
| 141 |
+
|
| 142 |
+
texture_file = gr.File.update(
|
| 143 |
+
value=texture_bytes,
|
| 144 |
+
label="下载纹理图(.png)",
|
| 145 |
+
file_name="test_texture.png"
|
| 146 |
+
) if texture_bytes else gr.File.update(value=None, label="下载纹理图(.png)")
|
| 147 |
+
|
| 148 |
+
return status, mesh_file, texture_file, texture_img
|
| 149 |
+
|
| 150 |
+
# 绑定点击事件(无输入,直接触发)
|
| 151 |
+
test_btn.click(
|
| 152 |
+
fn=on_test_click,
|
| 153 |
+
outputs=[status_box, mesh_download, texture_download, texture_preview]
|
| 154 |
)
|
| 155 |
|
| 156 |
+
|
| 157 |
+
# 启动应用(适配Hugging Face Space,默认使用0.0.0.0地址)
|
| 158 |
if __name__ == "__main__":
|
| 159 |
+
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
requirements.txt
CHANGED
|
@@ -1,3 +1,4 @@
|
|
| 1 |
gradio>=3.0
|
| 2 |
-
|
|
|
|
| 3 |
|
|
|
|
| 1 |
gradio>=3.0
|
| 2 |
+
pillow>=9.0
|
| 3 |
+
numpy>=1.21
|
| 4 |
|