asimfayaz commited on
Commit
bb5d3f0
·
1 Parent(s): bce600e

Receive data in multipart/form-data instead of base64

Browse files
Files changed (2) hide show
  1. API_README.md +38 -38
  2. gradio_app.py +51 -24
API_README.md CHANGED
@@ -34,20 +34,21 @@ Check if the service is running.
34
 
35
  Start a 3D model generation job.
36
 
37
- **Request Body:**
 
 
 
 
 
 
 
 
 
38
  ```json
39
  {
40
- "images": {
41
- "front": "base64_encoded_image",
42
- "back": "base64_encoded_image", // Optional
43
- "left": "base64_encoded_image", // Optional
44
- "right": "base64_encoded_image" // Optional
45
- },
46
- "options": {
47
- "enable_pbr": true,
48
- "should_remesh": true,
49
- "should_texture": true
50
- }
51
  }
52
  ```
53
 
@@ -60,10 +61,9 @@ Start a 3D model generation job.
60
  ```
61
 
62
  **Notes:**
63
- - At least one image is required
64
  - The `front` image is mandatory
65
- - Images should be base64 encoded
66
- - The `options` object is optional and will use defaults if not provided
67
  - **Texture generation is enabled by default for high-quality 3D models**
68
 
69
  ### 3. Check Job Status
@@ -104,22 +104,30 @@ Check the status of a generation job.
104
  ### Python Example
105
  ```python
106
  import requests
107
- import base64
 
 
 
 
 
 
 
 
 
 
108
 
109
- # Encode image
110
- with open("image.png", "rb") as f:
111
- image_base64 = base64.b64encode(f.read()).decode()
 
 
112
 
113
  # Start generation
114
- response = requests.post("http://localhost:7860/api/generate", json={
115
- "images": {
116
- "front": image_base64
117
- },
118
- "options": {
119
- "enable_pbr": True,
120
- "should_texture": True # Critical for 3D model quality
121
- }
122
- })
123
 
124
  job_id = response.json()["job_id"]
125
 
@@ -146,16 +154,8 @@ curl http://localhost:7860/api/health
146
 
147
  # Generate model
148
  curl -X POST http://localhost:7860/api/generate \
149
- -H "Content-Type: application/json" \
150
- -d '{
151
- "images": {
152
- "front": "base64_encoded_image_here"
153
- },
154
- "options": {
155
- "enable_pbr": true,
156
- "should_texture": true
157
- }
158
- }'
159
 
160
  # Check status
161
  curl "http://localhost:7860/api/status?job_id=your_job_id"
 
34
 
35
  Start a 3D model generation job.
36
 
37
+ **Request Format:**
38
+ Multipart/form-data with the following fields:
39
+
40
+ - `front`: (Required) Front view image file
41
+ - `back`: (Optional) Back view image file
42
+ - `left`: (Optional) Left view image file
43
+ - `right`: (Optional) Right view image file
44
+ - `options`: (Optional) JSON string with generation options
45
+
46
+ **Options JSON Format:**
47
  ```json
48
  {
49
+ "enable_pbr": true,
50
+ "should_remesh": true,
51
+ "should_texture": true
 
 
 
 
 
 
 
 
52
  }
53
  ```
54
 
 
61
  ```
62
 
63
  **Notes:**
 
64
  - The `front` image is mandatory
65
+ - Images should be uploaded as files in multipart/form-data format
66
+ - The `options` field is optional and will use defaults if not provided
67
  - **Texture generation is enabled by default for high-quality 3D models**
68
 
69
  ### 3. Check Job Status
 
104
  ### Python Example
105
  ```python
106
  import requests
107
+ import json
108
+ import time
109
+
110
+ # Prepare files and options
111
+ files = {
112
+ 'front': ('front.png', open('front.png', 'rb'), 'image/png'),
113
+ # Optional additional views
114
+ # 'back': ('back.png', open('back.png', 'rb'), 'image/png'),
115
+ # 'left': ('left.png', open('left.png', 'rb'), 'image/png'),
116
+ # 'right': ('right.png', open('right.png', 'rb'), 'image/png'),
117
+ }
118
 
119
+ options = {
120
+ "enable_pbr": True,
121
+ "should_texture": True, # Critical for 3D model quality
122
+ "should_remesh": True
123
+ }
124
 
125
  # Start generation
126
+ response = requests.post(
127
+ "http://localhost:7860/api/generate",
128
+ files=files,
129
+ data={'options': json.dumps(options)}
130
+ )
 
 
 
 
131
 
132
  job_id = response.json()["job_id"]
133
 
 
154
 
155
  # Generate model
156
  curl -X POST http://localhost:7860/api/generate \
157
+ -F "front=@front.png" \
158
+ -F 'options={"enable_pbr":true,"should_texture":true}'
 
 
 
 
 
 
 
 
159
 
160
  # Check status
161
  curl "http://localhost:7860/api/status?job_id=your_job_id"
gradio_app.py CHANGED
@@ -65,7 +65,7 @@ import gradio as gr
65
  import torch
66
  import trimesh
67
  import uvicorn
68
- from fastapi import FastAPI, HTTPException, BackgroundTasks
69
  from fastapi.staticfiles import StaticFiles
70
  from fastapi.responses import JSONResponse
71
  from pydantic import BaseModel
@@ -86,13 +86,10 @@ class JobStatus(Enum):
86
  FAILED = "failed"
87
 
88
 
89
- class GenerateRequest(BaseModel):
90
- images: Dict[str, str] # base64 encoded images
91
- options: Dict[str, Any] = {
92
- "enable_pbr": True,
93
- "should_remesh": True,
94
- "should_texture": True # Critical for 3D model quality
95
- }
96
 
97
 
98
  class JobInfo:
@@ -168,17 +165,15 @@ def base64_to_pil_image(base64_string: str) -> Image.Image:
168
  raise HTTPException(status_code=400, detail=f"Invalid image data: {str(e)}")
169
 
170
 
171
- def process_generation_job(job_id: str, images: Dict[str, str], options: Dict[str, Any]):
172
  """Background task to process generation job."""
173
  global face_reduce_worker, tex_pipeline, HAS_TEXTUREGEN, SAVE_DIR
174
 
175
  try:
176
  update_job_status(job_id, JobStatus.PROCESSING, progress=10, stage="initializing")
177
 
178
- # Convert base64 images to PIL Images
179
- pil_images = {}
180
- for view, base64_img in images.items():
181
- pil_images[view] = base64_to_pil_image(base64_img)
182
 
183
  # Extract options
184
  enable_pbr = options.get("enable_pbr", True)
@@ -1324,29 +1319,61 @@ if __name__ == '__main__':
1324
 
1325
  # API Endpoints
1326
  @app.post("/api/generate")
1327
- async def generate_3d_model(request: GenerateRequest, background_tasks: BackgroundTasks):
1328
- """Generate 3D model from images."""
 
 
 
 
 
 
 
1329
  try:
1330
- # Validate input
1331
- if not request.images:
1332
- raise HTTPException(status_code=400, detail="At least one image is required")
 
 
 
 
 
 
 
1333
 
1334
- if 'front' not in request.images:
 
1335
  raise HTTPException(status_code=400, detail="Front image is required")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1336
 
1337
  # Create job
1338
  job_id = create_job()
1339
 
1340
- # Store job data
1341
- jobs[job_id].images = request.images
1342
- jobs[job_id].options = request.options
1343
 
1344
  # Start background task
1345
  background_tasks.add_task(
1346
  process_generation_job,
1347
  job_id,
1348
- request.images,
1349
- request.options
1350
  )
1351
 
1352
  return JSONResponse({
 
65
  import torch
66
  import trimesh
67
  import uvicorn
68
+ from fastapi import FastAPI, HTTPException, BackgroundTasks, File, Form, UploadFile
69
  from fastapi.staticfiles import StaticFiles
70
  from fastapi.responses import JSONResponse
71
  from pydantic import BaseModel
 
86
  FAILED = "failed"
87
 
88
 
89
+ class GenerateOptions(BaseModel):
90
+ enable_pbr: bool = True
91
+ should_remesh: bool = True
92
+ should_texture: bool = True # Critical for 3D model quality
 
 
 
93
 
94
 
95
  class JobInfo:
 
165
  raise HTTPException(status_code=400, detail=f"Invalid image data: {str(e)}")
166
 
167
 
168
+ def process_generation_job(job_id: str, images: Dict[str, Image.Image], options: Dict[str, Any]):
169
  """Background task to process generation job."""
170
  global face_reduce_worker, tex_pipeline, HAS_TEXTUREGEN, SAVE_DIR
171
 
172
  try:
173
  update_job_status(job_id, JobStatus.PROCESSING, progress=10, stage="initializing")
174
 
175
+ # Images are already PIL Images
176
+ pil_images = images
 
 
177
 
178
  # Extract options
179
  enable_pbr = options.get("enable_pbr", True)
 
1319
 
1320
  # API Endpoints
1321
  @app.post("/api/generate")
1322
+ async def generate_3d_model(
1323
+ front: UploadFile = File(None),
1324
+ back: UploadFile = File(None),
1325
+ left: UploadFile = File(None),
1326
+ right: UploadFile = File(None),
1327
+ options: str = Form("{}"),
1328
+ background_tasks: BackgroundTasks = BackgroundTasks()
1329
+ ):
1330
+ """Generate 3D model from images using multipart/form-data."""
1331
  try:
1332
+ # Parse options
1333
+ options_dict = json.loads(options) if options else {}
1334
+ generate_options = {
1335
+ "enable_pbr": options_dict.get("enable_pbr", True),
1336
+ "should_remesh": options_dict.get("should_remesh", True),
1337
+ "should_texture": options_dict.get("should_texture", True)
1338
+ }
1339
+
1340
+ # Process uploaded files
1341
+ images = {}
1342
 
1343
+ # Validate input - at least one image is required
1344
+ if not front:
1345
  raise HTTPException(status_code=400, detail="Front image is required")
1346
+
1347
+ # Process each uploaded file
1348
+ for view, file in {
1349
+ "front": front,
1350
+ "back": back,
1351
+ "left": left,
1352
+ "right": right
1353
+ }.items():
1354
+ if file and file.filename:
1355
+ # Read file content
1356
+ contents = await file.read()
1357
+ # Convert to PIL Image
1358
+ try:
1359
+ img = Image.open(io.BytesIO(contents))
1360
+ images[view] = img
1361
+ except Exception as e:
1362
+ raise HTTPException(status_code=400, detail=f"Invalid image for {view}: {str(e)}")
1363
 
1364
  # Create job
1365
  job_id = create_job()
1366
 
1367
+ # Store job data (store file paths instead of actual images)
1368
+ jobs[job_id].images = {k: "<uploaded_file>" for k in images.keys()}
1369
+ jobs[job_id].options = generate_options
1370
 
1371
  # Start background task
1372
  background_tasks.add_task(
1373
  process_generation_job,
1374
  job_id,
1375
+ images,
1376
+ generate_options
1377
  )
1378
 
1379
  return JSONResponse({