Zhaoting123 commited on
Commit
80f274e
·
verified ·
1 Parent(s): cabf23d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -39
app.py CHANGED
@@ -579,35 +579,58 @@ def preload_current_trajectory(preset_name, custom_repo_id, custom_filename, tra
579
  return status
580
 
581
 
582
- def _compose_video_frame(gallery_items, frame_label):
 
 
 
 
 
583
  if not gallery_items:
584
- canvas = Image.new("RGB", (640, 360), color=(20, 20, 20))
585
- draw = ImageDraw.Draw(canvas)
586
  draw.text((16, 16), "No selected image keys", fill=(255, 255, 255))
587
- return np.asarray(canvas)
588
-
589
- pil_images = []
590
- for img, label in gallery_items:
591
- pil_img = Image.fromarray(np.asarray(img, dtype=np.uint8)).convert("RGB")
592
- label_h = 24
593
- panel = Image.new("RGB", (pil_img.width, pil_img.height + label_h), color=(0, 0, 0))
594
- panel.paste(pil_img, (0, label_h))
595
- draw = ImageDraw.Draw(panel)
596
- draw.text((6, 4), str(label), fill=(255, 255, 255))
597
- pil_images.append(panel)
598
-
599
- gap = 8
600
- top_h = 28
601
- width = sum(im.width for im in pil_images) + gap * max(len(pil_images) - 1, 0)
602
- height = max(im.height for im in pil_images) + top_h
603
- canvas = Image.new("RGB", (width, height), color=(0, 0, 0))
604
- draw = ImageDraw.Draw(canvas)
605
- draw.text((8, 6), frame_label, fill=(255, 255, 255))
606
-
607
- x = 0
608
- for im in pil_images:
609
- canvas.paste(im, (x, top_h))
610
- x += im.width + gap
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
611
 
612
  pad_w = int(np.ceil(canvas.width / 16.0) * 16)
613
  pad_h = int(np.ceil(canvas.height / 16.0) * 16)
@@ -619,7 +642,7 @@ def _compose_video_frame(gallery_items, frame_label):
619
  return np.asarray(canvas)
620
 
621
 
622
- def build_current_trajectory_video(preset_name, custom_repo_id, custom_filename, traj_id, image_keys, display_scale, reverse_channels, fps):
623
  if imageio is None:
624
  return None, "Video export requires imageio and imageio-ffmpeg in requirements.txt."
625
 
@@ -659,14 +682,22 @@ def build_current_trajectory_video(preset_name, custom_repo_id, custom_filename,
659
  bool(reverse_channels),
660
  )
661
  label = "trajectory {} | frame {}/{}".format(traj_id, t, len(traj) - 1)
662
- frame = _compose_video_frame(gallery_items, label)
 
 
 
 
 
 
 
663
  writer.append_data(frame)
664
  finally:
665
  writer.close()
666
 
667
- status = "Built trajectory video"
668
  status += "\nTrajectory: {}".format(traj_id)
669
  status += "\nFrames: {} | FPS: {}".format(len(traj), fps)
 
670
  return out_path, status
671
 
672
 
@@ -853,15 +884,37 @@ def build_app():
853
  video_fps = gr.Slider(minimum=1, maximum=30, value=10, step=1, label="Video FPS")
854
 
855
  preload_status = gr.Textbox(label="Preload / video status", lines=4, value="Not preloaded yet.", interactive=False)
856
- trajectory_video = gr.Video(label="Trajectory video: smooth browser-side playback")
857
-
858
- gallery = gr.Gallery(label="Observation images", columns=2, height="auto", object_fit="contain")
859
- status_plot = gr.Image(label="Trajectory status: no_teacher_action and valid starts", type="numpy")
860
- info = gr.Textbox(label="Frame info", lines=16)
861
 
862
- with gr.Accordion("Debug: HDF5 tree", open=False):
863
- inspect_btn = gr.Button("Inspect HDF5 structure")
864
- hdf5_tree = gr.Textbox(lines=24, label="HDF5 tree")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
 
866
  preset.change(
867
  fn=update_custom_visibility,
@@ -925,7 +978,7 @@ def build_app():
925
 
926
  video_btn.click(
927
  fn=build_current_trajectory_video,
928
- inputs=[preset, custom_repo_id, custom_filename, traj_slider, image_keys, display_scale, reverse_channels, video_fps],
929
  outputs=[trajectory_video, preload_status],
930
  )
931
 
 
579
  return status
580
 
581
 
582
+ def _compose_video_frame(gallery_items, frame_label, status_plot=None):
583
+ """Compose one video frame.
584
+
585
+ Top: selected observation images.
586
+ Bottom: trajectory-status plot with the moving timestep cursor.
587
+ """
588
  if not gallery_items:
589
+ obs_canvas = Image.new("RGB", (640, 360), color=(20, 20, 20))
590
+ draw = ImageDraw.Draw(obs_canvas)
591
  draw.text((16, 16), "No selected image keys", fill=(255, 255, 255))
592
+ else:
593
+ pil_images = []
594
+ for img, label in gallery_items:
595
+ pil_img = Image.fromarray(np.asarray(img, dtype=np.uint8)).convert("RGB")
596
+ label_h = 24
597
+ panel = Image.new("RGB", (pil_img.width, pil_img.height + label_h), color=(0, 0, 0))
598
+ panel.paste(pil_img, (0, label_h))
599
+ draw = ImageDraw.Draw(panel)
600
+ draw.text((6, 4), str(label), fill=(255, 255, 255))
601
+ pil_images.append(panel)
602
+
603
+ gap = 8
604
+ top_h = 28
605
+ width = sum(im.width for im in pil_images) + gap * max(len(pil_images) - 1, 0)
606
+ height = max(im.height for im in pil_images) + top_h
607
+ obs_canvas = Image.new("RGB", (width, height), color=(0, 0, 0))
608
+ draw = ImageDraw.Draw(obs_canvas)
609
+ draw.text((8, 6), frame_label, fill=(255, 255, 255))
610
+
611
+ x = 0
612
+ for im in pil_images:
613
+ obs_canvas.paste(im, (x, top_h))
614
+ x += im.width + gap
615
+
616
+ if status_plot is not None:
617
+ status_img = Image.fromarray(np.asarray(status_plot, dtype=np.uint8)).convert("RGB")
618
+
619
+ target_w = obs_canvas.width
620
+ if status_img.width != target_w:
621
+ target_h = max(1, int(round(status_img.height * float(target_w) / float(status_img.width))))
622
+ status_img = status_img.resize((target_w, target_h), resample=Image.Resampling.BILINEAR)
623
+
624
+ gap_h = 8
625
+ canvas = Image.new(
626
+ "RGB",
627
+ (max(obs_canvas.width, status_img.width), obs_canvas.height + gap_h + status_img.height),
628
+ color=(0, 0, 0),
629
+ )
630
+ canvas.paste(obs_canvas, (0, 0))
631
+ canvas.paste(status_img, (0, obs_canvas.height + gap_h))
632
+ else:
633
+ canvas = obs_canvas
634
 
635
  pad_w = int(np.ceil(canvas.width / 16.0) * 16)
636
  pad_h = int(np.ceil(canvas.height / 16.0) * 16)
 
642
  return np.asarray(canvas)
643
 
644
 
645
+ def build_current_trajectory_video(preset_name, custom_repo_id, custom_filename, traj_id, image_keys, display_scale, reverse_channels, fps, valid_window_len):
646
  if imageio is None:
647
  return None, "Video export requires imageio and imageio-ffmpeg in requirements.txt."
648
 
 
682
  bool(reverse_channels),
683
  )
684
  label = "trajectory {} | frame {}/{}".format(traj_id, t, len(traj) - 1)
685
+ status_plot, _is_valid_start, _num_valid_starts = get_cached_status_plot(
686
+ repo_id,
687
+ filename,
688
+ traj_id,
689
+ t,
690
+ int(valid_window_len),
691
+ )
692
+ frame = _compose_video_frame(gallery_items, label, status_plot=status_plot)
693
  writer.append_data(frame)
694
  finally:
695
  writer.close()
696
 
697
+ status = "Built trajectory video with moving status plot"
698
  status += "\nTrajectory: {}".format(traj_id)
699
  status += "\nFrames: {} | FPS: {}".format(len(traj), fps)
700
+ status += "\nValid-window length: {}".format(int(valid_window_len))
701
  return out_path, status
702
 
703
 
 
884
  video_fps = gr.Slider(minimum=1, maximum=30, value=10, step=1, label="Video FPS")
885
 
886
  preload_status = gr.Textbox(label="Preload / video status", lines=4, value="Not preloaded yet.", interactive=False)
 
 
 
 
 
887
 
888
+ with gr.Tabs():
889
+ with gr.Tab("Frame viewer"):
890
+ with gr.Row():
891
+ with gr.Column(scale=3):
892
+ gallery = gr.Gallery(
893
+ label="Observation images",
894
+ columns=2,
895
+ height="auto",
896
+ object_fit="contain",
897
+ )
898
+ with gr.Column(scale=2):
899
+ status_plot = gr.Image(
900
+ label="Trajectory status: no_teacher_action and valid starts",
901
+ type="numpy",
902
+ )
903
+ info = gr.Textbox(label="Frame info", lines=12)
904
+
905
+ with gr.Tab("Trajectory video"):
906
+ trajectory_video = gr.Video(
907
+ label="Trajectory video: observation images + moving status plot",
908
+ height=520,
909
+ )
910
+ gr.Markdown(
911
+ "The video is generated from the selected trajectory and image keys. "
912
+ "Use this tab for smooth browser-side playback/scrubbing."
913
+ )
914
+
915
+ with gr.Tab("Debug"):
916
+ inspect_btn = gr.Button("Inspect HDF5 structure")
917
+ hdf5_tree = gr.Textbox(lines=24, label="HDF5 tree")
918
 
919
  preset.change(
920
  fn=update_custom_visibility,
 
978
 
979
  video_btn.click(
980
  fn=build_current_trajectory_video,
981
+ inputs=[preset, custom_repo_id, custom_filename, traj_slider, image_keys, display_scale, reverse_channels, video_fps, chunk_len],
982
  outputs=[trajectory_video, preload_status],
983
  )
984