Spaces:
Sleeping
Sleeping
fix: fix app
Browse files
app.py
CHANGED
|
@@ -1,17 +1,34 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
import pytexrecon # 导入你
|
| 3 |
import numpy as np
|
| 4 |
from PIL import Image
|
| 5 |
import io
|
| 6 |
from typing import Dict, Tuple, Optional
|
| 7 |
|
| 8 |
|
| 9 |
-
|
|
|
|
| 10 |
"""
|
| 11 |
-
|
| 12 |
-
|
| 13 |
"""
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
ply_content = """ply
|
| 16 |
format ascii 1.0
|
| 17 |
element vertex 3
|
|
@@ -26,59 +43,42 @@ end_header
|
|
| 26 |
0.5 1.0 0.0
|
| 27 |
3 0 1 2
|
| 28 |
"""
|
| 29 |
-
mesh_bytes = ply_content.encode("utf-8")
|
| 30 |
|
| 31 |
-
# 2. 模拟
|
| 32 |
images: Dict[str, bytes] = {}
|
| 33 |
-
#
|
| 34 |
red_img = Image.new("RGB", (64, 64), color="red")
|
| 35 |
-
|
| 36 |
-
red_img.save(
|
| 37 |
-
images["test1.png"] =
|
| 38 |
-
#
|
| 39 |
blue_img = Image.new("RGB", (64, 64), color="blue")
|
| 40 |
-
|
| 41 |
-
blue_img.save(
|
| 42 |
-
images["test2.png"] =
|
| 43 |
|
| 44 |
-
# 3. 模拟相机内参
|
| 45 |
-
#
|
| 46 |
-
intrinsics = "0 32.0 32.0 32.0 32.0 0.0 0.0 0.0 0.0"
|
| 47 |
|
| 48 |
-
# 4. 模拟相机姿态
|
| 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
|
| 53 |
0.0 0.0 0.0 1.0"""
|
| 54 |
-
poses:
|
| 55 |
-
"test1.png": pose_matrix, # 第一张图的姿态
|
| 56 |
-
"test2.png": pose_matrix # 第二张图的姿态(简化用同一姿态)
|
| 57 |
-
}
|
| 58 |
|
| 59 |
return mesh_bytes, images, intrinsics, poses
|
| 60 |
|
| 61 |
|
| 62 |
-
def
|
| 63 |
-
"""
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
try:
|
| 68 |
-
#
|
| 69 |
-
|
| 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,
|
|
@@ -86,74 +86,117 @@ def test_texrecon_flow(verbose: bool = True) -> Tuple[str, Optional[bytes], Opti
|
|
| 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 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
except Exception as e:
|
| 104 |
-
# 捕获
|
| 105 |
status = f"❌ 测试失败:\n{str(e)}"
|
| 106 |
-
return status, None, None, None
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
-
#
|
| 110 |
-
|
| 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="状态
|
| 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 |
-
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
mesh_file = gr.File.update(
|
| 137 |
value=mesh_bytes,
|
| 138 |
-
|
| 139 |
-
|
| 140 |
) if mesh_bytes else gr.File.update(value=None, label="下载带纹理模型(.obj)")
|
| 141 |
|
| 142 |
-
|
| 143 |
-
value=
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
) if
|
| 147 |
-
|
| 148 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
|
| 150 |
-
# 绑定
|
| 151 |
test_btn.click(
|
| 152 |
fn=on_test_click,
|
| 153 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
)
|
| 155 |
|
| 156 |
|
| 157 |
-
# 启动应用(适配Hugging Face Space
|
| 158 |
if __name__ == "__main__":
|
| 159 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|
|
|
|
| 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 |
+
# -------------------------- 核心功能:拆分步骤,优先验证TexRecon实例 --------------------------
|
| 10 |
+
def init_texrecon(verbose: bool = True) -> pytexrecon.TexRecon:
|
| 11 |
"""
|
| 12 |
+
单独初始化TexRecon实例(优先验证核心依赖)
|
| 13 |
+
若初始化失败(如编译失败、可执行文件无效),直接抛出异常
|
| 14 |
"""
|
| 15 |
+
if verbose:
|
| 16 |
+
print("=== 步骤1/3:初始化TexRecon实例(优先验证) ===")
|
| 17 |
+
try:
|
| 18 |
+
# 调用pytexrecon的构造函数,内部会自动检查/编译TexRecon
|
| 19 |
+
texrecon_instance = pytexrecon.TexRecon(verbose=verbose)
|
| 20 |
+
# 额外验证一次可执行文件(双重保险)
|
| 21 |
+
texrecon_instance._check_texrecon()
|
| 22 |
+
return texrecon_instance
|
| 23 |
+
except Exception as e:
|
| 24 |
+
# 明确标注是“初始化失败”,方便定位
|
| 25 |
+
raise RuntimeError(f"TexRecon实例初始化失败(核心依赖问题):\n{str(e)}")
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def generate_test_data() -> Tuple[bytes, Dict[str, bytes], str, Dict[str, str]]:
|
| 29 |
+
"""生成TexRecon测试所需的模拟数据(不变,复用原逻辑)"""
|
| 30 |
+
print("=== 步骤2/3:生成模拟测试数据 ===")
|
| 31 |
+
# 1. 模拟PLY网格(3个顶点+1个三角形面)
|
| 32 |
ply_content = """ply
|
| 33 |
format ascii 1.0
|
| 34 |
element vertex 3
|
|
|
|
| 43 |
0.5 1.0 0.0
|
| 44 |
3 0 1 2
|
| 45 |
"""
|
| 46 |
+
mesh_bytes = ply_content.encode("utf-8")
|
| 47 |
|
| 48 |
+
# 2. 模拟2张64x64纯色图像(红+蓝)
|
| 49 |
images: Dict[str, bytes] = {}
|
| 50 |
+
# 红色图像
|
| 51 |
red_img = Image.new("RGB", (64, 64), color="red")
|
| 52 |
+
red_byteio = io.BytesIO()
|
| 53 |
+
red_img.save(red_byteio, format="PNG")
|
| 54 |
+
images["test1.png"] = red_byteio.getvalue()
|
| 55 |
+
# 蓝色图像
|
| 56 |
blue_img = Image.new("RGB", (64, 64), color="blue")
|
| 57 |
+
blue_byteio = io.BytesIO()
|
| 58 |
+
blue_img.save(blue_byteio, format="PNG")
|
| 59 |
+
images["test2.png"] = blue_byteio.getvalue()
|
| 60 |
|
| 61 |
+
# 3. 模拟相机内参(针孔相机,适配64x64图像)
|
| 62 |
+
intrinsics = "0 32.0 32.0 32.0 32.0 0.0 0.0 0.0 0.0" # 相机ID fx fy cx cy k1 k2 p1 p2
|
|
|
|
| 63 |
|
| 64 |
+
# 4. 模拟相机姿态(单位矩阵,相机在z轴2m处)
|
|
|
|
| 65 |
pose_matrix = """1.0 0.0 0.0 0.0
|
| 66 |
0.0 1.0 0.0 0.0
|
| 67 |
+
0.0 0.0 1.0 2.0
|
| 68 |
0.0 0.0 0.0 1.0"""
|
| 69 |
+
poses = {"test1.png": pose_matrix, "test2.png": pose_matrix}
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
return mesh_bytes, images, intrinsics, poses
|
| 72 |
|
| 73 |
|
| 74 |
+
def run_texturing_with_test_data(texrecon_instance: pytexrecon.TexRecon) -> Tuple[bytes, bytes, Image.Image]:
|
| 75 |
+
"""用模拟数据调用TexRecon纹理重建(依赖已验证的实例)"""
|
| 76 |
+
print("=== 步骤3/3:执行纹理重建 ===")
|
| 77 |
+
# 生成模拟数据
|
| 78 |
+
mesh_data, images, intrinsics, poses = generate_test_data()
|
| 79 |
try:
|
| 80 |
+
# 调用pytexrecon的核心功能
|
| 81 |
+
textured_mesh_bytes, texture_bytes = texrecon_instance.run_texturing(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
mesh_data=mesh_data,
|
| 83 |
images=images,
|
| 84 |
intrinsics=intrinsics,
|
|
|
|
| 86 |
texture_resolution=256, # 小分辨率加速测试
|
| 87 |
fill_holes=True
|
| 88 |
)
|
| 89 |
+
# 转换纹理图为PIL对象(用于预览)
|
| 90 |
+
texture_img = Image.open(io.BytesIO(texture_bytes))
|
| 91 |
+
return textured_mesh_bytes, texture_bytes, texture_img
|
| 92 |
+
except Exception as e:
|
| 93 |
+
raise RuntimeError(f"纹理重建执行失败:\n{str(e)}")
|
| 94 |
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
# -------------------------- 测试流程:分步执行,优先验证实例 --------------------------
|
| 97 |
+
def full_test_flow() -> Tuple[str, Optional[pytexrecon.TexRecon], Optional[bytes], Optional[bytes], Optional[Image.Image]]:
|
| 98 |
+
"""
|
| 99 |
+
完整测试流程(分步反馈):
|
| 100 |
+
1. 优先初始化TexRecon实例(验证核心依赖)
|
| 101 |
+
2. 生成模拟数据
|
| 102 |
+
3. 执行纹理重建
|
| 103 |
+
返回:(状态日志, TexRecon实例, 带纹理模型, 纹理图, 纹理图预览)
|
| 104 |
+
"""
|
| 105 |
+
status = ""
|
| 106 |
+
texrecon_instance = None
|
| 107 |
+
textured_mesh_bytes = None
|
| 108 |
+
texture_bytes = None
|
| 109 |
+
texture_img = None
|
| 110 |
+
|
| 111 |
+
try:
|
| 112 |
+
# 步骤1:优先初始化TexRecon(核心验证)
|
| 113 |
+
texrecon_instance = init_texrecon()
|
| 114 |
+
status += "✅ 步骤1/3:TexRecon实例初始化成功!\n"
|
| 115 |
+
status += f"- 可执行文件路径:{texrecon_instance.texrecon_path}\n"
|
| 116 |
+
|
| 117 |
+
# 步骤2+3:生成数据并执行纹理重建
|
| 118 |
+
textured_mesh_bytes, texture_bytes, texture_img = run_texturing_with_test_data(texrecon_instance)
|
| 119 |
+
status += "✅ 步骤2/3:模拟测试数据生成成功!\n"
|
| 120 |
+
status += "✅ 步骤3/3:纹理重建执行成功!\n"
|
| 121 |
+
status += "- 已生成带纹理模型(.obj)和纹理图(.png)\n"
|
| 122 |
|
| 123 |
except Exception as e:
|
| 124 |
+
# 捕获不同阶段的错误,明确标注
|
| 125 |
status = f"❌ 测试失败:\n{str(e)}"
|
|
|
|
| 126 |
|
| 127 |
+
return status, texrecon_instance, textured_mesh_bytes, texture_bytes, texture_img
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
# -------------------------- Gradio界面:分步反馈,清晰展示进度 --------------------------
|
| 131 |
+
with gr.Blocks(title="pytexrecon 分步测试工具") as demo:
|
| 132 |
+
gr.Markdown("# pytexrecon 全流程测试(优先验证TexRecon)")
|
| 133 |
+
gr.Markdown("🔍 逻辑优化:**先验证TexRecon实例可用性,再执行后续流程**,更早定位核心问题")
|
| 134 |
|
| 135 |
+
# 存储TexRecon实例(用gr.State保存,避免重复初始化)
|
| 136 |
+
texrecon_state = gr.State(None)
|
|
|
|
|
|
|
| 137 |
|
| 138 |
with gr.Row():
|
| 139 |
+
test_btn = gr.Button("🚀 开始全流程测试", variant="primary", size="lg")
|
|
|
|
| 140 |
|
| 141 |
+
# 结果展示:分阶段反馈
|
| 142 |
with gr.Group():
|
| 143 |
+
gr.Markdown("### 测试状态日志(分步反馈)")
|
| 144 |
+
status_box = gr.Textbox(label="状态", lines=8, interactive=False, placeholder="点击测试按钮开始...")
|
| 145 |
|
| 146 |
+
gr.Markdown("### 核心验证结果(TexRecon实例)")
|
| 147 |
+
texrecon_path_box = gr.Textbox(label="TexRecon可执行文件路径", interactive=False, placeholder="初始化成功后显示...")
|
| 148 |
+
|
| 149 |
+
gr.Markdown("### 最终输出结果")
|
| 150 |
with gr.Row():
|
| 151 |
+
texture_preview = gr.Image(label="纹理图预览", interactive=False, placeholder="重建成功后显示...")
|
|
|
|
|
|
|
| 152 |
mesh_download = gr.File(label="下载带纹理模型(.obj)", interactive=False)
|
|
|
|
| 153 |
texture_download = gr.File(label="下载纹理图(.png)", interactive=False)
|
| 154 |
|
| 155 |
+
# 绑定按钮事件:分步更新结果
|
| 156 |
def on_test_click():
|
| 157 |
+
# 执行全流程测试(优先验证实例)
|
| 158 |
+
status, texrecon_inst, mesh_bytes, tex_bytes, tex_img = full_test_flow()
|
| 159 |
+
|
| 160 |
+
# 整理TexRecon实例信息(仅当初始化成功时显示)
|
| 161 |
+
texrecon_path = texrecon_inst.texrecon_path if texrecon_inst else "未初始化"
|
| 162 |
+
|
| 163 |
+
# 整理下载文件(仅当重建成功时提供)
|
| 164 |
mesh_file = gr.File.update(
|
| 165 |
value=mesh_bytes,
|
| 166 |
+
file_name="test_textured_mesh.obj",
|
| 167 |
+
label="下载带纹理模型(.obj)"
|
| 168 |
) if mesh_bytes else gr.File.update(value=None, label="下载带纹理模型(.obj)")
|
| 169 |
|
| 170 |
+
tex_file = gr.File.update(
|
| 171 |
+
value=tex_bytes,
|
| 172 |
+
file_name="test_texture.png",
|
| 173 |
+
label="下载纹理图(.png)"
|
| 174 |
+
) if tex_bytes else gr.File.update(value=None, label="下载纹理图(.png)")
|
| 175 |
+
|
| 176 |
+
# 更新界面状态
|
| 177 |
+
return (
|
| 178 |
+
status, # 状态日志
|
| 179 |
+
texrecon_inst, # 保存实例到State
|
| 180 |
+
texrecon_path, # 显示可执行文件路径
|
| 181 |
+
mesh_file, # 模型下载
|
| 182 |
+
tex_file, # 纹理图下载
|
| 183 |
+
tex_img # 纹理图预览
|
| 184 |
+
)
|
| 185 |
|
| 186 |
+
# 绑定输出映射(顺序对应on_test_click的返回值)
|
| 187 |
test_btn.click(
|
| 188 |
fn=on_test_click,
|
| 189 |
+
outputs=[
|
| 190 |
+
status_box, # 状态日志
|
| 191 |
+
texrecon_state, # 保存实例(供后续复用,可选)
|
| 192 |
+
texrecon_path_box, # 可执行文件路径
|
| 193 |
+
mesh_download, # 模型下载
|
| 194 |
+
texture_download, # 纹理图下载
|
| 195 |
+
texture_preview # 纹理图预览
|
| 196 |
+
]
|
| 197 |
)
|
| 198 |
|
| 199 |
|
| 200 |
+
# 启动应用(适配Hugging Face Space)
|
| 201 |
if __name__ == "__main__":
|
| 202 |
demo.launch(server_name="0.0.0.0", server_port=7860)
|