This commit is contained in:
zcr
2026-04-13 11:36:10 +08:00
parent f4670217d1
commit e670609f8e
3 changed files with 81 additions and 83 deletions

9
docker-compose.yaml Normal file → Executable file
View File

@@ -1,7 +1,7 @@
services: services:
trellis: trellis:
image: zhouchengrong/fida-trellis:latest # 你 commit 的镜像 image: zhouchengrong/fida-trellis-a6000:latest # 你 commit 的镜像
container_name: trellis-dev container_name: FiDA_3D_Trellis
restart: unless-stopped restart: unless-stopped
environment: environment:
- NVIDIA_VISIBLE_DEVICES=all - NVIDIA_VISIBLE_DEVICES=all
@@ -24,8 +24,5 @@ services:
capabilities: [ gpu, compute, utility ] # 移除 video非必需减少兼容问题 capabilities: [ gpu, compute, utility ] # 移除 video非必需减少兼容问题
command: > command: >
bash -c " bash -c "
/opt/conda/envs/trellis/bin/python -c 'import torch; print(torch.__version__, torch.cuda.is_available())' && conda run --no-capture-output -n trellis python server.py
/opt/conda/envs/trellis/bin/python server.py
" "
# /opt/conda/envs/trellis/bin/python server.py
# tail -f /dev/null

144
server.py Normal file → Executable file
View File

@@ -144,11 +144,12 @@ class TrellisAPI(ls.LitAPI):
def decode_request(self, request): def decode_request(self, request):
image_paths = request["image_paths"] image_paths = request["image_paths"]
bucket_name = request["bucket_name"]
user_id = request["user_id"]
images = [] images = []
for path in image_paths: for path in image_paths:
bucket = path.split('/')[0] bucket, object_name = path.split('/', 1)
object_name = path[path.find('/') + 1:]
image = minio_get_image(minio_client, bucket, object_name) image = minio_get_image(minio_client, bucket, object_name)
images.append(image) images.append(image)
@@ -163,6 +164,8 @@ class TrellisAPI(ls.LitAPI):
"simplify": request.get("simplify", 0.95), "simplify": request.get("simplify", 0.95),
"texture_size": request.get("texture_size", 1024), "texture_size": request.get("texture_size", 1024),
"fps": request.get("fps", 30), "fps": request.get("fps", 30),
"bucket_name": bucket_name,
"user_id": user_id
} }
return images, params return images, params
@@ -202,8 +205,7 @@ class TrellisAPI(ls.LitAPI):
glb_info = analyze_mesh(local_glb_path) glb_info = analyze_mesh(local_glb_path)
local_static_model_image_path = os.path.join("glb_output", generate_unique_name("static_model_image.png")) static_model_image = self.get_static_model_image(model_path=local_glb_path, params=params)
static_model_image = self.get_static_model_image(model_path=local_glb_path, output_path=local_static_model_image_path)
return { return {
"glb_path": minio_glb_path, "glb_path": minio_glb_path,
@@ -214,49 +216,49 @@ class TrellisAPI(ls.LitAPI):
def encode_response(self, output): def encode_response(self, output):
return output return output
def upload_video(self, outputs, params): # def upload_video(self, outputs, params):
gaussian_name = f"3d_result/video/{params['file_name']}-gaussian.mp4" # gaussian_name = f"3d_result/video/{params['file_name']}-gaussian.mp4"
radiance_field_name = f"3d_result/video/{params['file_name']}-radiance_field.mp4" # radiance_field_name = f"3d_result/video/{params['file_name']}-radiance_field.mp4"
mesh_name = f"3d_result/video/{params['file_name']}-mesh.mp4" # mesh_name = f"3d_result/video/{params['file_name']}-mesh.mp4"
#
# gaussian video # # gaussian video
video = render_utils.render_video(outputs["gaussian"][0])["color"] # video = render_utils.render_video(outputs["gaussian"][0])["color"]
buffer = BytesIO() # buffer = BytesIO()
imageio.mimsave(buffer, video, format="mp4", fps=params['fps']) # imageio.mimsave(buffer, video, format="mp4", fps=params['fps'])
gaussian_video_path = upload_bytes( # gaussian_video_path = upload_bytes(
buffer.getvalue(), # buffer.getvalue(),
gaussian_name, # gaussian_name,
"video/mp4", # "video/mp4",
) # )
#
# radiance field video # # radiance field video
video = render_utils.render_video(outputs["radiance_field"][0])["color"] # video = render_utils.render_video(outputs["radiance_field"][0])["color"]
buffer = BytesIO() # buffer = BytesIO()
imageio.mimsave(buffer, video, format="mp4", fps=params['fps']) # imageio.mimsave(buffer, video, format="mp4", fps=params['fps'])
radiance_field_video_path = upload_bytes( # radiance_field_video_path = upload_bytes(
buffer.getvalue(), # buffer.getvalue(),
radiance_field_name, # radiance_field_name,
"video/mp4", # "video/mp4",
) # )
#
# mesh video # # mesh video
video = render_utils.render_video(outputs["mesh"][0])["normal"] # video = render_utils.render_video(outputs["mesh"][0])["normal"]
buffer = BytesIO() # buffer = BytesIO()
imageio.mimsave(buffer, video, format="mp4", fps=params['fps']) # imageio.mimsave(buffer, video, format="mp4", fps=params['fps'])
mesh_path = upload_bytes( # mesh_path = upload_bytes(
buffer.getvalue(), # buffer.getvalue(),
mesh_name, # mesh_name,
"video/mp4", # "video/mp4",
) # )
#
return { # return {
"gaussian": gaussian_video_path, # "gaussian": gaussian_video_path,
"radiance_field": radiance_field_video_path, # "radiance_field": radiance_field_video_path,
"mesh": mesh_path # "mesh": mesh_path
} # }
def upload_glb(self, outputs, params): def upload_glb(self, outputs, params):
file_name = f"3d_result/glb/{params['file_name']}.glb" minio_path = f"{params['bucket_name']}/{params['user_id']}/3d_result/{params['file_name']}.glb"
local_glb_path = os.path.join("glb_output", generate_unique_name("sample.glb")) local_glb_path = os.path.join("glb_output", generate_unique_name("sample.glb"))
out_dir = os.path.dirname(local_glb_path) out_dir = os.path.dirname(local_glb_path)
if out_dir: if out_dir:
@@ -275,31 +277,28 @@ class TrellisAPI(ls.LitAPI):
) )
glb_path = upload_local_file( glb_path = upload_local_file(
local_glb_path, file_path=local_glb_path,
file_name, minio_path=minio_path,
"application/octet-stream", content_type="application/octet-stream"
) )
return glb_path, local_glb_path return glb_path, local_glb_path
def upload_ply(self, outputs, params): # def upload_ply(self, outputs, params):
file_name = f"3d_result/ply/{params['file_name']}.ply" # file_name = f"3d_result/ply/{params['file_name']}.ply"
#
# with tempfile.NamedTemporaryFile(suffix=".ply") as tmp:
# outputs["gaussian"][0].save_ply(tmp.name)
# tmp.seek(0)
#
# ply_path = upload_bytes(
# tmp.read(),
# file_name,
# "application/octet-stream",
# )
# return {"ply": ply_path}
with tempfile.NamedTemporaryFile(suffix=".ply") as tmp: def get_static_model_image(self, model_path, params):
outputs["gaussian"][0].save_ply(tmp.name) local_static_model_image_path = os.path.join("glb_output", generate_unique_name("static_model_image.png"))
tmp.seek(0)
ply_path = upload_bytes(
tmp.read(),
file_name,
"application/octet-stream",
)
return {"ply": ply_path}
def get_static_model_image(self, model_path, output_path):
local_static_model_image_path = os.path.join(
"glb_output",
generate_unique_name("static_model_image.png")
)
print(f"model_path : {model_path}") print(f"model_path : {model_path}")
print(f"local_static_model_image_path :{local_static_model_image_path}") print(f"local_static_model_image_path :{local_static_model_image_path}")
@@ -307,17 +306,18 @@ class TrellisAPI(ls.LitAPI):
static_model_image = self.upload_local_file( static_model_image = self.upload_local_file(
output_path, output_path,
"png" params['bucket_name'],
params['user_id'],
) )
print(f"Saved to {static_model_image}") print(f"Saved to {static_model_image}")
return static_model_image return static_model_image
def upload_local_file(self, local_path, type): def upload_local_file(self, local_path, bucket_name, user_id):
""" """
通用上传函数:支持 SVG, PNG, OBJ 等 通用上传函数:支持 SVG, PNG, OBJ 等
""" """
object_name = f"3d_result/{type}/{uuid.uuid4().hex}.{type}" object_name = f"{user_id}/3d_result/{uuid.uuid4().hex}.png"
if not os.path.exists(local_path): if not os.path.exists(local_path):
print(f"错误: 文件 {local_path} 不存在") print(f"错误: 文件 {local_path} 不存在")
return None return None
@@ -330,13 +330,13 @@ class TrellisAPI(ls.LitAPI):
try: try:
minio_client.fput_object( minio_client.fput_object(
bucket_name=MINIO_BUCKET, bucket_name=bucket_name,
object_name=object_name, object_name=object_name,
file_path=local_path, file_path=local_path,
content_type=content_type content_type=content_type
) )
print(f"成功上传 [{content_type}]: {object_name}") print(f"成功上传 [{content_type}]: {object_name}")
return f"{MINIO_BUCKET}/{object_name}" return f"{bucket_name}/{object_name}"
except Exception as e: except Exception as e:
print(f"上传失败: {e}") print(f"上传失败: {e}")
return None return None

9
utils/new_oss_client.py Normal file → Executable file
View File

@@ -76,7 +76,7 @@ def upload_bytes(data_bytes, object_name, content_type):
return f"{MINIO_BUCKET}/{object_name}" return f"{MINIO_BUCKET}/{object_name}"
def upload_local_file(file_path, object_name, content_type="application/octet-stream"): def upload_local_file(file_path, minio_path, content_type="application/octet-stream"):
""" """
将本地磁盘上的文件上传到 MinIO 将本地磁盘上的文件上传到 MinIO
:param file_path: 本地文件路径 (如: 'output/sample.obj') :param file_path: 本地文件路径 (如: 'output/sample.obj')
@@ -88,14 +88,15 @@ def upload_local_file(file_path, object_name, content_type="application/octet-st
raise FileNotFoundError(f"本地文件未找到: {file_path}") raise FileNotFoundError(f"本地文件未找到: {file_path}")
# 使用 fput_object 直接从磁盘流式上传,不占用过多内存 # 使用 fput_object 直接从磁盘流式上传,不占用过多内存
bucket, object_name = minio_path.split('/', 1)
minio_client.fput_object( minio_client.fput_object(
bucket_name=MINIO_BUCKET, bucket_name=bucket,
object_name=object_name, object_name=object_name,
file_path=file_path, file_path=file_path,
content_type=content_type content_type=content_type
) )
return f"{bucket}/{object_name}"
return f"{MINIO_BUCKET}/{object_name}"
def download_from_minio(object_path, local_path): def download_from_minio(object_path, local_path):