stevee00 commited on
Commit
04de76b
·
verified ·
1 Parent(s): 44963e7

Upload api/main.py

Browse files
Files changed (1) hide show
  1. api/main.py +157 -0
api/main.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FastAPI backend for InteriorFusion inference service."""
2
+
3
+ import os
4
+ import tempfile
5
+ from pathlib import Path
6
+ from typing import List, Optional
7
+
8
+ import torch
9
+ from fastapi import FastAPI, File, Form, UploadFile
10
+ from fastapi.middleware.cors import CORSMiddleware
11
+ from fastapi.responses import FileResponse, JSONResponse
12
+ from PIL import Image
13
+ import io
14
+ import base64
15
+
16
+ from interiorfusion.pipelines import InteriorFusionPipeline, InteriorFusionOutput
17
+
18
+ app = FastAPI(
19
+ title="InteriorFusion API",
20
+ description="Single image to 3D interior scene generation",
21
+ version="0.1.0",
22
+ )
23
+
24
+ # CORS
25
+ app.add_middleware(
26
+ CORSMiddleware,
27
+ allow_origins=["*"],
28
+ allow_credentials=True,
29
+ allow_methods=["*"],
30
+ allow_headers=["*"],
31
+ )
32
+
33
+ # Global pipeline instance
34
+ _pipeline: Optional[InteriorFusionPipeline] = None
35
+
36
+
37
+ def get_pipeline() -> InteriorFusionPipeline:
38
+ global _pipeline
39
+ if _pipeline is None:
40
+ device = "cuda" if torch.cuda.is_available() else "cpu"
41
+ _pipeline = InteriorFusionPipeline(
42
+ model_size="L",
43
+ device=device,
44
+ dtype=torch.float16,
45
+ )
46
+ return _pipeline
47
+
48
+
49
+ @app.post("/generate")
50
+ async def generate_3d_scene(
51
+ image: UploadFile = File(...),
52
+ room_type: Optional[str] = Form(None),
53
+ style: Optional[str] = Form(None),
54
+ formats: str = Form("glb,ply"),
55
+ model_size: str = Form("L"),
56
+ ):
57
+ """
58
+ Generate a 3D interior scene from a single image.
59
+
60
+ Returns download links for the generated 3D files.
61
+ """
62
+ # Read image
63
+ contents = await image.read()
64
+ img = Image.open(io.BytesIO(contents)).convert("RGB")
65
+
66
+ # Parse formats
67
+ output_formats = [f.strip() for f in formats.split(",")]
68
+
69
+ # Run pipeline
70
+ pipeline = get_pipeline()
71
+ output = pipeline(
72
+ image=img,
73
+ room_type_hint=room_type,
74
+ style_hint=style,
75
+ )
76
+
77
+ # Export
78
+ output_dir = tempfile.mkdtemp()
79
+ output.export_all(output_dir)
80
+
81
+ # Collect file paths
82
+ files = {}
83
+ for fmt in output_formats:
84
+ path = Path(output_dir) / f"scene.{fmt}"
85
+ if path.exists():
86
+ files[fmt] = str(path)
87
+
88
+ return JSONResponse({
89
+ "success": True,
90
+ "room_type": output.room_type,
91
+ "style": output.style,
92
+ "processing_time": output.processing_time,
93
+ "num_objects": len(output.object_meshes),
94
+ "files": files,
95
+ })
96
+
97
+
98
+ @app.post("/edit")
99
+ async def edit_scene(
100
+ scene_glb: UploadFile = File(...),
101
+ edit_action: str = Form(...), # "move", "replace", "remove", "add"
102
+ object_id: Optional[int] = Form(None),
103
+ new_image: Optional[UploadFile] = File(None),
104
+ position: Optional[str] = Form(None), # JSON array [x, y, z]
105
+ ):
106
+ """
107
+ Edit an existing scene.
108
+
109
+ Actions:
110
+ - move: Move an existing object
111
+ - replace: Replace an object with a new one
112
+ - remove: Remove an object
113
+ - add: Add a new object
114
+ """
115
+ import json
116
+
117
+ pipeline = get_pipeline()
118
+
119
+ # Parse position
120
+ pos = None
121
+ if position:
122
+ pos = json.loads(position)
123
+
124
+ # Build edit dict
125
+ edit = {"action": edit_action}
126
+ if object_id is not None:
127
+ edit["object_id"] = object_id
128
+ if pos:
129
+ edit["position"] = pos
130
+ if new_image:
131
+ contents = await new_image.read()
132
+ edit["new_image"] = Image.open(io.BytesIO(contents)).convert("RGB")
133
+
134
+ # For simplicity, return not-implemented
135
+ return JSONResponse({
136
+ "success": False,
137
+ "message": "Scene editing API coming soon",
138
+ })
139
+
140
+
141
+ @app.get("/health")
142
+ async def health_check():
143
+ """Health check endpoint."""
144
+ return {"status": "ok", "version": "0.1.0"}
145
+
146
+
147
+ @app.get("/download/{filename}")
148
+ async def download_file(filename: str):
149
+ """Download a generated file."""
150
+ # In production, use proper file storage (S3, etc.)
151
+ # For now, placeholder
152
+ return JSONResponse({"message": f"Download {filename} from storage"})
153
+
154
+
155
+ if __name__ == "__main__":
156
+ import uvicorn
157
+ uvicorn.run(app, host="0.0.0.0", port=8000)