File size: 3,823 Bytes
c59decc
747fb82
 
 
 
c59decc
 
6496b60
c59decc
 
747fb82
 
 
 
 
c59decc
747fb82
c59decc
6496b60
 
 
 
 
 
 
 
 
747fb82
 
c59decc
 
747fb82
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c59decc
747fb82
 
 
 
 
 
c59decc
747fb82
c59decc
 
 
 
747fb82
 
 
 
 
c59decc
747fb82
c59decc
 
 
 
 
 
 
 
 
747fb82
 
c59decc
747fb82
 
c59decc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747fb82
 
c59decc
747fb82
 
 
 
c59decc
 
 
 
 
 
 
 
 
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
import os
import sys
import io
import uuid
import subprocess
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from PIL import Image

# Add the cloned repository to the system path so Python can locate the modules
sys.path.append("/app/Hunyuan3D-2")

from hy3dgen.shapegen import Hunyuan3DDiTFlowMatchingPipeline
from hy3dgen.texgen import Hunyuan3DPaintPipeline

app = FastAPI(title="Hunyuan3D-2 Multi-View Textured API")

# Add CORS so your HTML file can communicate with the API
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"], 
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

shape_pipeline = None
paint_pipeline = None
OUTPUT_DIR = "/app/outputs"

def start_tmate():
    """Starts a tmate session in the background and prints the SSH command."""
    print("Starting tmate for SSH access...")
    try:
        subprocess.run(["tmate", "-S", "/tmp/tmate.sock", "new-session", "-d"], check=True)
        subprocess.run(["tmate", "-S", "/tmp/tmate.sock", "wait", "tmate-ready"], check=True)
        result = subprocess.run(
            ["tmate", "-S", "/tmp/tmate.sock", "display", "-p", "#{tmate_ssh}"], 
            capture_output=True, text=True, check=True
        )
        ssh_command = result.stdout.strip()
        print("\n" + "="*60)
        print("🚀 TMATE SSH CONNECTION STRING READY 🚀")
        print(f"Run this command in your local terminal:\n\n{ssh_command}")
        print("="*60 + "\n")
    except Exception as e:
        print(f"Failed to start tmate: {e}")

@app.on_event("startup")
async def startup_event():
    """Runs on server startup to initialize tmate and load models."""
    start_tmate()
    
    global shape_pipeline, paint_pipeline
    print("Loading Hunyuan3D-2 Shape and Paint models...")
    try:
        shape_pipeline = Hunyuan3DDiTFlowMatchingPipeline.from_pretrained(
            'tencent/Hunyuan3D-2mv', 
            subfolder='hunyuan3d-dit-v2-mv',
            device='cuda'
        )
        paint_pipeline = Hunyuan3DPaintPipeline.from_pretrained(
            'tencent/Hunyuan3D-2',
            device='cuda'
        )
        print("Both models loaded successfully.")
    except Exception as e:
        print(f"Error loading models: {e}")

@app.post("/generate-3d")
async def generate_3d_model(
    front: UploadFile = File(...),
    back: UploadFile = File(...),
    left: UploadFile = File(...),
    right: UploadFile = File(...)
):
    """
    Endpoint that accepts 4 structural views, generates the shape, 
    paints the texture, and returns a colored .glb 3D file.
    """
    if shape_pipeline is None or paint_pipeline is None:
        raise HTTPException(status_code=503, detail="Models are still loading or failed to load.")

    try:
        def read_image(file: UploadFile):
            contents = file.file.read()
            return Image.open(io.BytesIO(contents)).convert("RGB")

        images_dict = {
            "front": read_image(front),
            "back": read_image(back),
            "left": read_image(left),
            "right": read_image(right)
        }

        job_id = str(uuid.uuid4())
        output_path = os.path.join(OUTPUT_DIR, f"{job_id}.glb")

        print("Generating shape geometry...")
        mesh = shape_pipeline(image=images_dict)[0]
        
        print("Applying textures...")
        mesh_with_texture = paint_pipeline(mesh, image=images_dict["front"])

        mesh_with_texture.export(output_path)

        return FileResponse(
            path=output_path, 
            media_type="model/gltf-binary", 
            filename=f"{job_id}.glb"
        )

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")