severo HF staff commited on
Commit
23222d6
·
1 Parent(s): 89f58c0

images => video

Browse files
Files changed (1) hide show
  1. app.py +90 -60
app.py CHANGED
@@ -13,8 +13,6 @@
13
  # - x: 128 to 512
14
  # - y: 128 to 512
15
  # - physical fields: 2 to 8 (density, pressure, energy, velocity…)
16
- from functools import lru_cache
17
-
18
  import gradio as gr
19
  import h5py
20
  import numpy as np
@@ -22,8 +20,16 @@ from fsspec import url_to_fs
22
  from matplotlib import cm
23
  from PIL import Image
24
  import av
25
- import io
 
 
 
 
 
 
 
26
 
 
27
 
28
  repo_id = "lhoestq/turbulent_radiative_layer_tcool_demo"
29
  set_path = f"hf://datasets/{repo_id}/**/*.hdf5"
@@ -31,103 +37,127 @@ fs, _ = url_to_fs(set_path)
31
  paths = fs.glob(set_path)
32
  files = {path: h5py.File(fs.open(path, "rb", cache_type="none"), "r") for path in paths}
33
 
 
34
  def get_scalar_fields(path: str) -> list[str]:
35
  # TODO: support t1_fields (vector) and t2_fields (tensor)
36
  return list(files[path]["t0_fields"].keys())
37
 
 
38
  def get_trajectories(path: str, field: str) -> list[int]:
39
  # The first dimension is the trajectory (8 to 256)
40
  return list(range(len(files[path]["t0_fields"][field])))
41
 
42
- @lru_cache(maxsize=4)
43
- def get_images(path: str, scalar_field: str, trajectory: int) -> list[Image.Image]:
44
- # The data is of shape (n_trajectories, n_time_steps, x, y)
45
- out = files[path]["t0_fields"][scalar_field][trajectory]
46
- out = np.log(out) # not sure why
47
- out = (out - out.min()) / (out.max() - out.min())
48
- out = np.uint8(cm.RdBu_r(out) * 255)
49
- return [Image.fromarray(img) for img in out]
50
 
51
  fps = 25
52
- # @lru_cache(maxsize=4)
53
- def get_video(path: str, scalar_field: str, trajectory: int) -> str:
54
- video_filename = 'output_vid.webm'
55
 
 
 
 
 
56
  out = files[path]["t0_fields"][scalar_field][trajectory]
57
- out = np.log(out) # not sure why
58
  out = (out - out.min()) / (out.max() - out.min())
59
- out = np.uint8(cm.RdBu_r(out) * 255)
60
-
61
- output = av.open(video_filename, 'w')
62
- stream = output.add_stream('libvpx-vp9', str(fps))
63
- width, height = out[0].shape[1], out[0].shape[0]
64
  stream.width = width
65
  stream.height = height
66
- stream.pix_fmt = 'yuv444p' # or yuva420p
67
- # stream.options = {'crf': '17'}
68
 
69
  for img in out:
70
- image = Image.fromarray(img)
71
- frame = av.VideoFrame.from_image(image)
72
- packet = stream.encode(frame)
73
- output.mux(packet)
 
 
 
74
 
75
  # Flush the encoder and close the "in memory" file:
76
  packet = stream.encode(None)
77
  output.mux(packet)
78
  output.close()
79
- return video_filename
80
-
81
- # subprocess.run(["ffmpeg", "-y", "-framerate", "25", "-i", os.path.join(output_dir, "density_%d.png"), "-c:v", "libvpx-vp9", "-pix_fmt", "yuva420p", os.path.join(output_dir, "density.webm")])
82
 
83
 
84
- default_scalar_fields = get_scalar_fields(paths[0])
85
- default_trajectories = get_trajectories(paths[0], default_scalar_fields[0])
86
- default_images = get_images(paths[0], default_scalar_fields[0], default_trajectories[0])
87
- default_video = get_video(paths[0], default_scalar_fields[0], default_trajectories[0])
 
 
 
 
 
 
88
 
89
  with gr.Blocks() as demo:
90
- gr.Markdown(f"# 💠 HDF5 Viewer for the [{repo_id}](https://huggingface.co/datasets/{repo_id}) Dataset 🌊")
 
 
 
 
 
 
 
 
91
  gr.Markdown(f"Showing files at `{set_path}`")
92
  with gr.Row():
