diff --git a/main.py b/main.py index 2dc4460..578c3d5 100644 --- a/main.py +++ b/main.py @@ -6,6 +6,7 @@ from fastapi.middleware.cors import CORSMiddleware from logging_env import LOGGER_CONFIG_DICT from src.routers import chat, deep_agent_chat +from src.routers import generate_3D logging.config.dictConfig(LOGGER_CONFIG_DICT) @@ -26,6 +27,7 @@ app_server.add_middleware( # 包含路由 app_server.include_router(chat.router) app_server.include_router(deep_agent_chat.router) +app_server.include_router(generate_3D.router) @app_server.get("/") diff --git a/src/core/config.py b/src/core/config.py index 9a9a2b5..38415fb 100644 --- a/src/core/config.py +++ b/src/core/config.py @@ -32,6 +32,9 @@ class Settings(BaseSettings): MONGODB_HOST: str = Field(default="localhost", description="") MONGODB_PORT: int = Field(default=27017, description="") + # --- 本地服务器配置信息 --- + IMAGE_TO_3D_MODEL_URL: str = Field(default='', description="") + # --- 外部工具api配置信息 --- TAVILY_API_KEY: str = Field(default="", description="") diff --git a/src/routers/generate_3D.py b/src/routers/generate_3D.py new file mode 100644 index 0000000..77b814a --- /dev/null +++ b/src/routers/generate_3D.py @@ -0,0 +1,151 @@ +import json +import logging + +import httpx +import requests +from fastapi import APIRouter + +from src.core.config import settings +from src.schemas.generate_3D import ImageTo3DRequest, ToSVGRequest +from src.schemas.response_template import ResponseModel + +router = APIRouter(prefix="/canvas", tags=["Furniture Canvas"]) +logger = logging.getLogger(__name__) + + +@router.post("/img_to_3D") +async def img_to_3D(request_data: ImageTo3DRequest): + """ + ### 参数说明: + - **input_images**:输入图片list,单张或多张 + - **model**: 推理模式,单张或多张 + ### 请求体示例: + ```json + 单张 + { + "input_images": ["test/img_to_3d_data/example_multi_image/character_1.png"], + "model": "single" + } + + 多张 + { + "input_imaes": [ + "test/img_to_3d_data/example_multi_image/character_1.png", + "test/img_to_3d_data/example_multi_image/character_2.png", + "test/img_to_3d_data/example_multi_image/character_3.png" + + ], + "model": "multi" + } + ``` + ### 输出示例: + ```json + { + "glb_path": "test/3d_result/glb/5ebe2fe118c94946bdc379e4d44799d2.glb", + "glb_static_img_path": "test/3d_result/png/19c4b60ab7594e3f84e58d0169739bd1.png", + "glb_info": { + "file_format": ".glb", + "vertex_count": 7312, + "centroid": [ + 0.0010040254158151611, + -0.10831894948487081, + 0.07473365460649548 + ], + "bounding_box_min": [ + -0.23948338627815247, + -0.38543057441711426, + -0.5015472769737244 + ], + "bounding_box_max": [ + 0.228701651096344, + 0.37523990869522095, + 0.49702101945877075 + ], + "size": [ + 0.46818503737449646, + 0.7606704831123352, + 0.9985682964324951 + ], + "size_ratio": [ + 0.21019126841430072, + 0.34150235681882596, + 0.4483063747668733 + ], + "size_ratio_percentage": [ + 21.019126841430072, + 34.1502356818826, + 44.83063747668733 + ] + } + } + ``` + """ + try: + logger.info( + f"img_to_3D request: {json.dumps(request_data.dict(), indent=4)}" + ) + + input_data = { + "image_paths": request_data.input_images, + "model": request_data.model, + } + + async with httpx.AsyncClient(timeout=120) as client: + resp = await client.post( + f"http://{settings.IMAGE_TO_3D_MODEL_URL}/canvas/img_to_3D", + json=input_data + ) + + result = resp.json() + + logger.info(f"img_to_3D response: {json.dumps(result, indent=4)}") + + return ResponseModel(data=result) + + except Exception as e: + logger.warning(f"img_to_3D Run Exception: {e}") + + + +@router.post("/3d_to_3views") +async def to_3views(request_data: ToSVGRequest): + """ + ### 参数说明: + - **minio_glb_path**:glb文件路径 + + ### 请求体示例: + ```json + { + "minio_glb_path": "test/3d_result/glb/543570111d344552b080ff6f875e4e83.glb" + } + ``` + ### 输出示例: + ```json + { + "minio_svg_path": "test/3d_result/svg/bbcd534cffa143bba418148a0db80ad0.svg" + } + ``` + """ + try: + logger.info( + f"img_to_3D request: {json.dumps(request_data.dict(), indent=4)}" + ) + + input_data = { + "minio_glb_path": request_data.minio_glb_path, + } + + async with httpx.AsyncClient(timeout=120) as client: + resp = await client.post( + f"http://{settings.IMAGE_TO_3D_MODEL_URL}/canvas/3d_to_3views", + json=input_data + ) + + result = resp.json() + + logger.info(f"img_to_3D response: {json.dumps(result, indent=4)}") + + return ResponseModel(data=result) + + except Exception as e: + logger.warning(f"img_to_3D Run Exception: {e}") diff --git a/src/schemas/generate_3D.py b/src/schemas/generate_3D.py new file mode 100644 index 0000000..c23e339 --- /dev/null +++ b/src/schemas/generate_3D.py @@ -0,0 +1,21 @@ +from pydantic import BaseModel, Field, confloat +from typing import Optional, List, Dict, Any + + +class ImageTo3DRequest(BaseModel): + input_images: List[str] = Field( + ..., + description="输入图片路径列表" + ) + + model: str = Field( + default="single", + description="模型类型: single 或 multi" + ) + + +class ToSVGRequest(BaseModel): + minio_glb_path: str = Field( + ..., + description="输入图片路径列表" + ) diff --git a/src/schemas/response_template.py b/src/schemas/response_template.py new file mode 100644 index 0000000..b3b773c --- /dev/null +++ b/src/schemas/response_template.py @@ -0,0 +1,8 @@ +from pydantic import BaseModel +from typing import Any, Optional + + +class ResponseModel(BaseModel): + code: int = 200 + msg: str = "OK!" + data: Optional[Any] = None diff --git a/uv.lock b/uv.lock index 5195287..f69a09a 100644 --- a/uv.lock +++ b/uv.lock @@ -4161,6 +4161,11 @@ dependencies = [ wheels = [ { url = "https://files.pythonhosted.org/packages/d3/54/a2ba279afcca44bbd320d4e73675b282fcee3d81400ea1b53934efca6462/torch-2.10.0-2-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:13ec4add8c3faaed8d13e0574f5cd4a323c11655546f91fbe6afa77b57423574", size = 79498202, upload-time = "2026-02-10T21:44:52.603Z" }, { url = "https://files.pythonhosted.org/packages/ec/23/2c9fe0c9c27f7f6cb865abcea8a4568f29f00acaeadfc6a37f6801f84cb4/torch-2.10.0-2-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:e521c9f030a3774ed770a9c011751fb47c4d12029a3d6522116e48431f2ff89e", size = 79498254, upload-time = "2026-02-10T21:44:44.095Z" }, + { url = "https://files.pythonhosted.org/packages/b3/7a/abada41517ce0011775f0f4eacc79659bc9bc6c361e6bfe6f7052a6b9363/torch-2.10.0-3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:98c01b8bb5e3240426dcde1446eed6f40c778091c8544767ef1168fc663a05a6", size = 915622781, upload-time = "2026-03-11T14:17:11.354Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c6/4dfe238342ffdcec5aef1c96c457548762d33c40b45a1ab7033bb26d2ff2/torch-2.10.0-3-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:80b1b5bfe38eb0e9f5ff09f206dcac0a87aadd084230d4a36eea5ec5232c115b", size = 915627275, upload-time = "2026-03-11T14:16:11.325Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/72bf18847f58f877a6a8acf60614b14935e2f156d942483af1ffc081aea0/torch-2.10.0-3-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:46b3574d93a2a8134b3f5475cfb98e2eb46771794c57015f6ad1fb795ec25e49", size = 915523474, upload-time = "2026-03-11T14:17:44.422Z" }, + { url = "https://files.pythonhosted.org/packages/f4/39/590742415c3030551944edc2ddc273ea1fdfe8ffb2780992e824f1ebee98/torch-2.10.0-3-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b1d5e2aba4eb7f8e87fbe04f86442887f9167a35f092afe4c237dfcaaef6e328", size = 915632474, upload-time = "2026-03-11T14:15:13.666Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8e/34949484f764dde5b222b7fe3fede43e4a6f0da9d7f8c370bb617d629ee2/torch-2.10.0-3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:0228d20b06701c05a8f978357f657817a4a63984b0c90745def81c18aedfa591", size = 915523882, upload-time = "2026-03-11T14:14:46.311Z" }, { url = "https://files.pythonhosted.org/packages/cc/af/758e242e9102e9988969b5e621d41f36b8f258bb4a099109b7a4b4b50ea4/torch-2.10.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:5fd4117d89ffd47e3dcc71e71a22efac24828ad781c7e46aaaf56bf7f2796acf", size = 145996088, upload-time = "2026-01-21T16:24:44.171Z" }, { url = "https://files.pythonhosted.org/packages/23/8e/3c74db5e53bff7ed9e34c8123e6a8bfef718b2450c35eefab85bb4a7e270/torch-2.10.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:787124e7db3b379d4f1ed54dd12ae7c741c16a4d29b49c0226a89bea50923ffb", size = 915711952, upload-time = "2026-01-21T16:23:53.503Z" }, { url = "https://files.pythonhosted.org/packages/6e/01/624c4324ca01f66ae4c7cd1b74eb16fb52596dce66dbe51eff95ef9e7a4c/torch-2.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2c66c61f44c5f903046cc696d088e21062644cbe541c7f1c4eaae88b2ad23547", size = 113757972, upload-time = "2026-01-21T16:24:39.516Z" },