dkatz2391 commited on
Commit
dbfa86b
·
verified ·
1 Parent(s): 78e1902

just uplaod file to server first then process

Browse files
Files changed (1) hide show
  1. app.py +131 -155
app.py CHANGED
@@ -16,7 +16,6 @@ import requests
16
  import base64
17
  import io
18
  import tempfile
19
- import traceback
20
  MAX_SEED = np.iinfo(np.int32).max
21
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
22
  os.makedirs(TMP_DIR, exist_ok=True)
@@ -142,167 +141,144 @@ def generate_model_from_images_and_upload(
142
  model_description: str,
143
  req: gr.Request
144
  ) -> str:
145
- try:
146
- user_dir = os.path.join(TMP_DIR, str(req.session_hash))
147
- os.makedirs(user_dir, exist_ok=True)
148
-
149
- print(f"Python DEBUG: Raw image_inputs (as received by function): {image_inputs}")
150
- print(f"Python DEBUG: Type of image_inputs: {type(image_inputs)}")
151
- if isinstance(image_inputs, list):
152
- print(f"Python DEBUG: Length of image_inputs list: {len(image_inputs)}")
153
- if len(image_inputs) > 0 and isinstance(image_inputs[0], dict):
154
- print(f"Python DEBUG: First element of image_inputs (should be a dict): {image_inputs[0]}")
155
- print(f"Python DEBUG: Type of first element: {type(image_inputs[0])}")
156
- print(f"Python DEBUG: Received input_type from Node.js: '{input_type}'")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
- pil_images = []
159
- image_basenames_for_prompt = []
160
-
161
- for i, file_data_obj in enumerate(image_inputs):
162
- img_to_open_path = None
163
- current_image_name = file_data_obj.get('name', f"image_{i}.png")
164
- print(f"Python DEBUG: Processing item {i}: {file_data_obj}, current_image_name: {current_image_name}")
165
-
166
- if input_type == 'url':
167
- img_to_open_path = file_data_obj.get('path')
168
- if not img_to_open_path:
169
- print(f"Error: For 'url' input_type, 'path' was missing in item {i}: {file_data_obj}")
170
- continue
171
- print(f"Python INFO: 'url' type. Using Gradio-provided path for '{current_image_name}': {img_to_open_path}")
172
- elif input_type == 'base64':
173
- base64_data_uri = file_data_obj.get('data_uri')
174
- if not base64_data_uri or not isinstance(base64_data_uri, str) or not base64_data_uri.startswith('data:image'):
175
- print(f"Error: For 'base64' input_type, 'data_uri' was missing or invalid in item {i}: {file_data_obj}")
176
- continue
177
- try:
178
- print(f"Python INFO: 'base64' type. Decoding data_uri for '{current_image_name}'...")
179
- header, encoded_data = base64_data_uri.split(',', 1)
180
- image_data_bytes = base64.b64decode(encoded_data)
181
- file_extension = ".png"
182
- try:
183
- parsed_extension = os.path.splitext(current_image_name)[1]
184
- if parsed_extension:
185
- file_extension = parsed_extension
186
- elif '/' in header and ';' in header:
187
- mime_subtype = header.split('/')[1].split(';')[0]
188
- if mime_subtype:
189
- file_extension = "." + mime_subtype
190
- except Exception as e_ext:
191
- print(f"Python WARNING: Could not parse precise extension for '{current_image_name}' from header/name: {e_ext}. Using {file_extension}.")
192
- with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension, dir=TMP_DIR) as tmp_file:
193
- tmp_file.write(image_data_bytes)
194
- img_to_open_path = tmp_file.name
195
- print(f"Python INFO: Decoded base64 for '{current_image_name}' and saved to temporary file: {img_to_open_path}")
196
- except Exception as e_b64:
197
- print(f"Error processing base64 image data for item {i} ('{current_image_name}'): {e_b64}")
198
- traceback.print_exc()
199
- continue
200
- else:
201
- print(f"Error: Unrecognized input_type '{input_type}' for item {i}. Skipping.")
202
- continue
203
-
204
- if not img_to_open_path:
205
- print(f"Error: No valid image path could be derived for item {i} (name: '{current_image_name}', type: '{input_type}'). Skipping.")
206
- continue
207
- try:
208
- print(f"Python INFO: Opening image from path: {img_to_open_path} (intended name for prompt: {current_image_name})")
209
- img = Image.open(img_to_open_path)
210
- image_basenames_for_prompt.append(os.path.splitext(current_image_name)[0] or f"image_{i}")
211
- if img.mode == 'RGBA' or img.mode == 'P':
212
- print(f"Converting image '{current_image_name}' from {img.mode} to RGB")
213
- img = img.convert('RGB')
214
- processed_img = pipeline.preprocess_image(img)
215
- pil_images.append(processed_img)
216
- print(f"Image '{current_image_name}' (item {i+1}) processed successfully and added to list.")
217
- except Exception as e_img_proc:
218
- print(f"Error opening or processing image at '{img_to_open_path}' (item {i}, name: '{current_image_name}'): {e_img_proc}")
219
- traceback.print_exc()
220
- finally:
221
- if input_type == 'base64' and img_to_open_path and os.path.exists(img_to_open_path):
222
- if TMP_DIR in os.path.abspath(img_to_open_path):
223
- try:
224
- os.remove(img_to_open_path)
225
- print(f"Python INFO: Removed temporary base64 file: {img_to_open_path}")
226
- except Exception as e_remove:
227
- print(f"Python WARNING: Could not remove temp file {img_to_open_path}: {e_remove}")
228
- else:
229
- print(f"Python WARNING: Skipped deletion of temp file as it's not in TMP_DIR (or was a Gradio-managed URL path): {img_to_open_path}")
230
-
231
- if not pil_images:
232
- print("Python ERROR: No valid images could be processed from the input list.")
233
- raise gr.Error("No valid images could be processed.")
234
- print(f"Python INFO: Total PIL images ready for pipeline: {len(pil_images)}")
235
- print("Running multi-image pipeline...")
236
- outputs = pipeline.run_multi_image(
237
- pil_images,
238
  seed=seed_val,
239
- formats=["gaussian", "mesh"],
240
- preprocess_image=False,
241
- sparse_structure_sampler_params={
242
- "steps": ss_sampling_steps_val,
243
- "cfg_strength": ss_guidance_strength_val,
244
- },
245
- slat_sampler_params={
246
- "steps": slat_sampling_steps_val,
247
- "cfg_strength": slat_guidance_strength_val,
248
- },
249
- mode=multiimage_algo_val,
 
 
 
 
250
  )