93
- files_dropdown = gr.Dropdown(choices=paths, value=paths[0], label="File", scale=4)
94
- scalar_fields_dropdown = gr.Dropdown(choices=default_scalar_fields, value=default_scalar_fields[0], label="Physical field")
95
- trajectory_dropdown = gr.Dropdown(choices=default_trajectories, value=default_trajectories[0], label="Trajectory")
96
- gallery = gr.Gallery(default_images, preview=False, selected_index=len(default_images) // 2)
97
- gr.Markdown("_Tip: click on the image to go forward or backwards_")
98
- video = gr.Video(default_video)
99
-
100
- @files_dropdown.select(inputs=[files_dropdown], outputs=[scalar_fields_dropdown, trajectory_dropdown, gallery, video])
 
 
 
 
 
 
 
 
 
 
 
101
  def _update_file(path: str):
102
  scalar_fields = get_scalar_fields(path)
103
  trajectories = get_trajectories(path, scalar_fields[0])
104
- images = get_images(path, scalar_fields[0], trajectories[0])
105
  vid = get_video(path, scalar_fields[0], trajectories[0])
106
  yield {
107
- scalar_fields_dropdown: gr.Dropdown(choices=scalar_fields, value=scalar_fields[0]),
108
- trajectory_dropdown: gr.Dropdown(choices=trajectories, value=trajectories[0]),
109
- gallery: gr.Gallery(images),
110
- video: gr.Video(vid)
 
 
 
111
  }
112
- yield {gallery: gr.Gallery(selected_index=len(default_images) // 2)}
113
-
114
- @scalar_fields_dropdown.select(inputs=[files_dropdown, scalar_fields_dropdown], outputs=[trajectory_dropdown, gallery, video])
 
 
115
  def _update_scalar_field(path: str, scalar_field: str):
116
  trajectories = get_trajectories(path, scalar_field)
117
- images = get_images(path, scalar_field, trajectories[0])
118
  vid = get_video(path, scalar_field, trajectories[0])
119
  yield {
120
- trajectory_dropdown: gr.Dropdown(choices=trajectories, value=trajectories[0]),
121
- gallery: gr.Gallery(images),
122
- video: gr.Video(vid)
 
123
  }
124
- yield {gallery: gr.Gallery(selected_index=len(default_images) // 2)}
125
 
126
- @trajectory_dropdown.select(inputs=[files_dropdown, scalar_fields_dropdown, trajectory_dropdown], outputs=[gallery, video])
 
 
 
127
  def _update_trajectory(path: str, scalar_field: str, trajectory: int):
128
- images = get_images(path, scalar_field, trajectory)
129
  vid = get_video(path, scalar_field, trajectory)
130
- yield {gallery: gr.Gallery(images), video: gr.Video(vid)}
131
- yield {gallery: gr.Gallery(selected_index=len(default_images) // 2)}
132
 
133
- demo.launch()
 
13
  # - x: 128 to 512
14
  # - y: 128 to 512
15
  # - physical fields: 2 to 8 (density, pressure, energy, velocity…)
 
 
16
  import gradio as gr
17
  import h5py
18
  import numpy as np
 
20
  from matplotlib import cm
21
  from PIL import Image
22
  import av
23
+ from tempfile import gettempdir
24
+ import os
25
+
26
+ # Get the path of the system's temporary directory
27
+ temp_directory = gettempdir()
28
+ print(f"System's temporary directory is: {temp_directory}")
29
+ videos_temp_directory = os.path.join(temp_directory, "videos")
30
+ print(f"Videos are saved (and never deleted) in: {videos_temp_directory}")
31
 
32
+ # TODO: add colormap input
33
 
34
  repo_id = "lhoestq/turbulent_radiative_layer_tcool_demo"
35
  set_path = f"hf://datasets/{repo_id}/**/*.hdf5"
 
37
  paths = fs.glob(set_path)
38
  files = {path: h5py.File(fs.open(path, "rb", cache_type="none"), "r") for path in paths}
39
 
40
+
41
  def get_scalar_fields(path: str) -> list[str]:
42
  # TODO: support t1_fields (vector) and t2_fields (tensor)
43
  return list(files[path]["t0_fields"].keys())
44
 
45
+
46
  def get_trajectories(path: str, field: str) -> list[int]:
47
  # The first dimension is the trajectory (8 to 256)
48
  return list(range(len(files[path]["t0_fields"][field])))
49
 
 
 
 
 
 
 
 
 
50
 
51
  fps = 25
 
 
 
52
 
53
+
54
+ def create_video(
55
+ path: str, scalar_field: str, trajectory: int, video_filename: str
56
+ ) -> None:
57
  out = files[path]["t0_fields"][scalar_field][trajectory]
58
+ # out = np.log(out) # not sure why
59
  out = (out - out.min()) / (out.max() - out.min())
60
+ out = np.uint8(cm.viridis(out) * 255)
61
+
62
+ output = av.open(video_filename, "w")
63
+ stream = output.add_stream("libvpx-vp9", str(fps))
64
+ height, width = out[0].shape[1], out[0].shape[0]
65
  stream.width = width
66
  stream.height = height
67
+ stream.pix_fmt = "yuv444p"
 
68
 
69
  for img in out:
70
+ image = Image.fromarray(img)
71
+ # I think it's the way to get the expected orientation
72
+ image = image.transpose(method=Image.Transpose.TRANSPOSE)
73
+ image = image.transpose(method=Image.Transpose.FLIP_TOP_BOTTOM)
74
+ frame = av.VideoFrame.from_image(image)
75
+ packet = stream.encode(frame)
76
+ output.mux(packet)
77
 
78
  # Flush the encoder and close the "in memory" file:
79
  packet = stream.encode(None)
80
  output.mux(packet)
81
  output.close()
 
 
 
82
 
83
 
84
+ # no limit on the size of the videos on the disk
85
+ def get_video(path: str, scalar_field: str, trajectory: int) -> str:
86
+ video_filename = os.path.join(
87
+ videos_temp_directory, *path.split("/"), scalar_field, f"{trajectory}.webm"
88
+ )
89
+ os.makedirs(os.path.dirname(video_filename), exist_ok=True)
90
+ if not os.path.isfile(video_filename):
91
+ create_video(path, scalar_field, trajectory, video_filename)
92
+ return video_filename
93
+
94
 
95
  with gr.Blocks() as demo:
96
+ default_scalar_fields = get_scalar_fields(paths[0])
97
+ default_trajectories = get_trajectories(paths[0], default_scalar_fields[0])
98
+ default_video = get_video(
99
+ paths[0], default_scalar_fields[0], default_trajectories[0]
100
+ )
101
+
102
+ gr.Markdown(
103
+ f"# 💠 HDF5 Viewer for the [{repo_id}](https://huggingface.co/datasets/{repo_id}) Dataset 🌊"
104
+ )
105
  gr.Markdown(f"Showing files at `{set_path}`")
106
  with gr.Row():
107
+ files_dropdown = gr.Dropdown(
108
+ choices=paths, value=paths[0], label="File", scale=4
109
+ )
110
+ scalar_fields_dropdown = gr.Dropdown(
111
+ choices=default_scalar_fields,
112
+ value=default_scalar_fields[0],
113
+ label="Physical field",
114
+ )
115
+ trajectory_dropdown = gr.Dropdown(
116
+ choices=default_trajectories,
117
+ value=default_trajectories[0],
118
+ label="Trajectory",
119
+ )
120
+ video = gr.Video(default_video, height=400)
121
+
122
+ @files_dropdown.select(
123
+ inputs=[files_dropdown],
124
+ outputs=[scalar_fields_dropdown, trajectory_dropdown, video],
125
+ )
126
  def _update_file(path: str):
127
  scalar_fields = get_scalar_fields(path)
128
  trajectories = get_trajectories(path, scalar_fields[0])
 
129
  vid = get_video(path, scalar_fields[0], trajectories[0])
130
  yield {
131
+ scalar_fields_dropdown: gr.Dropdown(
132
+ choices=scalar_fields, value=scalar_fields[0]
133
+ ),
134
+ trajectory_dropdown: gr.Dropdown(
135
+ choices=trajectories, value=trajectories[0]
136
+ ),
137
+ video: gr.Video(vid),
138
  }
139
+
140
+ @scalar_fields_dropdown.select(
141
+ inputs=[files_dropdown, scalar_fields_dropdown],
142
+ outputs=[trajectory_dropdown, video],
143
+ )
144
  def _update_scalar_field(path: str, scalar_field: str):
145
  trajectories = get_trajectories(path, scalar_field)
 
146
  vid = get_video(path, scalar_field, trajectories[0])
147
  yield {
148
+ trajectory_dropdown: gr.Dropdown(
149
+ choices=trajectories, value=trajectories[0]
150
+ ),
151
+ video: gr.Video(vid),
152
  }
 
153
 
154
+ @trajectory_dropdown.select(
155
+ inputs=[files_dropdown, scalar_fields_dropdown, trajectory_dropdown],
156
+ outputs=[video],
157
+ )
158
  def _update_trajectory(path: str, scalar_field: str, trajectory: int):
 
159
  vid = get_video(path, scalar_field, trajectory)
160
+ yield {video: gr.Video(vid)}
161
+
162
 
163
+ demo.launch()