MohamedRashad commited on
Commit
c499701
·
1 Parent(s): 27fa9cc

Refactor UniRigDemo to replace bash scripts with Python functions for skeleton and skinning generation; update requirements.txt to remove unnecessary dependencies.

Browse files
Files changed (2) hide show
  1. app.py +294 -138
  2. requirements.txt +0 -3
app.py CHANGED
@@ -1,19 +1,32 @@
1
  import gradio as gr
2
  import tempfile
3
  import os
4
- import sys
5
  import shutil
6
  import subprocess
7
  import traceback
8
  from pathlib import Path
9
  from typing import Optional, Tuple, List
10
  import spaces
 
11
 
12
  import subprocess
13
  subprocess.run('pip install flash-attn --no-build-isolation', env={'FLASH_ATTENTION_SKIP_CUDA_BUILD': "TRUE"}, shell=True)
14
 
15
- # Add the current directory to the path so we can import UniRig modules
16
- sys.path.insert(0, str(Path(__file__).parent))
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  import trimesh
19
  import yaml
@@ -88,7 +101,7 @@ class UniRigDemo:
88
 
89
  def generate_skeleton(self, input_file: str, seed: int = 12345) -> Tuple[str, str, str]:
90
  """
91
- Generate skeleton for the input 3D model.
92
 
93
  Args:
94
  input_file: Path to the input 3D model file
@@ -97,57 +110,35 @@ class UniRigDemo:
97
  Returns:
98
  Tuple of (status_message, output_file_path, preview_info)
99
  """
100
- try:
101
- # Validate input
102
- if not self.validate_input_file(input_file):
103
- return "Error: Invalid or unsupported file format. Supported: " + ", ".join(self.supported_formats), "", ""
104
-
105
- # Create working directory
106
- work_dir = os.path.join(self.temp_dir, f"skeleton_{seed}")
107
- os.makedirs(work_dir, exist_ok=True)
108
-
109
- # Copy input file to work directory
110
- input_name = Path(input_file).name
111
- work_input = os.path.join(work_dir, input_name)
112
- shutil.copy2(input_file, work_input)
113
-
114
- # Generate skeleton using the launch script
115
- output_file = os.path.join(work_dir, f"{Path(input_name).stem}_skeleton.fbx")
116
-
117
- skeleton_cmd = [
118
- 'bash', 'launch/inference/generate_skeleton.sh',
119
- '--input', work_input,
120
- '--output', output_file,
121
- '--seed', str(seed)
122
- ]
123
-
124
- # Run skeleton generation
125
- result = subprocess.run(
126
- skeleton_cmd,
127
- cwd=str(Path(__file__).parent),
128
- capture_output=True,
129
- text=True
130
- )
131
-
132
- if result.returncode != 0:
133
- return f"Error: Skeleton generation failed: {result.stderr}", "", ""
134
-
135
- if not os.path.exists(output_file):
136
- return "Error: Skeleton file was not generated", "", ""
137
-
138
- # Generate preview information
139
- preview_info = self.generate_model_preview(output_file)
140
-
141
- return "✅ Skeleton generated successfully!", output_file, preview_info
142
-
143
- except Exception as e:
144
- error_msg = f"Error: {str(e)}"
145
- traceback.print_exc()
146
- return error_msg, "", ""
147
 
148
  def generate_skinning(self, skeleton_file: str) -> Tuple[str, str, str]:
149
  """
150
- Generate skinning weights for the skeleton.
151
 
152
  Args:
153
  skeleton_file: Path to the skeleton file (from skeleton generation step)
@@ -155,48 +146,32 @@ class UniRigDemo:
155
  Returns:
156
  Tuple of (status_message, output_file_path, preview_info)
157
  """
 
 
 
 
 
 
 
 
158
  try:
