multimodalart HF Staff commited on
Commit
161c052
·
verified ·
1 Parent(s): 3b53547

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +38 -18
app.py CHANGED
@@ -758,19 +758,27 @@ with gr.Blocks() as demo:
758
  height = gr.Slider(label="Height", minimum=256, maximum=2048, step=8, value=1024)
759
  width = gr.Slider(label="Width", minimum=256, maximum=2048, step=8, value=1024)
760
 
 
 
 
761
  # --- Helper Functions ---
762
  def update_prompt_from_sliders(rotate, forward, tilt, wide):
763
  return build_camera_prompt(rotate, forward, tilt, wide)
764
 
765
  def sync_3d_to_sliders(camera_value):
 
766
  if camera_value and isinstance(camera_value, dict):
767
  rot = camera_value.get('rotate_deg', 0)
768
  fwd = camera_value.get('move_forward', 0)
769
  tilt = camera_value.get('vertical_tilt', 0)
770
  wide = camera_value.get('wideangle', False)
771
  prompt = build_camera_prompt(rot, fwd, tilt, wide)
772
- return rot, fwd, tilt, wide, prompt
773
- return gr.update(), gr.update(), gr.update(), gr.update(), gr.update()
 
 
 
 
774
 
775
  def sync_sliders_to_3d(rotate, forward, tilt, wide):
776
  return {"rotate_deg": rotate, "move_forward": forward, "vertical_tilt": tilt, "wideangle": wide}
@@ -784,6 +792,20 @@ with gr.Blocks() as demo:
784
  data_url = f"data:image/png;base64,{img_str}"
785
  return gr.update(imageUrl=data_url)
786
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
787
  # Define inputs/outputs
788
  inputs = [image, rotate_deg, move_forward, vertical_tilt, wideangle, seed, randomize_seed, true_guidance_scale, num_inference_steps, height, width, prev_output]
789
  outputs = [result, seed, prompt_preview]
@@ -804,18 +826,22 @@ with gr.Blocks() as demo:
804
  slider.change(fn=update_prompt_from_sliders, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[prompt_preview])
805
  wideangle.change(fn=update_prompt_from_sliders, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[prompt_preview])
806
 
807
- # 3D control -> Sliders + Prompt + Inference
808
  camera_3d.change(
809
  fn=sync_3d_to_sliders,
810
  inputs=[camera_3d],
811
- outputs=[rotate_deg, move_forward, vertical_tilt, wideangle, prompt_preview]
812
  ).then(
813
  fn=maybe_infer,
814
  inputs=control_inputs_with_flag,
815
  outputs=outputs + [create_video_button]
 
 
 
 
816
  )
817
 
818
- # Sliders -> 3D control
819
  for slider in [rotate_deg, move_forward, vertical_tilt]:
820
  slider.release(fn=sync_sliders_to_3d, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[camera_3d])
821
  wideangle.input(fn=sync_sliders_to_3d, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[camera_3d])
@@ -845,7 +871,7 @@ with gr.Blocks() as demo:
845
 
846
  image.clear(fn=lambda: gr.update(imageUrl=None), outputs=[camera_3d])
847
 
848
- # Live updates on slider release
849
  for control in [rotate_deg, move_forward, vertical_tilt]:
850
  control.release(fn=maybe_infer, inputs=control_inputs_with_flag, outputs=outputs + [create_video_button])
851
  wideangle.input(fn=maybe_infer, inputs=control_inputs_with_flag, outputs=outputs + [create_video_button])
@@ -870,19 +896,13 @@ with gr.Blocks() as demo:
870
  )
871
 
872
  # Sync 3D component when sliders change (covers example loading)
873
- def sync_3d_on_slider_change(img, rot, fwd, tilt, wide):
874
- camera_value = {"rotate_deg": rot, "move_forward": fwd, "vertical_tilt": tilt, "wideangle": wide}
875
- if img is not None:
876
- buffered = BytesIO()
877
- img.save(buffered, format="PNG")
878
- img_str = base64.b64encode(buffered.getvalue()).decode()
879
- data_url = f"data:image/png;base64,{img_str}"
880
- return gr.update(value=camera_value, imageUrl=data_url)
881
- return gr.update(value=camera_value)
882
-
883
- # When any slider value changes (including from examples), sync the 3D component
884
  for slider in [rotate_deg, move_forward, vertical_tilt]:
885
- slider.change(fn=sync_3d_on_slider_change, inputs=[image, rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[camera_3d])
 
 
 
 
886
 
887
  gr.api(infer_camera_edit, api_name="infer_edit_camera_angles")
888
  gr.api(create_video_between_images, api_name="create_video_between_images")
 
758
  height = gr.Slider(label="Height", minimum=256, maximum=2048, step=8, value=1024)
759
  width = gr.Slider(label="Width", minimum=256, maximum=2048, step=8, value=1024)
760
 
761
+ # State to prevent circular updates between 3D control and sliders
762
+ is_syncing_from_3d = gr.State(False)
763
+
764
  # --- Helper Functions ---
765
  def update_prompt_from_sliders(rotate, forward, tilt, wide):
766
  return build_camera_prompt(rotate, forward, tilt, wide)
767
 
768
  def sync_3d_to_sliders(camera_value):
769
+ """Sync 3D control values to sliders. Returns syncing flag = True."""
770
  if camera_value and isinstance(camera_value, dict):
771
  rot = camera_value.get('rotate_deg', 0)
772
  fwd = camera_value.get('move_forward', 0)
773
  tilt = camera_value.get('vertical_tilt', 0)
774
  wide = camera_value.get('wideangle', False)
775
  prompt = build_camera_prompt(rot, fwd, tilt, wide)
776
+ return rot, fwd, tilt, wide, prompt, True # Set syncing flag
777
+ return gr.update(), gr.update(), gr.update(), gr.update(), gr.update(), False
778
+
779
+ def clear_syncing_flag():
780
+ """Clear the syncing flag after sync is complete."""
781
+ return False
782
 
783
  def sync_sliders_to_3d(rotate, forward, tilt, wide):
784
  return {"rotate_deg": rotate, "move_forward": forward, "vertical_tilt": tilt, "wideangle": wide}
 
792
  data_url = f"data:image/png;base64,{img_str}"
793
  return gr.update(imageUrl=data_url)
794
 
795
+ def sync_3d_on_slider_change_if_not_syncing(is_syncing, img, rot, fwd, tilt, wide):
796
+ """Only sync sliders to 3D if we're not already syncing from 3D."""
797
+ if is_syncing:
798
+ # Skip update to prevent circular trigger
799
+ return gr.update()
800
+ camera_value = {"rotate_deg": rot, "move_forward": fwd, "vertical_tilt": tilt, "wideangle": wide}
801
+ if img is not None:
802
+ buffered = BytesIO()
803
+ img.save(buffered, format="PNG")
804
+ img_str = base64.b64encode(buffered.getvalue()).decode()
805
+ data_url = f"data:image/png;base64,{img_str}"
806
+ return gr.update(value=camera_value, imageUrl=data_url)
807
+ return gr.update(value=camera_value)
808
+
809
  # Define inputs/outputs
810
  inputs = [image, rotate_deg, move_forward, vertical_tilt, wideangle, seed, randomize_seed, true_guidance_scale, num_inference_steps, height, width, prev_output]
811
  outputs = [result, seed, prompt_preview]
 
826
  slider.change(fn=update_prompt_from_sliders, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[prompt_preview])
827
  wideangle.change(fn=update_prompt_from_sliders, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[prompt_preview])
828
 
829
+ # 3D control -> Sliders + Prompt + Inference (with syncing flag)
830
  camera_3d.change(
831
  fn=sync_3d_to_sliders,
832
  inputs=[camera_3d],
833
+ outputs=[rotate_deg, move_forward, vertical_tilt, wideangle, prompt_preview, is_syncing_from_3d]
834
  ).then(
835
  fn=maybe_infer,
836
  inputs=control_inputs_with_flag,
837
  outputs=outputs + [create_video_button]
838
+ ).then(
839
+ fn=clear_syncing_flag,
840
+ inputs=None,
841
+ outputs=[is_syncing_from_3d]
842
  )
843
 
844
+ # Sliders -> 3D control (only on user release, not programmatic changes)
845
  for slider in [rotate_deg, move_forward, vertical_tilt]:
846
  slider.release(fn=sync_sliders_to_3d, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[camera_3d])
847
  wideangle.input(fn=sync_sliders_to_3d, inputs=[rotate_deg, move_forward, vertical_tilt, wideangle], outputs=[camera_3d])
 
871
 
872
  image.clear(fn=lambda: gr.update(imageUrl=None), outputs=[camera_3d])
873
 
874
+ # Live updates on slider release (user interaction only)
875
  for control in [rotate_deg, move_forward, vertical_tilt]:
876
  control.release(fn=maybe_infer, inputs=control_inputs_with_flag, outputs=outputs + [create_video_button])
877
  wideangle.input(fn=maybe_infer, inputs=control_inputs_with_flag, outputs=outputs + [create_video_button])
 
896
  )
897
 
898
  # Sync 3D component when sliders change (covers example loading)
899
+ # Uses syncing flag to prevent circular updates
 
 
 
 
 
 
 
 
 
 
900
  for slider in [rotate_deg, move_forward, vertical_tilt]:
901
+ slider.change(
902
+ fn=sync_3d_on_slider_change_if_not_syncing,
903
+ inputs=[is_syncing_from_3d, image, rotate_deg, move_forward, vertical_tilt, wideangle],
904
+ outputs=[camera_3d]
905
+ )
906
 
907
  gr.api(infer_camera_edit, api_name="infer_edit_camera_angles")
908
  gr.api(create_video_between_images, api_name="create_video_between_images")