251
- print("Multi-image pipeline completed.")
252
- gs_result = outputs['gaussian'][0]
253
- mesh_result = outputs['mesh'][0]
254
- print(f"Extracting GLB with simplify: {mesh_simplify_val}, texture_size: {texture_size_val}")
255
- glb_data = postprocessing_utils.to_glb(gs_result, mesh_result, simplify=mesh_simplify_val, texture_size=texture_size_val, verbose=False)
256
- temp_glb_filename = 'temp_output_image_model.glb'
257
- temp_glb_path = os.path.join(user_dir, temp_glb_filename)
258
- print(f"Exporting GLB to temporary path: {temp_glb_path}")
259
- glb_data.export(temp_glb_path)
260
- torch.cuda.empty_cache()
261
- print("CUDA cache cleared.")
262
- print(f"Uploading GLB from {temp_glb_path} to {NODE_SERVER_UPLOAD_URL}")
263
  persistent_url = None
264
- upload_prompt_name = model_description or "_".join(filter(None, image_basenames_for_prompt)) or "imagen_generated_model"
265
- upload_prompt_name = "".join(c if c.isalnum() or c in ['_', '-'] else '_' for c in upload_prompt_name)[:50]
266
- try:
267
- with open(temp_glb_path, "rb") as f:
268
- files = {"modelFile": (temp_glb_filename, f, "model/gltf-binary")}
269
- payload = {
270
- "clientType": "imagen",
271
- "prompt": upload_prompt_name,
272
- "modelStage": "imagen_mesh"
273
- }
274
- print(f"Upload payload to Node.js: {payload}")
275
- response = requests.post(NODE_SERVER_UPLOAD_URL, files=files, data=payload, timeout=120)
276
- response.raise_for_status()
277
- result = response.json()
278
- persistent_url = result.get("persistentUrl")
279
- if not persistent_url:
280
- print(f"No persistent URL in Node.js server response: {result}")
281
- raise ValueError("Upload successful, but no persistent URL returned from Node.js server")
282
- print(f"Successfully uploaded to Node server. Persistent URL: {persistent_url}")
283
- except requests.exceptions.RequestException as upload_err:
284
- print(f"FAILED to upload GLB to Node server: {upload_err}")
285
- if hasattr(upload_err, 'response') and upload_err.response is not None:
286
- print(f"Node server response status: {upload_err.response.status_code}")
287
- print(f"Node server response text: {upload_err.response.text}")
288
- raise gr.Error(f"Failed to upload result to backend server: {upload_err}")
289
- except Exception as e:
290
- print(f"UNEXPECTED error during upload: {e}", exc_info=True)
291
- raise gr.Error(f"Unexpected error during upload: {e}")
292
- finally:
293
- if os.path.exists(temp_glb_path):
294
- print(f"Cleaning up temporary GLB: {temp_glb_path}")
295
- os.remove(temp_glb_path)
296
- if not persistent_url:
297
- print("Failed to obtain a persistent URL for the generated model.")
298
- raise gr.Error("Failed to obtain a persistent URL for the generated model.")
299
- print(f"Returning persistent URL: {persistent_url}")
300
  return persistent_url