159
- if not skeleton_file or not os.path.exists(skeleton_file):
160
- return "Error: No skeleton file provided or file doesn't exist", "", ""
161
-
162
- # Create output directory
163
- work_dir = Path(skeleton_file).parent
164
- output_file = os.path.join(work_dir, f"{Path(skeleton_file).stem}_skin.fbx")
165
-
166
- # Generate skinning using the launch script
167
- skin_cmd = [
168
- 'bash', 'launch/inference/generate_skin.sh',
169
- '--input', skeleton_file,
170
- '--output', output_file
171
- ]
172
-
173
- # Run skinning generation
174
- result = subprocess.run(
175
- skin_cmd,
176
- cwd=str(Path(__file__).parent),
177
- capture_output=True,
178
- text=True
179
- )
180
-
181
- if result.returncode != 0:
182
- return f"Error: Skinning generation failed: {result.stderr}", "", ""
183
-
184
- if not os.path.exists(output_file):
185
- return "Error: Skinning file was not generated", "", ""
186
-
187
- # Generate preview information
188
- preview_info = self.generate_model_preview(output_file)
189
-
190
- return "✅ Skinning weights generated successfully!", output_file, preview_info
191
-
192
  except Exception as e:
193
- error_msg = f"Error: {str(e)}"
194
  traceback.print_exc()
195
  return error_msg, "", ""
 
 
 
 
 
 
 
 
196
 
197
  def merge_results(self, original_file: str, rigged_file: str) -> Tuple[str, str, str]:
198
  """
199
- Merge the rigged skeleton/skin with the original model.
200
 
201
  Args:
202
  original_file: Path to the original 3D model
@@ -205,48 +180,31 @@ class UniRigDemo:
205
  Returns:
206
  Tuple of (status_message, output_file_path, preview_info)
207
  """
 
 
 
 
 
 
 
 
 
 
 
208
  try:
209
- if not original_file or not os.path.exists(original_file):
210
- return "Error: Original file not provided or doesn't exist", "", ""
211
-
212
- if not rigged_file or not os.path.exists(rigged_file):
213
- return "Error: Rigged file not provided or doesn't exist", "", ""
214
-
215
- # Create output file
216
- work_dir = Path(rigged_file).parent
217
- output_file = os.path.join(work_dir, f"{Path(original_file).stem}_rigged.glb")
218
-
219
- # Merge using the launch script
220
- merge_cmd = [
221
- 'bash', 'launch/inference/merge.sh',
222
- '--source', rigged_file,
223
- '--target', original_file,
224
- '--output', output_file
225
- ]
226
-
227
- # Run merge
228
- result = subprocess.run(
229
- merge_cmd,
230
- cwd=str(Path(__file__).parent),
231
- capture_output=True,
232
- text=True
233
- )
234
-
235
- if result.returncode != 0:
236
- return f"Error: Merge failed: {result.stderr}", "", ""
237
-
238
- if not os.path.exists(output_file):
239
- return "Error: Merged file was not generated", "", ""
240
-
241
- # Generate preview information
242
- preview_info = self.generate_model_preview(output_file)
243
-
244
- return "✅ Model rigging completed successfully!", output_file, preview_info
245
-
246
  except Exception as e:
247
- error_msg = f"Error: {str(e)}"
248
  traceback.print_exc()
249
  return error_msg, "", ""
 
 
 
 
 
 
 
 
250
 
251
  def generate_model_preview(self, model_path: str) -> str:
252
  """
@@ -338,7 +296,211 @@ class UniRigDemo:
338
  return error_msg, "", "", "", ""
339
 
340
 
341
- def create_demo_interface():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  """Create and configure the Gradio interface."""
343
 
344
  demo_instance = UniRigDemo()
@@ -374,9 +536,10 @@ def create_demo_interface():
374
  with gr.Tab("🚀 Complete Pipeline", elem_id="pipeline-tab"):
375
  with gr.Row():
376
  with gr.Column(scale=1):
377
- pipeline_input = gr.Model3D(
378
  label="Upload 3D Model",
379
- display_mode="solid",
 
380
  )
381
  pipeline_seed = gr.Slider(
382
  minimum=1,
@@ -523,16 +686,9 @@ def create_demo_interface():
523
 
524
  return interface
525
 
526
-
527
- def main():
528
- """Main function to launch the Gradio demo."""
529
-
530
  # Create and launch the interface
531
- demo = create_demo_interface()
532
 
533
  # Launch configuration
534
- demo.queue().launch()
535
-
536
-
537
- if __name__ == "__main__":
538
- main()
 
1
  import gradio as gr
2
  import tempfile
3
  import os
 
4
  import shutil
5
  import subprocess
6
  import traceback
7
  from pathlib import Path
8
  from typing import Optional, Tuple, List
9
  import spaces
10
+ import torch
11
 
12
  import subprocess
13
  subprocess.run('pip install flash-attn --no-build-isolation', env={'FLASH_ATTENTION_SKIP_CUDA_BUILD': "TRUE"}, shell=True)
14
 
15
+ # Get the PyTorch and CUDA versions
16
+ torch_version = torch.__version__.split("+")[0] # Strips any "+cuXXX" suffix
17
+ cuda_version = torch.version.cuda
18
+
19
+ # Format CUDA version to match the URL convention (e.g., "cu118" for CUDA 11.8)
20
+ if cuda_version:
21
+ cuda_version = f"cu{cuda_version.replace('.', '')}"
22
+ else:
23
+ cuda_version = "cpu" # Fallback in case CUDA is not available
24
+
25
+ spconv_version = f"-{cuda_version}" if cuda_version != "cpu" else ""
26
+
27
+ subprocess.run(f'pip install spconv{spconv_version}', shell=True)
28
+ subprocess.run(f'pip install torch_scatter torch_cluster -f https://data.pyg.org/whl/torch-{torch_version}+{cuda_version}.html --no-cache-dir', shell=True)
29
+
30
 
31
  import trimesh
32
  import yaml
 
101
 
102
  def generate_skeleton(self, input_file: str, seed: int = 12345) -> Tuple[str, str, str]:
103
  """
104
+ OPERATION 1: Generate skeleton for the input 3D model using Python
105
 
106
  Args:
107
  input_file: Path to the input 3D model file
 
110
  Returns:
111
  Tuple of (status_message, output_file_path, preview_info)
112
  """
113
+ # Validate input
114
+ if not self.validate_input_file(input_file):
115
+ return "Error: Invalid or unsupported file format. Supported: " + ", ".join(self.supported_formats), "", ""
116
+
117
+ # Create working directory
118
+ work_dir = os.path.join(self.temp_dir, f"skeleton_{seed}")
119
+ os.makedirs(work_dir, exist_ok=True)
120
+
121
+ # Copy input file to work directory
122
+ input_name = Path(input_file).name
123
+ work_input = os.path.join(work_dir, input_name)
124
+ shutil.copy2(input_file, work_input)
125
+
126
+ # Generate skeleton using Python (replaces bash script)
127
+ output_file = os.path.join(work_dir, f"{Path(input_name).stem}_skeleton.fbx")
128
+
129
+ self.run_skeleton_inference_python(work_input, output_file, seed)
130
+
131
+ if not os.path.exists(output_file):
132
+ return "Error: Skeleton file was not generated", "", ""
133
+
134
+ # Generate preview information
135
+ preview_info = self.generate_model_preview(output_file)
136
+
137
+ return "✅ Skeleton generated successfully!", output_file, preview_info
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  def generate_skinning(self, skeleton_file: str) -> Tuple[str, str, str]:
140
  """
141
+ OPERATION 2: Generate skinning weights for the skeleton using Python functions.
142
 
143
  Args:
144
  skeleton_file: Path to the skeleton file (from skeleton generation step)
 
146
  Returns:
147
  Tuple of (status_message, output_file_path, preview_info)
148
  """
149
+ if not skeleton_file or not os.path.exists(skeleton_file):
150
+ return "Error: No skeleton file provided or file doesn't exist", "", ""
151
+
152
+ # Create output directory
153
+ work_dir = Path(skeleton_file).parent
154
+ output_file = os.path.join(work_dir, f"{Path(skeleton_file).stem}_skin.fbx")
155
+
156
+ # Run skinning generation using Python function
157
  try:
158
+ self.run_skin_inference_python(skeleton_file, output_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  except Exception as e:
160
+ error_msg = f"Error: Skinning generation failed: {str(e)}"
161
  traceback.print_exc()
162
  return error_msg, "", ""
163
+
164
+ if not os.path.exists(output_file):
165
+ return "Error: Skinning file was not generated", "", ""
166
+
167
+ # Generate preview information
168
+ preview_info = self.generate_model_preview(output_file)
169
+
170
+ return "✅ Skinning weights generated successfully!", output_file, preview_info
171
 
172
  def merge_results(self, original_file: str, rigged_file: str) -> Tuple[str, str, str]:
173
  """
174
+ OPERATION 3: Merge the rigged skeleton/skin with the original model using Python functions.
175
 
176
  Args:
177
  original_file: Path to the original 3D model
 
180
  Returns:
181
  Tuple of (status_message, output_file_path, preview_info)
182
  """
183
+ if not original_file or not os.path.exists(original_file):
184
+ return "Error: Original file not provided or doesn't exist", "", ""
185
+
186
+ if not rigged_file or not os.path.exists(rigged_file):
187
+ return "Error: Rigged file not provided or doesn't exist", "", ""
188
+
189
+ # Create output file
190
+ work_dir = Path(rigged_file).parent
191
+ output_file = os.path.join(work_dir, f"{Path(original_file).stem}_rigged.glb")
192
+
193
+ # Run merge using Python function
194
  try:
195
+ self.merge_results_python(rigged_file, original_file, output_file)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  except Exception as e:
197
+ error_msg = f"Error: Merge failed: {str(e)}"
198
  traceback.print_exc()
199
  return error_msg, "", ""
200
+
201
+ if not os.path.exists(output_file):
202
+ return "Error: Merged file was not generated", "", ""
203
+
204
+ # Generate preview information
205
+ preview_info = self.generate_model_preview(output_file)
206
+
207
+ return "✅ Model rigging completed successfully!", output_file, preview_info
208
 
209
  def generate_model_preview(self, model_path: str) -> str:
210
  """
 
296
  return error_msg, "", "", "", ""
297
 
298
 
299
+ # ==========================================
300
+ # CORE PYTHON FUNCTIONS (NO BASH SCRIPTS)
301
+ # ==========================================
302
+
303
+ def extract_mesh_python(self, input_file: str, output_dir: str) -> str:
304
+ """
305
+ Extract mesh data from 3D model using Python (replaces extract.sh)
306
+ Returns path to generated .npz file
307
+ """
308
+ # Import required modules
309
+ from src.data.extract import get_files
310
+
311
+ # Create extraction parameters
312
+ files = get_files(
313
+ data_name="raw_data.npz",
314
+ inputs=input_file,
315
+ input_dataset_dir=None,
316
+ output_dataset_dir=output_dir,
317
+ force_override=True,
318
+ warning=False,
319
+ )
320
+
321
+ # Get the npz file path
322
+ if files:
323
+ return files[0][1] # Return the npz file path
324
+
325
+ raise RuntimeError("No .npz file generated during extraction")
326
+
327
+ def run_skeleton_inference_python(self, input_file: str, output_file: str, seed: int = 12345) -> str:
328
+ """
329
+ Run skeleton inference using Python (replaces skeleton part of generate_skeleton.sh)
330
+ Returns path to skeleton FBX file
331
+ """
332
+ import lightning as L
333
+ from box import Box
334
+ from src.data.dataset import UniRigDatasetModule, DatasetConfig
335
+ from src.data.datapath import Datapath
336
+ from src.data.transform import TransformConfig
337
+ from src.tokenizer.spec import TokenizerConfig
338
+ from src.tokenizer.parse import get_tokenizer
339
+ from src.model.parse import get_model
340
+ from src.system.parse import get_system, get_writer
341
+ from src.inference.download import download
342
+
343
+ # Set random seed
344
+ L.seed_everything(seed, workers=True)
345
+
346
+ # Load task configuration
347
+ task_config_path = "configs/task/quick_inference_skeleton_articulationxl_ar_256.yaml"
348
+ with open(task_config_path, 'r') as f:
349
+ task = Box(yaml.safe_load(f))
350
+
351
+ # Create temporary npz directory
352
+ npz_dir = os.path.join(os.path.dirname(output_file), "tmp")
353
+ os.makedirs(npz_dir, exist_ok=True)
354
+
355
+ # Extract mesh data
356
+ npz_file = self.extract_mesh_python(input_file, npz_dir)
357
+
358
+ # Setup datapath
359
+ datapath = Datapath(files=[npz_file], cls=None)
360
+
361
+ # Load configurations
362
+ data_config = Box(yaml.safe_load(open("configs/data/quick_inference.yaml", 'r')))
363
+ transform_config = Box(yaml.safe_load(open("configs/transform/inference_ar_transform.yaml", 'r')))
364
+
365
+ # Get tokenizer
366
+ tokenizer_config = TokenizerConfig.parse(config=Box(yaml.safe_load(open("configs/tokenizer/tokenizer_parts_articulationxl_256.yaml", 'r'))))
367
+ tokenizer = get_tokenizer(config=tokenizer_config)
368
+
369
+ # Get model
370
+ model_config = Box(yaml.safe_load(open("configs/model/unirig_ar_350m_1024_81920_float32.yaml", 'r')))
371
+ model = get_model(tokenizer=tokenizer, **model_config)
372
+
373
+ # Setup datasets and transforms
374
+ predict_dataset_config = DatasetConfig.parse(config=data_config.predict_dataset_config).split_by_cls()
375
+ predict_transform_config = TransformConfig.parse(config=transform_config.predict_transform_config)
376
+
377
+ # Create data module
378
+ data = UniRigDatasetModule(
379
+ process_fn=model._process_fn,
380
+ predict_dataset_config=predict_dataset_config,
381
+ predict_transform_config=predict_transform_config,
382
+ tokenizer_config=tokenizer_config,
383
+ debug=False,
384
+ data_name="raw_data.npz",
385
+ datapath=datapath,
386
+ cls=None,
387
+ )
388
+
389
+ # Setup callbacks and writer
390
+ callbacks = []
391
+ writer_config = task.writer.copy()
392
+ writer_config['npz_dir'] = npz_dir
393
+ writer_config['output_dir'] = os.path.dirname(output_file)
394
+ writer_config['output_name'] = output_file
395
+ writer_config['user_mode'] = True
396
+ callbacks.append(get_writer(**writer_config, order_config=predict_transform_config.order_config))
397
+
398
+ # Get system
399
+ system_config = Box(yaml.safe_load(open("configs/system/ar_inference_articulationxl.yaml", 'r')))
400
+ system = get_system(**system_config, model=model, steps_per_epoch=1)
401
+
402
+ # Setup trainer
403
+ trainer_config = task.trainer
404
+ resume_from_checkpoint = download(task.resume_from_checkpoint)
405
+
406
+ trainer = L.Trainer(callbacks=callbacks, logger=None, **trainer_config)
407
+
408
+ # Run prediction
409
+ trainer.predict(system, datamodule=data, ckpt_path=resume_from_checkpoint, return_predictions=False)
410
+
411
+ return output_file
412
+
413
+ def run_skin_inference_python(self, skeleton_file: str, output_file: str) -> str:
414
+ """
415
+ Run skin inference using Python (replaces skin part of generate_skin.sh)
416
+ Returns path to skin FBX file
417
+ """
418
+ import lightning as L
419
+ from box import Box
420
+ from src.data.dataset import UniRigDatasetModule, DatasetConfig
421
+ from src.data.datapath import Datapath
422
+ from src.data.transform import TransformConfig
423
+ from src.model.parse import get_model
424
+ from src.system.parse import get_system, get_writer
425
+ from src.inference.download import download
426
+
427
+ # Load task configuration
428
+ task_config_path = "configs/task/quick_inference_unirig_skin.yaml"
429
+ with open(task_config_path, 'r') as f:
430
+ task = Box(yaml.safe_load(f))
431
+
432
+ # Find the npz directory (should contain predict_skeleton.npz)
433
+ npz_dir = os.path.join(os.path.dirname(skeleton_file), "tmp")
434
+ skeleton_npz = os.path.join(npz_dir, "predict_skeleton.npz")
435
+
436
+ if not os.path.exists(skeleton_npz):
437
+ raise RuntimeError(f"Skeleton NPZ file not found: {skeleton_npz}")
438
+
439
+ # Setup datapath
440
+ datapath = Datapath(files=[skeleton_npz], cls=None)
441
+
442
+ # Load configurations
443
+ data_config = Box(yaml.safe_load(open("configs/data/quick_inference.yaml", 'r')))
444
+ transform_config = Box(yaml.safe_load(open("configs/transform/inference_skin_transform.yaml", 'r')))
445
+
446
+ # Get model
447
+ model_config = Box(yaml.safe_load(open("configs/model/unirig_skin.yaml", 'r')))
448
+ model = get_model(tokenizer=None, **model_config)
449
+
450
+ # Setup datasets and transforms
451
+ predict_dataset_config = DatasetConfig.parse(config=data_config.predict_dataset_config).split_by_cls()
452
+ predict_transform_config = TransformConfig.parse(config=transform_config.predict_transform_config)
453
+
454
+ # Create data module
455
+ data = UniRigDatasetModule(
456
+ process_fn=model._process_fn,
457
+ predict_dataset_config=predict_dataset_config,
458
+ predict_transform_config=predict_transform_config,
459
+ tokenizer_config=None,
460
+ debug=False,
461
+ data_name="predict_skeleton.npz",
462
+ datapath=datapath,
463
+ cls=None,
464
+ )
465
+
466
+ # Setup callbacks and writer
467
+ callbacks = []
468
+ writer_config = task.writer.copy()
469
+ writer_config['npz_dir'] = npz_dir
470
+ writer_config['output_dir'] = os.path.dirname(output_file)
471
+ writer_config['output_name'] = output_file
472
+ writer_config['user_mode'] = True
473
+ callbacks.append(get_writer(**writer_config, order_config=predict_transform_config.order_config))
474
+
475
+ # Get system
476
+ system_config = Box(yaml.safe_load(open("configs/system/skin.yaml", 'r')))
477
+ system = get_system(**system_config, model=model, steps_per_epoch=1)
478
+
479
+ # Setup trainer
480
+ trainer_config = task.trainer
481
+ resume_from_checkpoint = download(task.resume_from_checkpoint)
482
+
483
+ trainer = L.Trainer(callbacks=callbacks, logger=None, **trainer_config)
484
+
485
+ # Run prediction
486
+ trainer.predict(system, datamodule=data, ckpt_path=resume_from_checkpoint, return_predictions=False)
487
+
488
+ return output_file
489
+
490
+ def merge_results_python(self, source_file: str, target_file: str, output_file: str) -> str:
491
+ """
492
+ Merge results using Python (replaces merge.sh)
493
+ Returns path to merged file
494
+ """
495
+ from src.inference.merge import transfer
496
+
497
+ # Use the transfer function directly
498
+ transfer(source=source_file, target=target_file, output=output_file, add_root=False)
499
+
500
+ return output_file
501
+
502
+
503
+ def create_app():
504
  """Create and configure the Gradio interface."""
505
 
506
  demo_instance = UniRigDemo()
 
536
  with gr.Tab("🚀 Complete Pipeline", elem_id="pipeline-tab"):
537
  with gr.Row():
538
  with gr.Column(scale=1):
539
+ pipeline_input = gr.File(
540
  label="Upload 3D Model",
541
+ file_types=[".obj", ".fbx", ".glb", ".gltf", ".vrm"],
542
+ type="filepath",
543
  )
544
  pipeline_seed = gr.Slider(
545
  minimum=1,
 
686
 
687
  return interface
688
 
689
+ if __name__ == "__main__":
 
 
 
690
  # Create and launch the interface
691
+ app = create_app()
692
 
693
  # Launch configuration
694
+ app.queue().launch()
 
 
 
 
requirements.txt CHANGED
@@ -17,8 +17,5 @@ numpy==1.26.4
17
  scipy
18
  matplotlib
19
  plotly
20
- torch_scatter
21
- torch_cluster
22
- spconv-cu121
23
  pyyaml
24
  spaces
 
17
  scipy
18
  matplotlib
19
  plotly
 
 
 
20
  pyyaml
21
  spaces