Spaces:
Sleeping
Sleeping
File size: 9,281 Bytes
f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 662e51a f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 22c90d8 f813a93 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 | import subprocess
import os
import tempfile
from typing import Optional, Dict, Tuple
class TexRecon:
def __init__(self, verbose: bool = True):
"""初始化TexRecon绑定,适配mvs-texturing项目和Hugging Face Space环境"""
self.verbose = verbose
self.texrecon_path = self._find_texrecon()
# 检查是否可用,若未找到则尝试自动编译
if not self.texrecon_path:
# Hugging Face Space特征路径
self.texrecon_path = self._compile_texrecon()
self._check_texrecon()
# 创建临时目录用于处理中间文件
self.temp_dir = tempfile.TemporaryDirectory()
self.work_dir = self.temp_dir.name
def _find_texrecon(self) -> Optional[str]:
"""在常见路径中查找mvs-texturing编译的texrecon可执行文件"""
# 重点:mvs-texturing的可执行文件在build/apps/texrecon路径下
search_paths = [
"./mvs-texturing/build/apps/texrecon/texrecon",
"/home/user/app/mvs-texturing/build/apps/texrecon/texrecon",
os.path.expanduser("~/mvs-texturing/build/apps/texrecon/texrecon")
]
for path in search_paths:
if os.path.exists(path) and os.access(path, os.X_OK):
if self.verbose:
print(f"找到TexRecon可执行文件:{path}")
return path
return None
def _compile_texrecon(self) -> str:
"""在Hugging Face Space中自动编译mvs-texturing项目"""
try:
if self.verbose:
print("=== 开始自动编译mvs-texturing ===")
# 1. 安装项目所需依赖(根据官方文档)
if self.verbose:
print("=== 安装编译依赖 ===")
deps = [
"git", "cmake", "make", "g++", # 基础编译工具
"libpng-dev", "libjpeg-dev", "libtiff-dev", "libtbb-dev" # 图像和并行库
]
# Space环境无需sudo
subprocess.run(
["apt-get", "update"],
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
subprocess.run(
["apt-get", "install", "-y"] + deps,
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
# 2. 克隆mvs-texturing源码(使用官方仓库)
if self.verbose:
print("=== 克隆mvs-texturing源码 ===")
repo_dir = "/home/user/app/mvs-texturing"
src_dir = os.path.join(repo_dir, "src")
build_dir = os.path.join(repo_dir, "build")
# 确保目录存在
os.makedirs(repo_dir, exist_ok=True)
# 若目录为空则克隆
if not os.listdir(repo_dir):
subprocess.run(
["git", "clone", "https://github.com/nmoehrle/mvs-texturing.git", repo_dir],
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
else:
if self.verbose:
print("mvs-texturing源码已存在,跳过克隆")
# 3. 按照官方文档步骤编译
if self.verbose:
print("=== 编译mvs-texturing ===")
os.makedirs(build_dir, exist_ok=True)
# 运行cmake(官方命令)
subprocess.run(
["cmake", ".."],
cwd=build_dir,
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
# 运行make(官方命令,-j4并行编译)
subprocess.run(
["make", "-j4"],
cwd=build_dir,
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
# 4. 验证可执行文件(mvs-texturing的texrecon在build/apps/texrecon下)
texrecon_path = os.path.join(build_dir, "apps", "texrecon", "texrecon")
if os.path.exists(texrecon_path) and os.access(texrecon_path, os.X_OK):
if self.verbose:
print(f"=== mvs-texturing编译成功:{texrecon_path} ===")
return texrecon_path
else:
raise RuntimeError(f"编译成功但未找到可执行文件:{texrecon_path}")
except subprocess.CalledProcessError as e:
error_msg = f"命令执行失败(返回码:{e.returncode})"
if self.verbose and e.output:
error_msg += f"\n输出:{e.output.decode('utf-8', errors='ignore')}"
raise RuntimeError(error_msg)
except Exception as e:
raise RuntimeError(f"自动编译失败:{str(e)}")
def _check_texrecon(self) -> None:
"""检查TexRecon是否可执行"""
try:
subprocess.run(
[self.texrecon_path, "--help"],
check=True,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None
)
except FileNotFoundError:
raise RuntimeError(f"未找到TexRecon可执行文件:{self.texrecon_path}")
except subprocess.CalledProcessError:
raise RuntimeError(f"TexRecon可执行文件无效(可能编译不完整):{self.texrecon_path}")
def run_texturing(self,
mesh_data: bytes,
images: Dict[str, bytes],
intrinsics: str,
poses: Dict[str, str],
texture_resolution: int = 2048,
padding: int = 5,
fill_holes: bool = True) -> Tuple[bytes, bytes]:
"""运行纹理重建(适配mvs-texturing的参数要求)"""
# 创建工作目录结构
mesh_path = os.path.join(self.work_dir, "mesh.ply")
images_dir = os.path.join(self.work_dir, "images")
intrinsics_path = os.path.join(self.work_dir, "intrinsics.txt")
poses_dir = os.path.join(self.work_dir, "poses")
output_dir = os.path.join(self.work_dir, "output")
os.makedirs(images_dir, exist_ok=True)
os.makedirs(poses_dir, exist_ok=True)
os.makedirs(output_dir, exist_ok=True)
# 写入输入文件
with open(mesh_path, "wb") as f:
f.write(mesh_data)
for img_name, img_data in images.items():
img_path = os.path.join(images_dir, img_name)
with open(img_path, "wb") as f:
f.write(img_data)
with open(intrinsics_path, "w") as f:
f.write(intrinsics)
for img_name, pose_data in poses.items():
pose_path = os.path.join(poses_dir, f"{img_name}.txt")
with open(pose_path, "w") as f:
f.write(pose_data)
# 构建mvs-texturing的命令(符合官方文档参数)
cmd = [
self.texrecon_path,
"--mesh", mesh_path,
"--images", images_dir,
"--intrinsics", intrinsics_path,
"--poses", poses_dir,
"--output", output_dir,
"--resolution", str(texture_resolution),
"--padding", str(padding)
]
if fill_holes:
cmd.append("--fill-holes")
try:
subprocess.run(
cmd,
stdout=subprocess.PIPE if not self.verbose else None,
stderr=subprocess.STDOUT if not self.verbose else None,
check=True
)
# mvs-texturing的输出文件命名
textured_mesh_path = os.path.join(output_dir, "mesh_textured.obj")
texture_path = os.path.join(output_dir, "texture_0.png")
# 读取输出结果
with open(textured_mesh_path, "rb") as f:
mesh_bytes = f.read()
with open(texture_path, "rb") as f:
texture_bytes = f.read()
return mesh_bytes, texture_bytes
except subprocess.CalledProcessError as e:
error_msg = f"纹理重建命令失败(返回码:{e.returncode})"
if self.verbose and e.output:
error_msg += f"\n输出:{e.output.decode('utf-8', errors='ignore')}"
raise RuntimeError(error_msg)
def __del__(self):
"""清理临时目录"""
if hasattr(self, 'temp_dir'):
self.temp_dir.cleanup()
|