301
 
302
- except Exception as e_main:
303
- print(f"TOP LEVEL PYTHON ERROR IN generate_model_from_images_and_upload: {e_main}")
 
304
  traceback.print_exc()
305
- raise gr.Error(f"A critical error occurred in the Python backend: {e_main}")
306
 
307
  # Interfaz Gradio
308
  with gr.Blocks(delete_cache=(600, 600)) as demo:
 
16
  import base64
17
  import io
18
  import tempfile
 
19
  MAX_SEED = np.iinfo(np.int32).max
20
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
21
  os.makedirs(TMP_DIR, exist_ok=True)
 
141
  model_description: str,
142
  req: gr.Request
143
  ) -> str:
144
+ user_dir = os.path.join(TMP_DIR, str(req.session_hash))
145
+ os.makedirs(user_dir, exist_ok=True)
146
+
147
+ # --- DEBUG LOGS ---
148
+ print(f"Python DEBUG: Raw image_inputs (as received by function): {image_inputs}")
149
+ print(f"Python DEBUG: Type of image_inputs: {type(image_inputs)}")
150
+ if isinstance(image_inputs, list):
151
+ print(f"Python DEBUG: Length of image_inputs list: {len(image_inputs)}")
152
+ if len(image_inputs) > 0 and isinstance(image_inputs[0], dict):
153
+ print(f"Python DEBUG: First element of image_inputs (should be a dict): {image_inputs[0]}")
154
+ print(f"Python DEBUG: Type of first element: {type(image_inputs[0])}")
155
+ print(f"Python DEBUG: Received input_type from Node.js: '{input_type}'") # Should always be 'url' now
156
+ # --- END DEBUG LOGS ---
157
+
158
+ pil_images = []
159
+ image_basenames_for_prompt = []
160
+
161
+ for i, file_data_obj in enumerate(image_inputs): # file_data_obj is one dict from the list
162
+ img_to_open_path = None
163
+ current_image_name = file_data_obj.get('name', f"image_{i}.png")
164
+
165
+ print(f"Python DEBUG: Processing item {i}: {file_data_obj}, current_image_name: {current_image_name}")
166
+
167
+ # For URLs (which is now always the case from Node.js),
168
+ # Gradio should have downloaded the image and put its local path in file_data_obj.get('path')
169
+ img_to_open_path = file_data_obj.get('path')
170
+ if not img_to_open_path:
171
+ print(f"Error: 'path' was missing in item {i}: {file_data_obj}. Skipping.")
172
+ continue
173
+ print(f"Python INFO: Using Gradio-provided path for '{current_image_name}': {img_to_open_path}")
174
+
175
+ # Now, process the image using img_to_open_path
176
+ try:
177
+ print(f"Python INFO: Opening image from path: {img_to_open_path} (intended name for prompt: {current_image_name})")
178
+ img = Image.open(img_to_open_path)
179
+
180
+ image_basenames_for_prompt.append(os.path.splitext(current_image_name)[0] or f"image_{i}")
181
+
182
+ if img.mode == 'RGBA' or img.mode == 'P':
183
+ print(f"Converting image '{current_image_name}' from {img.mode} to RGB")
184
+ img = img.convert('RGB')
185
+
186
+ processed_img = pipeline.preprocess_image(img)
187
+ pil_images.append(processed_img)
188
+ print(f"Image '{current_image_name}' (item {i+1}) processed successfully and added to list.")
189
+
190
+ except Exception as e_img_proc:
191
+ print(f"Error opening or processing image at '{img_to_open_path}' (item {i}, name: '{current_image_name}'): {e_img_proc}")
192
+ import traceback
193
+ traceback.print_exc()
194
+ # Continue to next image if one fails
195
 
