Silvio Galesso commited on
Commit
86d8d8b
·
1 Parent(s): 380643c

appearance fixes

Browse files
Files changed (2) hide show
  1. app2.py +43 -8
  2. orbis2_app_engine.py +8 -0
app2.py CHANGED
@@ -192,6 +192,10 @@ if UI_ONLY:
192
  # auto-seek stay a no-op instead of erroring.
193
  MIN_CONTEXT_END_FRAME = 0
194
  MIN_CONTEXT_TIME_S = 0.0
 
 
 
 
195
  else:
196
  MODELS_DIR = resolve_models_dir()
197
  os.environ["ORBIS2_MODELS_DIR"] = str(MODELS_DIR) # L1/L2 configs expandvars this
@@ -210,7 +214,22 @@ else:
210
  MIN_CONTEXT_END_FRAME = ENGINE.min_context_end_frame(L1_FRAME_RATE)
211
  MIN_CONTEXT_TIME_S = MIN_CONTEXT_END_FRAME / CONTEXT_FPS
212
 
 
 
 
 
 
 
 
213
  DEFAULT_GEN_STEPS = 10 # rollout steps; each yields model.num_pred_frames frames
 
 
 
 
 
 
 
 
214
  # L1 and L2 are sampled at different step counts. L1 (detail) needs more steps for
215
  # image fidelity; L2 (abstract, consistency-distilled) is designed for very few.
216
  DEFAULT_L1_STEPS = 4 # L1 sampler steps (NFE)
@@ -291,8 +310,9 @@ is resized to {FRAME_H}×{FRAME_W}. The **L2 abstract predictor** forecasts a la
291
  future, and the **L1 detail predictor**, conditioned on it, generates the next frames
292
  autoregressively.
293
 
294
- **Rollout length.** *Rollout steps* sets how many autoregressive steps to run; each
295
- step emits several image frames, so the final clip is longer than the step count.
 
296
 
297
  **Sampling.** The two levels are sampled independently, with their own step counts.
298
  *L1 sampler steps* controls the detail predictor, which integrates the flow-matching
@@ -504,7 +524,7 @@ def render_trajectory_plot(raw_json: str):
504
  # because it's inserted as real page <head> content.
505
  # --------------------------------------------------------------------------
506
  TRAJ_CANVAS_HTML_JS = f"""
507
- <div style="position:relative; width:{TRAJ_CANVAS_SIZE_PX}px;">
508
  <canvas id="traj-canvas" width="{TRAJ_CANVAS_SIZE_PX}" height="{TRAJ_CANVAS_SIZE_PX}"
509
  style="display:block; border:1px solid #999; border-radius:8px; cursor:crosshair; background:#fafafa;">
510
  </canvas>
@@ -843,7 +863,7 @@ def _parse_cursor_time_s(raw) -> float | None:
843
  # GPU inference
844
  # ----------------------------------------------------------------------------
845
  @spaces.GPU(duration=300)
846
- def run_rollout(video_path: str, num_gen_steps: int, l1_steps: int, l2_steps: int,
847
  seed: int, trajectory, cursor_time_s: str, progress=gr.Progress()):
848
  if video_path is None:
849
  raise gr.Error("Please upload a short mp4 clip first.")
@@ -867,6 +887,10 @@ def run_rollout(video_path: str, num_gen_steps: int, l1_steps: int, l2_steps: in
867
  # eta is not exposed: 0.0 keeps sampling on the deterministic ODE.
868
  eta = DEFAULT_ETA
869
 
 
 
 
 
870
  job = Path(tempfile.gettempdir()) / f"orbis2_{uuid.uuid4().hex[:8]}"
871
 
872
  say(0.02, "⏳ Loading…")
@@ -979,9 +1003,10 @@ def build_demo():
979
 
980
  with gr.Row():
981
  with gr.Column(scale=1):
982
- n_steps_gen = gr.Slider(4, 30, value=DEFAULT_GEN_STEPS, step=2,
983
- label="Rollout steps",
984
- info="Each step emits several frames")
 
985
  l1_steps = gr.Slider(5, 20, value=DEFAULT_L1_STEPS, step=1,
986
  label="L1 sampler steps",
987
  info="Detail predictor — more steps, sharper frames")
@@ -997,6 +1022,10 @@ def build_demo():
997
  inp = gr.Video(label="Upload a short mp4 clip", sources=["upload"],
998
  elem_id="input_video")
999
  cursor_time_s = gr.Textbox(elem_id="cursor_time_s", value="-1", visible=False)
 
 
 
 
1000
 
1001
  with gr.Column(scale=1):
1002
  gr.Markdown(
@@ -1005,6 +1034,12 @@ def build_demo():
1005
  "it untouched, or hit Clear, to run the rollout unconditionally."
1006
  )
1007
  gr.HTML(TRAJ_CANVAS_HTML_JS)
 
 
 
 
 
 
1008
  raw_points_json = gr.Textbox(elem_id="raw_points_json", visible=False)
1009
  smoothed_points_json = gr.Textbox(elem_id="smoothed_points_json", visible=False)
1010
  trajectory_state = gr.State()
@@ -1027,7 +1062,7 @@ def build_demo():
1027
 
1028
  btn.click(
1029
  run_rollout,
1030
- inputs=[inp, n_steps_gen, l1_steps, l2_steps, seed_in, trajectory_state, cursor_time_s],
1031
  outputs=outs,
1032
  concurrency_limit=1,
1033
  )
 
192
  # auto-seek stay a no-op instead of erroring.
193
  MIN_CONTEXT_END_FRAME = 0
194
  MIN_CONTEXT_TIME_S = 0.0
195
+ # No model to read model.num_pred_frames from -- 1 is just a placeholder so the
196
+ # UI-only slider below has *some* seconds-per-step to show; it's never used for
197
+ # an actual rollout (run_rollout raises before reaching the model in this mode).
198
+ FRAMES_PER_GEN_STEP = 1
199
  else:
200
  MODELS_DIR = resolve_models_dir()
201
  os.environ["ORBIS2_MODELS_DIR"] = str(MODELS_DIR) # L1/L2 configs expandvars this
 
214
  MIN_CONTEXT_END_FRAME = ENGINE.min_context_end_frame(L1_FRAME_RATE)
215
  MIN_CONTEXT_TIME_S = MIN_CONTEXT_END_FRAME / CONTEXT_FPS
216
 
217
+ FRAMES_PER_GEN_STEP = ENGINE.frames_per_rollout_step
218
+
219
+ # Seconds of output video one rollout step yields, at L1_FRAME_RATE fps -- lets the
220
+ # UI expose rollout length in seconds while the model itself still steps in whole
221
+ # rollout steps (see GEN_SECONDS_STEP / run_rollout's seconds -> steps conversion below).
222
+ SECONDS_PER_GEN_STEP = FRAMES_PER_GEN_STEP / L1_FRAME_RATE
223
+
224
  DEFAULT_GEN_STEPS = 10 # rollout steps; each yields model.num_pred_frames frames
225
+ GEN_STEPS_MIN, GEN_STEPS_MAX, GEN_STEPS_STEP = 4, 30, 2 # bounds on the underlying step count
226
+
227
+ # The slider itself is in seconds (see build_demo()); these are its bounds, derived
228
+ # from the step-count bounds above via SECONDS_PER_GEN_STEP.
229
+ GEN_SECONDS_MIN = GEN_STEPS_MIN * SECONDS_PER_GEN_STEP
230
+ GEN_SECONDS_MAX = GEN_STEPS_MAX * SECONDS_PER_GEN_STEP
231
+ GEN_SECONDS_STEP = GEN_STEPS_STEP * SECONDS_PER_GEN_STEP
232
+ DEFAULT_GEN_SECONDS = DEFAULT_GEN_STEPS * SECONDS_PER_GEN_STEP
233
  # L1 and L2 are sampled at different step counts. L1 (detail) needs more steps for
234
  # image fidelity; L2 (abstract, consistency-distilled) is designed for very few.
235
  DEFAULT_L1_STEPS = 4 # L1 sampler steps (NFE)
 
310
  future, and the **L1 detail predictor**, conditioned on it, generates the next frames
311
  autoregressively.
312
 
313
+ **Rollout length.** *Generated video length (s)* sets how much video to generate; under
314
+ the hood this is rounded to a whole number of autoregressive steps, each of which emits
315
+ {FRAMES_PER_GEN_STEP} image frame{"s" if FRAMES_PER_GEN_STEP != 1 else ""} ({SECONDS_PER_GEN_STEP:.2g}s of video per step).
316
 
317
  **Sampling.** The two levels are sampled independently, with their own step counts.
318
  *L1 sampler steps* controls the detail predictor, which integrates the flow-matching
 
524
  # because it's inserted as real page <head> content.
525
  # --------------------------------------------------------------------------
526
  TRAJ_CANVAS_HTML_JS = f"""
527
+ <div style="position:relative; width:{TRAJ_CANVAS_SIZE_PX}px; margin:0 auto;">
528
  <canvas id="traj-canvas" width="{TRAJ_CANVAS_SIZE_PX}" height="{TRAJ_CANVAS_SIZE_PX}"
529
  style="display:block; border:1px solid #999; border-radius:8px; cursor:crosshair; background:#fafafa;">
530
  </canvas>
 
863
  # GPU inference
864
  # ----------------------------------------------------------------------------
865
  @spaces.GPU(duration=300)
866
+ def run_rollout(video_path: str, gen_seconds: float, l1_steps: int, l2_steps: int,
867
  seed: int, trajectory, cursor_time_s: str, progress=gr.Progress()):
868
  if video_path is None:
869
  raise gr.Error("Please upload a short mp4 clip first.")
 
887
  # eta is not exposed: 0.0 keeps sampling on the deterministic ODE.
888
  eta = DEFAULT_ETA
889
 
890
+ # The model itself only understands whole rollout steps -- convert the
891
+ # seconds the user picked back into a step count (see SECONDS_PER_GEN_STEP).
892
+ num_gen_steps = max(1, round(gen_seconds / SECONDS_PER_GEN_STEP))
893
+
894
  job = Path(tempfile.gettempdir()) / f"orbis2_{uuid.uuid4().hex[:8]}"
895
 
896
  say(0.02, "⏳ Loading…")
 
1003
 
1004
  with gr.Row():
1005
  with gr.Column(scale=1):
1006
+ n_seconds_gen = gr.Slider(GEN_SECONDS_MIN, GEN_SECONDS_MAX, value=DEFAULT_GEN_SECONDS,
1007
+ step=GEN_SECONDS_STEP,
1008
+ label="Generated video length (s)",
1009
+ info=f"Generated in chunks of {SECONDS_PER_GEN_STEP:.2g}s each")
1010
  l1_steps = gr.Slider(5, 20, value=DEFAULT_L1_STEPS, step=1,
1011
  label="L1 sampler steps",
1012
  info="Detail predictor — more steps, sharper frames")
 
1022
  inp = gr.Video(label="Upload a short mp4 clip", sources=["upload"],
1023
  elem_id="input_video")
1024
  cursor_time_s = gr.Textbox(elem_id="cursor_time_s", value="-1", visible=False)
1025
+ gr.Markdown(
1026
+ "Use the video cursor to pick where the model generation should "
1027
+ "start (i.e. the last context frame)."
1028
+ )
1029
 
1030
  with gr.Column(scale=1):
1031
  gr.Markdown(
 
1034
  "it untouched, or hit Clear, to run the rollout unconditionally."
1035
  )
1036
  gr.HTML(TRAJ_CANVAS_HTML_JS)
1037
+ gr.HTML(
1038
+ '<p style="font-size:11px; color:#555; margin-top:6px;">'
1039
+ "Disclaimer: hand-drawn trajectories are likely to be "
1040
+ "out-of-distribution for the model and can generate unexpected "
1041
+ "outputs. Happy crashing!</p>"
1042
+ )
1043
  raw_points_json = gr.Textbox(elem_id="raw_points_json", visible=False)
1044
  smoothed_points_json = gr.Textbox(elem_id="smoothed_points_json", visible=False)
1045
  trajectory_state = gr.State()
 
1062
 
1063
  btn.click(
1064
  run_rollout,
1065
+ inputs=[inp, n_seconds_gen, l1_steps, l2_steps, seed_in, trajectory_state, cursor_time_s],
1066
  outputs=outs,
1067
  concurrency_limit=1,
1068
  )
orbis2_app_engine.py CHANGED
@@ -168,6 +168,14 @@ class RolloutEngine:
168
  )
169
  return indexer.get_required_l1_start_offset() + l1_context_frames - 1
170
 
 
 
 
 
 
 
 
 
171
  def ensure_ready(self, device="cuda", compile=True, compile_mode="reduce-overhead",
172
  compile_artifacts=None, speed_scale=1.0, yaw_rate_scale=1.0):
173
  """Move the model to `device` and wrap it with torch.compile, exactly once.
 
168
  )
169
  return indexer.get_required_l1_start_offset() + l1_context_frames - 1
170
 
171
+ @property
172
+ def frames_per_rollout_step(self):
173
+ """Number of output frames each autoregressive rollout step yields
174
+ (model.num_pred_frames). CPU-only (model metadata), callable right after
175
+ load(). Lets callers convert the "rollout steps" the model actually takes
176
+ into seconds of generated video (frames_per_rollout_step / l1_frame_rate)."""
177
+ return int(self.model.num_pred_frames)
178
+
179
  def ensure_ready(self, device="cuda", compile=True, compile_mode="reduce-overhead",
180
  compile_artifacts=None, speed_scale=1.0, yaw_rate_scale=1.0):
181
  """Move the model to `device` and wrap it with torch.compile, exactly once.