rayli commited on
Commit
750feb6
·
verified ·
1 Parent(s): 212726c

Split ZeroGPU inference from CPU postprocessing

Browse files
Files changed (1) hide show
  1. app.py +317 -97
app.py CHANGED
@@ -3150,6 +3150,30 @@ def _zip_directory(directory: Path) -> Path:
3150
  return zip_path
3151
 
3152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3153
  def _ensure_instruct_checkpoint(checkpoint_path: Path) -> Path:
3154
  if checkpoint_path.exists():
3155
  return checkpoint_path
@@ -3242,7 +3266,7 @@ def _prefetch_startup_assets(config: dict[str, Any]) -> None:
3242
  def _spaces_gpu(fn):
3243
  if spaces is None:
3244
  return fn
3245
- duration = min(int(os.environ.get("SPACES_GPU_DURATION", "120")), 120)
3246
  return spaces.GPU(duration=duration)(fn)
3247
 
3248
 
@@ -3531,7 +3555,7 @@ class InstructParticulateApp:
3531
  finally:
3532
  torch.cuda.empty_cache()
3533
 
3534
- def predict(
3535
  self,
3536
  mesh_path_value: Any,
3537
  kinematic_tree_json: str,
@@ -3547,14 +3571,10 @@ class InstructParticulateApp:
3547
  ):
3548
  mesh_path = _extract_gradio_path(mesh_path_value)
3549
  if mesh_path is None:
3550
- yield None, None, None, None, None, "Upload a mesh first.", gr.update(interactive=False)
3551
  return
3552
  if not mesh_path.exists():
3553
  yield (
3554
- None,
3555
- None,
3556
- None,
3557
- None,
3558
  None,
3559
  f"Mesh file does not exist: {mesh_path}",
3560
  gr.update(interactive=False),
@@ -3562,17 +3582,12 @@ class InstructParticulateApp:
3562
  return
3563
  if not up_dir:
3564
  yield (
3565
- None,
3566
- None,
3567
- None,
3568
- None,
3569
  None,
3570
  "Select the upright orientation image before running inference.",
3571
  gr.update(interactive=False),
3572
  )
3573
  return
3574
 
3575
- early_visualization_path: Path | None = None
3576
  try:
3577
  link_names, joint_specs = parse_kinematic_tree(kinematic_tree_json)
3578
  point_prompt_arrays = _parse_point_prompt_arrays(
@@ -3585,10 +3600,6 @@ class InstructParticulateApp:
3585
  )
3586
  if duplicate_prompt_warning is not None:
3587
  yield (
3588
- None,
3589
- None,
3590
- None,
3591
- None,
3592
  None,
3593
  duplicate_prompt_warning,
3594
  gr.update(interactive=False),
@@ -3684,51 +3695,161 @@ class InstructParticulateApp:
3684
  batch["query_point_normals"][0],
3685
  dtype=np.float32,
3686
  )
3687
- links_for_query_visualization = [
3688
- {"link_id": int(link_id), "name": str(link_name)}
3689
- for link_id, link_name in enumerate(link_names)
3690
- ]
3691
- visualization_link_point_prompts_world, visualization_link_point_prompt_ids = (
3692
- _resolve_visualized_batch_link_point_prompts(
3693
- args=args,
3694
- batch=batch,
3695
- links=links_for_query_visualization,
3696
- center=mesh_geometry.center,
3697
- scale=mesh_geometry.scale,
3698
- )
3699
- )
3700
- early_visualization_path = save_predicted_point_query_rest_visualization(
3701
- output_dir,
3702
- query_points=_denormalize_points(
3703
- query_points,
3704
- center=mesh_geometry.center,
3705
- scale=mesh_geometry.scale,
3706
- ),
3707
- query_normals=query_normals,
3708
- predicted_part_ids=point_part_ids,
3709
- link_point_prompts=visualization_link_point_prompts_world,
3710
- link_point_prompt_ids=visualization_link_point_prompt_ids,
3711
- links=links_for_query_visualization,
3712
- )
 
 
3713
  yield (
 
 
 
 
 
 
 
 
 
 
3714
  None,
3715
- None,
3716
- str(early_visualization_path),
3717
- None,
3718
- None,
3719
- "Point query visualization ready. Running face postprocessing and articulation export...",
3720
  gr.update(interactive=False),
3721
  )
3722
 
3723
- face_part_ids, face_part_ids_unrefined = _decode_face_part_ids(
3724
- mesh_geometry.normalized_mesh,
3725
- point_part_ids=point_part_ids,
3726
- point_part_probabilities=point_part_probabilities,
3727
- query_face_indices=query_face_indices,
3728
- input_part_ids=np.arange(len(link_names), dtype=np.int32),
3729
- strict=bool(args.strict_face_postprocess),
3730
- enforce_connectivity_per_part=bool(args.enforce_connectivity_per_part),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3731
  )
 
 
 
 
3732
  motion_artifacts = _compute_motion_prediction_artifacts(
3733
  args,
3734
  model=model,
@@ -3736,59 +3857,114 @@ class InstructParticulateApp:
3736
  normalized_mesh=mesh_geometry.normalized_mesh,
3737
  face_part_ids=face_part_ids,
3738
  joint_refit_num_query_points=int(args.num_query_points),
3739
- num_links=len(link_names),
3740
  center=mesh_geometry.center,
3741
  scale=mesh_geometry.scale,
3742
  )
3743
  prediction = {
3744
- "query_points": query_points,
3745
- "query_normals": query_normals,
3746
- "point_part_ids": point_part_ids,
3747
  "face_part_ids": face_part_ids,
3748
- "face_part_ids_unrefined": face_part_ids_unrefined,
 
 
 
3749
  **motion_artifacts,
3750
  }
3751
- self._write_outputs(
3752
- args=args,
3753
- mesh_path=mesh_path,
3754
- up_dir=canonical_up,
3755
- output_dir=output_dir,
3756
- mesh_geometry=mesh_geometry,
3757
- batch=batch,
3758
- query_face_indices=query_face_indices,
3759
- link_names=link_names,
3760
- joint_specs=joint_specs,
3761
- prediction=prediction,
3762
- segmentation_num_query_points=segmentation_num_query_points,
3763
- visualization_path=early_visualization_path,
3764
- )
3765
- zip_path = _zip_directory(output_dir)
3766
- yield (
3767
- str(output_dir / "animated_textured.glb"),
3768
- str(output_dir / "mesh_parts_with_axes.glb"),
3769
- gr.update(),
3770
- str(zip_path),
3771
- str(output_dir),
3772
- f"Success using input up direction {canonical_up}. Wrote outputs to {output_dir}",
3773
- gr.update(interactive=True),
3774
- )
3775
- except Exception as exc:
3776
- traceback.print_exc()
3777
  yield (
3778
- None,
3779
- None,
3780
- gr.update()
3781
- if early_visualization_path is not None
3782
- and early_visualization_path.exists()
3783
- else None,
3784
- None,
3785
- None,
3786
- f"Error: {exc}",
3787
  gr.update(interactive=False),
3788
  )
3789
  finally:
3790
  torch.cuda.empty_cache()
3791
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3792
  def _prepare_geometry(self, mesh_path: Path, up_dir: str):
3793
  from infer import _prepare_mesh_geometry
3794
 
@@ -4079,7 +4255,7 @@ def run_predict_on_gpu(
4079
  enforce_connectivity_per_part: bool,
4080
  joint_decoding_confidence_temperature: float,
4081
  ):
4082
- yield from _get_active_app().predict(
4083
  mesh_path_value,
4084
  kinematic_tree_json,
4085
  point_prompt_json,
@@ -4094,8 +4270,22 @@ def run_predict_on_gpu(
4094
  )
4095
 
4096
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4097
  def prepare_inference_ui():
4098
  return (
 
4099
  None,
4100
  gr.update(interactive=False),
4101
  gr.update(value=None, interactive=False),
@@ -4203,6 +4393,7 @@ def create_gradio_app(app: InstructParticulateApp) -> gr.Blocks:
4203
  elem_classes=["kinematic-json-sync"],
4204
  )
4205
  latest_output_dir = gr.State(None)
 
4206
 
4207
  with gr.Row(equal_height=True, elem_classes=["demo-row", "demo-top-row"]):
4208
  with gr.Column(scale=1, min_width=300, elem_classes=["demo-panel", "mesh-panel"]):
@@ -4415,6 +4606,7 @@ def create_gradio_app(app: InstructParticulateApp) -> gr.Blocks:
4415
  fn=prepare_inference_ui,
4416
  inputs=None,
4417
  outputs=[
 
4418
  latest_output_dir,
4419
  export_urdf_button,
4420
  urdf_zip,
@@ -4422,7 +4614,7 @@ def create_gradio_app(app: InstructParticulateApp) -> gr.Blocks:
4422
  ],
4423
  queue=False,
4424
  )
4425
- run_event.then(
4426
  fn=run_predict_on_gpu,
4427
  inputs=[
4428
  input_mesh,
@@ -4437,6 +4629,34 @@ def create_gradio_app(app: InstructParticulateApp) -> gr.Blocks:
4437
  connectivity,
4438
  confidence_temperature,
4439
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4440
  outputs=[
4441
  animated_model,
4442
  prediction_model,
 
3150
  return zip_path
3151
 
3152
 
3153
+ def _to_cpu_payload(value: Any) -> Any:
3154
+ if isinstance(value, torch.Tensor):
3155
+ return value.detach().cpu()
3156
+ if isinstance(value, dict):
3157
+ return {key: _to_cpu_payload(item) for key, item in value.items()}
3158
+ if isinstance(value, list):
3159
+ return [_to_cpu_payload(item) for item in value]
3160
+ if isinstance(value, tuple):
3161
+ return tuple(_to_cpu_payload(item) for item in value)
3162
+ return value
3163
+
3164
+
3165
+ def _to_device_payload(value: Any, device: torch.device) -> Any:
3166
+ if isinstance(value, torch.Tensor):
3167
+ return value.to(device)
3168
+ if isinstance(value, dict):
3169
+ return {key: _to_device_payload(item, device) for key, item in value.items()}
3170
+ if isinstance(value, list):
3171
+ return [_to_device_payload(item, device) for item in value]
3172
+ if isinstance(value, tuple):
3173
+ return tuple(_to_device_payload(item, device) for item in value)
3174
+ return value
3175
+
3176
+
3177
  def _ensure_instruct_checkpoint(checkpoint_path: Path) -> Path:
3178
  if checkpoint_path.exists():
3179
  return checkpoint_path
 
3266
  def _spaces_gpu(fn):
3267
  if spaces is None:
3268
  return fn
3269
+ duration = max(1, min(int(os.environ.get("SPACES_GPU_DURATION", "10")), 10))
3270
  return spaces.GPU(duration=duration)(fn)
3271
 
3272
 
 
3555
  finally:
3556
  torch.cuda.empty_cache()
3557
 
3558
+ def predict_segmentation_payload(
3559
  self,
3560
  mesh_path_value: Any,
3561
  kinematic_tree_json: str,
 
3571
  ):
3572
  mesh_path = _extract_gradio_path(mesh_path_value)
3573
  if mesh_path is None:
3574
+ yield None, "Upload a mesh first.", gr.update(interactive=False)
3575
  return
3576
  if not mesh_path.exists():
3577
  yield (
 
 
 
 
3578
  None,
3579
  f"Mesh file does not exist: {mesh_path}",
3580
  gr.update(interactive=False),
 
3582
  return
3583
  if not up_dir:
3584
  yield (
 
 
 
 
3585
  None,
3586
  "Select the upright orientation image before running inference.",
3587
  gr.update(interactive=False),
3588
  )
3589
  return
3590
 
 
3591
  try:
3592
  link_names, joint_specs = parse_kinematic_tree(kinematic_tree_json)
3593
  point_prompt_arrays = _parse_point_prompt_arrays(
 
3600
  )
3601
  if duplicate_prompt_warning is not None:
3602
  yield (
 
 
 
 
3603
  None,
3604
  duplicate_prompt_warning,
3605
  gr.update(interactive=False),
 
3695
  batch["query_point_normals"][0],
3696
  dtype=np.float32,
3697
  )
3698
+ payload = {
3699
+ "args": {
3700
+ "num_query_points": int(args.num_query_points),
3701
+ "num_query_points_per_face_for_seg": args.num_query_points_per_face_for_seg,
3702
+ "query_batch_size": int(args.query_batch_size),
3703
+ "animation_frames": int(args.animation_frames),
3704
+ "strict_face_postprocess": bool(args.strict_face_postprocess),
3705
+ "enforce_connectivity_per_part": bool(args.enforce_connectivity_per_part),
3706
+ "joint_decoding_confidence_temperature": float(
3707
+ args.joint_decoding_confidence_temperature
3708
+ ),
3709
+ },
3710
+ "mesh_path": str(mesh_path),
3711
+ "up_dir": str(canonical_up),
3712
+ "output_dir": str(output_dir),
3713
+ "batch": _to_cpu_payload(batch),
3714
+ "query_face_indices": np.asarray(query_face_indices, dtype=np.int64),
3715
+ "link_names": [str(link_name) for link_name in link_names],
3716
+ "joint_specs": [
3717
+ (int(parent), int(child), str(joint_type))
3718
+ for parent, child, joint_type in joint_specs
3719
+ ],
3720
+ "point_part_probabilities": point_part_probabilities,
3721
+ "point_part_ids": point_part_ids,
3722
+ "query_points": query_points,
3723
+ "query_normals": query_normals,
3724
+ "segmentation_num_query_points": int(segmentation_num_query_points),
3725
+ }
3726
  yield (
3727
+ payload,
3728
+ "Point predictions ready. Running CPU face postprocessing...",
3729
+ gr.update(interactive=False),
3730
+ )
3731
+ finally:
3732
+ torch.cuda.empty_cache()
3733
+
3734
+ def postprocess_segmentation_payload(self, payload: dict[str, Any] | None):
3735
+ if not payload:
3736
+ return (
3737
  None,
3738
+ gr.update(),
3739
+ gr.update(),
 
 
 
3740
  gr.update(interactive=False),
3741
  )
3742
 
3743
+ args_payload = dict(payload["args"])
3744
+ mesh_path = Path(str(payload["mesh_path"]))
3745
+ canonical_up = str(payload["up_dir"])
3746
+ output_dir = Path(str(payload["output_dir"]))
3747
+ args = _make_inference_args(
3748
+ output_dir=output_dir,
3749
+ num_query_points=int(args_payload["num_query_points"]),
3750
+ num_query_points_per_face_for_seg=args_payload[
3751
+ "num_query_points_per_face_for_seg"
3752
+ ],
3753
+ query_batch_size=int(args_payload["query_batch_size"]),
3754
+ animation_frames=int(args_payload["animation_frames"]),
3755
+ strict_face_postprocess=bool(args_payload["strict_face_postprocess"]),
3756
+ enforce_connectivity_per_part=bool(
3757
+ args_payload["enforce_connectivity_per_part"]
3758
+ ),
3759
+ joint_decoding_confidence_temperature=float(
3760
+ args_payload["joint_decoding_confidence_temperature"]
3761
+ ),
3762
+ )
3763
+ mesh_geometry = self._prepare_geometry(mesh_path, canonical_up)
3764
+ link_names = [str(link_name) for link_name in payload["link_names"]]
3765
+ links_for_query_visualization = [
3766
+ {"link_id": int(link_id), "name": str(link_name)}
3767
+ for link_id, link_name in enumerate(link_names)
3768
+ ]
3769
+ batch = payload["batch"]
3770
+ visualization_link_point_prompts_world, visualization_link_point_prompt_ids = (
3771
+ _resolve_visualized_batch_link_point_prompts(
3772
+ args=args,
3773
+ batch=batch,
3774
+ links=links_for_query_visualization,
3775
+ center=mesh_geometry.center,
3776
+ scale=mesh_geometry.scale,
3777
+ )
3778
+ )
3779
+ query_points = np.asarray(payload["query_points"], dtype=np.float32)
3780
+ query_normals = np.asarray(payload["query_normals"], dtype=np.float32)
3781
+ point_part_ids = np.asarray(payload["point_part_ids"], dtype=np.int32)
3782
+ query_face_indices = np.asarray(payload["query_face_indices"], dtype=np.int64)
3783
+ early_visualization_path = save_predicted_point_query_rest_visualization(
3784
+ output_dir,
3785
+ query_points=_denormalize_points(
3786
+ query_points,
3787
+ center=mesh_geometry.center,
3788
+ scale=mesh_geometry.scale,
3789
+ ),
3790
+ query_normals=query_normals,
3791
+ predicted_part_ids=point_part_ids,
3792
+ link_point_prompts=visualization_link_point_prompts_world,
3793
+ link_point_prompt_ids=visualization_link_point_prompt_ids,
3794
+ links=links_for_query_visualization,
3795
+ )
3796
+ face_part_ids, face_part_ids_unrefined = _decode_face_part_ids(
3797
+ mesh_geometry.normalized_mesh,
3798
+ point_part_ids=point_part_ids,
3799
+ point_part_probabilities=np.asarray(
3800
+ payload["point_part_probabilities"],
3801
+ dtype=np.float32,
3802
+ ),
3803
+ query_face_indices=query_face_indices,
3804
+ input_part_ids=np.arange(len(link_names), dtype=np.int32),
3805
+ strict=bool(args.strict_face_postprocess),
3806
+ enforce_connectivity_per_part=bool(args.enforce_connectivity_per_part),
3807
+ )
3808
+ motion_request = dict(payload)
3809
+ motion_request.pop("point_part_probabilities", None)
3810
+ motion_request["face_part_ids"] = np.asarray(face_part_ids, dtype=np.int32)
3811
+ motion_request["face_part_ids_unrefined"] = np.asarray(
3812
+ face_part_ids_unrefined,
3813
+ dtype=np.int32,
3814
+ )
3815
+ motion_request["visualization_path"] = str(early_visualization_path)
3816
+ return (
3817
+ motion_request,
3818
+ str(early_visualization_path),
3819
+ "Point query visualization ready. Running articulation prediction on GPU...",
3820
+ gr.update(interactive=False),
3821
+ )
3822
+
3823
+ def predict_motion_payload(self, payload: dict[str, Any] | None):
3824
+ if not payload:
3825
+ yield None, gr.update(), gr.update(interactive=False)
3826
+ return
3827
+
3828
+ try:
3829
+ args_payload = dict(payload["args"])
3830
+ mesh_path = Path(str(payload["mesh_path"]))
3831
+ canonical_up = str(payload["up_dir"])
3832
+ output_dir = Path(str(payload["output_dir"]))
3833
+ args = _make_inference_args(
3834
+ output_dir=output_dir,
3835
+ num_query_points=int(args_payload["num_query_points"]),
3836
+ num_query_points_per_face_for_seg=args_payload[
3837
+ "num_query_points_per_face_for_seg"
3838
+ ],
3839
+ query_batch_size=int(args_payload["query_batch_size"]),
3840
+ animation_frames=int(args_payload["animation_frames"]),
3841
+ strict_face_postprocess=bool(args_payload["strict_face_postprocess"]),
3842
+ enforce_connectivity_per_part=bool(
3843
+ args_payload["enforce_connectivity_per_part"]
3844
+ ),
3845
+ joint_decoding_confidence_temperature=float(
3846
+ args_payload["joint_decoding_confidence_temperature"]
3847
+ ),
3848
  )
3849
+ model, device = self._ensure_model_loaded()
3850
+ mesh_geometry = self._prepare_geometry(mesh_path, canonical_up)
3851
+ batch = _to_device_payload(payload["batch"], device)
3852
+ face_part_ids = np.asarray(payload["face_part_ids"], dtype=np.int32)
3853
  motion_artifacts = _compute_motion_prediction_artifacts(
3854
  args,
3855
  model=model,
 
3857
  normalized_mesh=mesh_geometry.normalized_mesh,
3858
  face_part_ids=face_part_ids,
3859
  joint_refit_num_query_points=int(args.num_query_points),
3860
+ num_links=len(payload["link_names"]),
3861
  center=mesh_geometry.center,
3862
  scale=mesh_geometry.scale,
3863
  )
3864
  prediction = {
3865
+ "query_points": np.asarray(payload["query_points"], dtype=np.float32),
3866
+ "query_normals": np.asarray(payload["query_normals"], dtype=np.float32),
3867
+ "point_part_ids": np.asarray(payload["point_part_ids"], dtype=np.int32),
3868
  "face_part_ids": face_part_ids,
3869
+ "face_part_ids_unrefined": np.asarray(
3870
+ payload["face_part_ids_unrefined"],
3871
+ dtype=np.int32,
3872
+ ),
3873
  **motion_artifacts,
3874
  }
3875
+ output_payload = {
3876
+ "args": args_payload,
3877
+ "mesh_path": str(mesh_path),
3878
+ "up_dir": str(canonical_up),
3879
+ "output_dir": str(output_dir),
3880
+ "query_face_indices": np.asarray(
3881
+ payload["query_face_indices"],
3882
+ dtype=np.int64,
3883
+ ),
3884
+ "link_names": [str(link_name) for link_name in payload["link_names"]],
3885
+ "joint_specs": [
3886
+ (int(parent), int(child), str(joint_type))
3887
+ for parent, child, joint_type in payload["joint_specs"]
3888
+ ],
3889
+ "prediction": _to_cpu_payload(prediction),
3890
+ "segmentation_num_query_points": int(
3891
+ payload["segmentation_num_query_points"]
3892
+ ),
3893
+ "visualization_path": str(payload["visualization_path"]),
3894
+ }
 
 
 
 
 
 
3895
  yield (
3896
+ output_payload,
3897
+ "Articulation prediction ready. Writing output files on CPU...",
 
 
 
 
 
 
 
3898
  gr.update(interactive=False),
3899
  )
3900
  finally:
3901
  torch.cuda.empty_cache()
3902
 
3903
+ def finish_predict_payload(self, payload: dict[str, Any] | None):
3904
+ if not payload:
3905
+ return (
3906
+ gr.update(),
3907
+ gr.update(),
3908
+ gr.update(),
3909
+ gr.update(),
3910
+ gr.update(),
3911
+ gr.update(),
3912
+ gr.update(interactive=False),
3913
+ )
3914
+
3915
+ args_payload = dict(payload["args"])
3916
+ mesh_path = Path(str(payload["mesh_path"]))
3917
+ canonical_up = str(payload["up_dir"])
3918
+ output_dir = Path(str(payload["output_dir"]))
3919
+ visualization_path = Path(str(payload["visualization_path"]))
3920
+ args = _make_inference_args(
3921
+ output_dir=output_dir,
3922
+ num_query_points=int(args_payload["num_query_points"]),
3923
+ num_query_points_per_face_for_seg=args_payload[
3924
+ "num_query_points_per_face_for_seg"
3925
+ ],
3926
+ query_batch_size=int(args_payload["query_batch_size"]),
3927
+ animation_frames=int(args_payload["animation_frames"]),
3928
+ strict_face_postprocess=bool(args_payload["strict_face_postprocess"]),
3929
+ enforce_connectivity_per_part=bool(
3930
+ args_payload["enforce_connectivity_per_part"]
3931
+ ),
3932
+ joint_decoding_confidence_temperature=float(
3933
+ args_payload["joint_decoding_confidence_temperature"]
3934
+ ),
3935
+ )
3936
+ mesh_geometry = self._prepare_geometry(mesh_path, canonical_up)
3937
+ self._write_outputs(
3938
+ args=args,
3939
+ mesh_path=mesh_path,
3940
+ up_dir=canonical_up,
3941
+ output_dir=output_dir,
3942
+ mesh_geometry=mesh_geometry,
3943
+ batch={},
3944
+ query_face_indices=np.asarray(
3945
+ payload["query_face_indices"],
3946
+ dtype=np.int64,
3947
+ ),
3948
+ link_names=[str(link_name) for link_name in payload["link_names"]],
3949
+ joint_specs=[
3950
+ (int(parent), int(child), str(joint_type))
3951
+ for parent, child, joint_type in payload["joint_specs"]
3952
+ ],
3953
+ prediction=payload["prediction"],
3954
+ segmentation_num_query_points=int(payload["segmentation_num_query_points"]),
3955
+ visualization_path=visualization_path,
3956
+ )
3957
+ zip_path = _zip_directory(output_dir)
3958
+ return (
3959
+ str(output_dir / "animated_textured.glb"),
3960
+ str(output_dir / "mesh_parts_with_axes.glb"),
3961
+ gr.update(),
3962
+ str(zip_path),
3963
+ str(output_dir),
3964
+ f"Success using input up direction {canonical_up}. Wrote outputs to {output_dir}",
3965
+ gr.update(interactive=True),
3966
+ )
3967
+
3968
  def _prepare_geometry(self, mesh_path: Path, up_dir: str):
3969
  from infer import _prepare_mesh_geometry
3970
 
 
4255
  enforce_connectivity_per_part: bool,
4256
  joint_decoding_confidence_temperature: float,
4257
  ):
4258
+ yield from _get_active_app().predict_segmentation_payload(
4259
  mesh_path_value,
4260
  kinematic_tree_json,
4261
  point_prompt_json,
 
4270
  )
4271
 
4272
 
4273
+ def postprocess_segmentation_on_cpu(payload: dict[str, Any] | None):
4274
+ return _get_active_app().postprocess_segmentation_payload(payload)
4275
+
4276
+
4277
+ @_spaces_gpu
4278
+ def run_motion_on_gpu(payload: dict[str, Any] | None):
4279
+ yield from _get_active_app().predict_motion_payload(payload)
4280
+
4281
+
4282
+ def finish_predict_on_cpu(payload: dict[str, Any] | None):
4283
+ return _get_active_app().finish_predict_payload(payload)
4284
+
4285
+
4286
  def prepare_inference_ui():
4287
  return (
4288
+ None,
4289
  None,
4290
  gr.update(interactive=False),
4291
  gr.update(value=None, interactive=False),
 
4393
  elem_classes=["kinematic-json-sync"],
4394
  )
4395
  latest_output_dir = gr.State(None)
4396
+ inference_payload = gr.State(None)
4397
 
4398
  with gr.Row(equal_height=True, elem_classes=["demo-row", "demo-top-row"]):
4399
  with gr.Column(scale=1, min_width=300, elem_classes=["demo-panel", "mesh-panel"]):
 
4606
  fn=prepare_inference_ui,
4607
  inputs=None,
4608
  outputs=[
4609
+ inference_payload,
4610
  latest_output_dir,
4611
  export_urdf_button,
4612
  urdf_zip,
 
4614
  ],
4615
  queue=False,
4616
  )
4617
+ gpu_event = run_event.then(
4618
  fn=run_predict_on_gpu,
4619
  inputs=[
4620
  input_mesh,
 
4629
  connectivity,
4630
  confidence_temperature,
4631
  ],
4632
+ outputs=[
4633
+ inference_payload,
4634
+ status,
4635
+ export_urdf_button,
4636
+ ],
4637
+ )
4638
+ postprocess_event = gpu_event.then(
4639
+ fn=postprocess_segmentation_on_cpu,
4640
+ inputs=[inference_payload],
4641
+ outputs=[
4642
+ inference_payload,
4643
+ query_visualization,
4644
+ status,
4645
+ export_urdf_button,
4646
+ ],
4647
+ )
4648
+ motion_event = postprocess_event.then(
4649
+ fn=run_motion_on_gpu,
4650
+ inputs=[inference_payload],
4651
+ outputs=[
4652
+ inference_payload,
4653
+ status,
4654
+ export_urdf_button,
4655
+ ],
4656
+ )
4657
+ motion_event.then(
4658
+ fn=finish_predict_on_cpu,
4659
+ inputs=[inference_payload],
4660
  outputs=[
4661
  animated_model,
4662
  prediction_model,