196
+ # No finally block needed here anymore for deleting temp base64 files
197
+
198
+ if not pil_images:
199
+ print("Error: No images could be processed from the input. Aborting generation.")
200
+ raise gr.Error("Failed to process any input images.")
201
+
202
+ print(f"Python INFO: Total images processed for pipeline: {len(pil_images)}")
203
+ effective_model_description = model_description
204
+ if not effective_model_description and image_basenames_for_prompt:
205
+ effective_model_description = "_prompted_by_" + "_and_".join(image_basenames_for_prompt)
206
+ effective_model_description = effective_model_description[:100] # Keep it reasonably short
207
+ elif not effective_model_description:
208
+ effective_model_description = "ImageGenModel"
209
+ print(f"Python INFO: Using model_description for upload: {effective_model_description}")
210
+
211
+ # Generate 3D model using the Trellis image pipeline
212
+ try:
213
+ print(f"Python INFO: Calling internal image_to_3d with {len(pil_images)} images.")
214
+ # The image_to_3d function expects a list of tuples (PIL.Image, str_filename_or_label)
215
+ # We have processed_img in pil_images which are already PIL.Image objects after pipeline.preprocess_image
216
+ # We can use the current_image_name (or derived basenames) as the string part if needed by image_to_3d,
217
+ # but Trellis's run_multi_image takes a list of PIL images directly.
218
+ # Let's adapt multiimages for image_to_3d to be List[Tuple[Image.Image, str]]
219
+ multiimages_for_pipeline = []
220
+ for idx, p_img in enumerate(pil_images):
221
+ # Create a simple label for each image for the tuple structure
222
+ label = image_basenames_for_prompt[idx] if idx < len(image_basenames_for_prompt) else f"image_{idx}"
223
+ multiimages_for_pipeline.append((p_img, label)) # p_img here is already the *processed* image tensor.
224
+ # image_to_3d will take image[0] from this list.
225
+ # This might need adjustment if image_to_3d expects raw PIL Images.
226
+ # Re-checking Trellis: pipeline.run_multi_image takes List[Image.Image]
227
+ # and preprocesses them internally if preprocess_image=True (default).
228
+ # Since we pre-process above, we should pass preprocess_image=False if run_multi_image allows.
229
+ # The `image_to_3d` in this file is a wrapper for run_multi_image.
230
+ # It passes `preprocess_image=False`.
231
+ # So, `pil_images` containing already processed images is what `image_to_3d` expects for `[image[0] for image in multiimages]`
232
+
233
+ state, _ = image_to_3d(
234
+ multiimages=[(img, name) for img, name in zip(pil_images, image_basenames_for_prompt)], # Pass list of (processed_PIL_image, name_str)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  seed=seed_val,
236
+ ss_guidance_strength=ss_guidance_strength_val,
237
+ ss_sampling_steps=ss_sampling_steps_val,
238
+ slat_guidance_strength=slat_guidance_strength_val,
239
+ slat_sampling_steps=slat_sampling_steps_val,
240
+ multiimage_algo=multiimage_algo_val,
241
+ req=req
242
+ )
243
+ if state is None:
244
+ print("Error: Internal image_to_3d returned None state!")
245
+ raise ValueError("Internal image_to_3d failed to return state")
246
+ print(f"Python INFO: Internal image_to_3d completed. State type: {type(state)}")
247
+
248
+ print("Python INFO: Calling internal extract_glb...")
249
+ glb_path, _ = extract_glb(
250
+ state, mesh_simplify_val, texture_size_val, req
251
  )
252
+ if glb_path is None or not os.path.isfile(glb_path):
253
+ print(f"Error: Internal extract_glb returned None or invalid path: {glb_path}")
254
+ raise FileNotFoundError(f"Generated GLB file not found at {glb_path}")
255
+ print(f"Python INFO: Internal extract_glb completed. GLB path: {glb_path}")
256
+
257
+ print(f"Python INFO: Uploading GLB from {glb_path} to {NODE_SERVER_UPLOAD_URL}")
 
 
 
 
 
 
258
  persistent_url = None
259
+ with open(glb_path, "rb") as f:
260
+ files = {"modelFile": (os.path.basename(glb_path), f, "model/gltf-binary")}
261
+ payload = {
262
+ "clientType": "imagen",
263
+ "modelStage": "imagen_mesh",
264
+ "prompt": effective_model_description # Use the description here
265
+ }
266
+ print(f"Python INFO: Upload payload: {payload}")
267
+ response = requests.post(NODE_SERVER_UPLOAD_URL, files=files, data=payload)
268
+ response.raise_for_status() # Raise an exception for bad status codes
269
+ result = response.json()
270
+ persistent_url = result.get("persistentUrl")
271
+ if not persistent_url:
272
+ print(f"Error: No persistent URL in Node.js server response: {result}")
273
+ raise ValueError("Upload successful, but no persistent URL returned")
274
+ print(f"Python INFO: Successfully uploaded to Node server. Persistent URL: {persistent_url}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  return persistent_url
276
 
277
+ except Exception as e:
278
+ print(f"ERROR in Image-to-3D pipeline: {e}")
279
+ import traceback
280
  traceback.print_exc()
281
+ raise gr.Error(f"Image-to-3D pipeline failed: {e}")
282
 
283
  # Interfaz Gradio
284
  with gr.Blocks(delete_cache=(600, 600)) as demo: