Spaces:
Sleeping
Sleeping
adds depth, anatomy segmentation viewer
Browse files- gradio_app.py +112 -93
gradio_app.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from functools import partial
|
| 2 |
import gradio as gr
|
| 3 |
-
|
| 4 |
from PIL import Image
|
| 5 |
import numpy as np
|
| 6 |
import gradio as gr
|
|
@@ -129,6 +129,59 @@ background_ds = Background2d(
|
|
| 129 |
image_filenames=None,
|
| 130 |
)
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
def prepare_ds_renderer(
|
| 134 |
randomize,
|
|
@@ -168,30 +221,30 @@ def prepare_ds_renderer(
|
|
| 168 |
config=None,
|
| 169 |
is_blended=is_blend,
|
| 170 |
)
|
| 171 |
-
blended3d = Blended3d(
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
)
|
| 182 |
-
normal_texture = load_texture_map(
|
| 183 |
-
|
| 184 |
-
).maps_padded()
|
| 185 |
-
if num_lesion > 0:
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
|
| 196 |
# texture_lesion_mask = blended3d.lesion_texture_mask(astensor=True).to(device)
|
| 197 |
# non_skin_texture_mask = blended3d.nonskin_texture_mask(astensor=True).to(device)
|
|
@@ -210,16 +263,8 @@ def prepare_ds_renderer(
|
|
| 210 |
mat_sh,
|
| 211 |
mat_sc,
|
| 212 |
)
|
| 213 |
-
# mesh_renderer.mesh = mesh
|
| 214 |
-
# mesh_renderer.cameras = cameras
|
| 215 |
-
# mesh_renderer.lights = lights
|
| 216 |
-
# mesh_renderer.materials = materials
|
| 217 |
-
# mesh_renderer.renderer = renderer
|
| 218 |
gr.Info("Successfully prepared renderer.")
|
| 219 |
-
# render normal images
|
| 220 |
gr.Info("Rendering Images...")
|
| 221 |
-
# if num_views > 1:
|
| 222 |
-
# mesh_renderer.mesh = mesh.extend(num_views)
|
| 223 |
gr.Info(f"Rendering {num_views} views on {DEVICE}. Please wait...")
|
| 224 |
img_count = 0
|
| 225 |
view2d = []
|
|
@@ -227,10 +272,11 @@ def prepare_ds_renderer(
|
|
| 227 |
anatomy2d = []
|
| 228 |
seg2d = []
|
| 229 |
view_size = (224, 224)
|
|
|
|
| 230 |
while img_count < num_views:
|
| 231 |
if randomize:
|
| 232 |
gr.Info("Finding suitable parameters...")
|
| 233 |
-
success = gen2d.randomize_parameters(config=None)
|
| 234 |
if not success:
|
| 235 |
gr.Info("Could not find suitable parameters. Trying again.")
|
| 236 |
continue
|
|
@@ -238,9 +284,7 @@ def prepare_ds_renderer(
|
|
| 238 |
raster_settings = RasterizationSettings(
|
| 239 |
image_size=view_size[0],
|
| 240 |
blur_radius=0.0,
|
| 241 |
-
faces_per_pixel=
|
| 242 |
-
# max_faces_per_bin=100,
|
| 243 |
-
# bin_size=0,
|
| 244 |
perspective_correct=True,
|
| 245 |
)
|
| 246 |
gen2d.mesh_renderer.cameras = cameras
|
|
@@ -259,60 +303,19 @@ def prepare_ds_renderer(
|
|
| 259 |
print("***Not enough skin or unable to paste lesion. Skipping.")
|
| 260 |
continue
|
| 261 |
paste_img = (paste_img * 255).astype(np.uint8)
|
|
|
|
| 262 |
depth_view = target[:, :, 4]
|
| 263 |
-
depth_img = (depth_view
|
| 264 |
-
depth_view.max() - depth_view.min()
|
| 265 |
-
)
|
| 266 |
-
depth_img = (depth_img * 255).astype(np.uint8)
|
| 267 |
view2d.append(paste_img)
|
| 268 |
depth2d.append(depth_img)
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
| 271 |
gr.Info(f"Successfully rendered {img_count+1}/{num_views} image+annotations.")
|
| 272 |
img_count += 1
|
| 273 |
return view2d, depth2d, anatomy2d, seg2d
|
| 274 |
|
| 275 |
-
# mesh_renderer.compute_fragments()
|
| 276 |
-
# view2d = mesh_renderer.render_view(asnumpy=True, asRGB=True)
|
| 277 |
-
# gr.Info("Successfully rendered images.")
|
| 278 |
-
# gr.Info("Preparing annotations...")
|
| 279 |
-
# # breakpoint()
|
| 280 |
-
# pix2face = torch.from_numpy(mesh_renderer.pixels_to_face()).to(
|
| 281 |
-
# mesh_renderer.mesh.device
|
| 282 |
-
# )
|
| 283 |
-
# pix2vert = torch.stack(
|
| 284 |
-
# [a[i] for a, i in zip(mesh_renderer.mesh.faces_padded().squeeze(), pix2face)]
|
| 285 |
-
# )
|
| 286 |
-
# pix2vert = pix2vert.detach().cpu().numpy()
|
| 287 |
-
# anatomy_image = [
|
| 288 |
-
# vertices_to_anatomy[pix2vert[i]] * mesh_renderer.body_mask()
|
| 289 |
-
# for i in range(num_views)
|
| 290 |
-
# ]
|
| 291 |
-
# anatomy_image = np.stack(anatomy_image)
|
| 292 |
-
|
| 293 |
-
# anatomy_image = mesh_renderer.anatomy_image(vertices_to_anatomy)
|
| 294 |
-
# depth_img = mesh_renderer.depth_view(asnumpy=True)
|
| 295 |
-
# mesh_renderer.set_texture_image(texture_lesion_mask[:, :, np.newaxis])
|
| 296 |
-
# mask2d = mesh_renderer.render_view(asnumpy=True, asRGB=True)
|
| 297 |
-
# lesion_mask = mesh_renderer.lesion_mask(mask2d[:, :, 0], lesion_mask_id=None)
|
| 298 |
-
# # skin mask
|
| 299 |
-
# mesh_renderer.set_texture_image(non_skin_texture_mask)
|
| 300 |
-
# nonskin_mask = mesh_renderer.render_view(asnumpy=True, asRGB=True)
|
| 301 |
-
# skin_mask = mesh_renderer.skin_mask(nonskin_mask[:, :, 0] > 0.5)
|
| 302 |
-
# segmentation_mask = make_masks(lesion_mask, skin_mask)
|
| 303 |
-
# gr.Info("Successfully prepared annotations.")
|
| 304 |
-
# print(view2d.shape, anatomy_image.shape, depth_img.shape, segmentation_mask.shape)
|
| 305 |
-
# convert anatomy image with labels for each pixel to an image with RGB values
|
| 306 |
-
# map labels to pixels
|
| 307 |
-
|
| 308 |
-
# return (
|
| 309 |
-
# view2d,
|
| 310 |
-
# anatomy_image,
|
| 311 |
-
# depth_img,
|
| 312 |
-
# skin_mask,
|
| 313 |
-
# ) # segmentation_mask
|
| 314 |
-
|
| 315 |
-
|
| 316 |
# define the list of all the examples
|
| 317 |
def get_examples():
|
| 318 |
# setup_paths()
|
|
@@ -392,7 +395,7 @@ def plotly_mesh(verts, faces, vc, mesh_name):
|
|
| 392 |
)
|
| 393 |
]
|
| 394 |
)
|
| 395 |
-
fig.update_layout(scene_aspectmode="manual", scene_aspectratio=dict(x=1, y=1, z=1))
|
| 396 |
fig.update_layout(scene=dict(xaxis=dict(visible=False), yaxis=dict(visible=False)))
|
| 397 |
fig.update_layout(scene=dict(zaxis=dict(visible=False)))
|
| 398 |
fig.update_layout(scene=dict(camera=dict(up=dict(x=1, y=0, z=1))))
|
|
@@ -624,7 +627,6 @@ def process_examples(mesh_name, tex_name, n_lesion):
|
|
| 624 |
mesh_path = get_mesh_path(mesh_name)
|
| 625 |
texture_path = get_texture_module(tex_name)(mesh_name, n_lesion)
|
| 626 |
mesh_to_view = plotly_mesh(*get_trimesh_attrs(mesh_name, tex_name, n_lesion))
|
| 627 |
-
# mesh = load_mesh_and_texture(mesh_name, tex_name, n_lesion)
|
| 628 |
return mesh_to_view, texture_path, n_lesion
|
| 629 |
|
| 630 |
|
|
@@ -640,7 +642,7 @@ def update_plots(mesh_name, texture_name, num_lesion):
|
|
| 640 |
)
|
| 641 |
return default_mesh_plot, default_texture, num_lesion
|
| 642 |
mesh_path = get_mesh_path(mesh_name)
|
| 643 |
-
texture_path = get_texture_module(texture_name)(mesh_name, num_lesion)
|
| 644 |
mesh_to_view = plotly_mesh(*get_trimesh_attrs(mesh_name, texture_name, num_lesion))
|
| 645 |
gr.Info("Successfully updated mesh and texture.")
|
| 646 |
return mesh_to_view, texture_path, num_lesion
|
|
@@ -852,9 +854,26 @@ def run_demo():
|
|
| 852 |
)
|
| 853 |
# rendered views panel
|
| 854 |
with gr.Row(variant="panel"):
|
| 855 |
-
|
| 856 |
-
|
| 857 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 858 |
|
| 859 |
@gr.on(
|
| 860 |
triggers=[render_button.click],
|
|
@@ -874,7 +893,7 @@ def run_demo():
|
|
| 874 |
mat_sh,
|
| 875 |
mat_sc,
|
| 876 |
],
|
| 877 |
-
outputs=[render_block],
|
| 878 |
)
|
| 879 |
def render_views(
|
| 880 |
randomize,
|
|
@@ -912,7 +931,7 @@ def run_demo():
|
|
| 912 |
# renderer, mesh, lights, cameras, materials, num_views
|
| 913 |
# )
|
| 914 |
# return [_ for _ in images.detach().cpu().numpy()]
|
| 915 |
-
view2d,
|
| 916 |
randomize,
|
| 917 |
select_mesh,
|
| 918 |
select_texture,
|
|
@@ -928,7 +947,7 @@ def run_demo():
|
|
| 928 |
mat_sh,
|
| 929 |
mat_sc,
|
| 930 |
)
|
| 931 |
-
return view2d
|
| 932 |
|
| 933 |
# examples panel when the iuser does not want to input
|
| 934 |
with gr.Row(variant="panel"):
|
|
|
|
| 1 |
from functools import partial
|
| 2 |
import gradio as gr
|
| 3 |
+
import pdb
|
| 4 |
from PIL import Image
|
| 5 |
import numpy as np
|
| 6 |
import gradio as gr
|
|
|
|
| 129 |
image_filenames=None,
|
| 130 |
)
|
| 131 |
|
| 132 |
+
from dermsynth3d.utils.anatomy import SimpleAnatomy
|
| 133 |
+
color_labels = {
|
| 134 |
+
0: (0., 0., 0.), # background
|
| 135 |
+
1: (174., 199., 232.), # head
|
| 136 |
+
2: (152., 223., 138.), # torso
|
| 137 |
+
3: (31., 119., 180.), # hips
|
| 138 |
+
4: (255., 187., 120.), # legs
|
| 139 |
+
5: (188., 189., 34.), # feet
|
| 140 |
+
6: (140., 86., 75.), # arms
|
| 141 |
+
7: (255., 152., 150.), # hands
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def to_simple_anatomy(anatomy):
|
| 146 |
+
for i in range(16+1):
|
| 147 |
+
if i in [0,1]:
|
| 148 |
+
continue
|
| 149 |
+
if i in [2,3]:
|
| 150 |
+
anatomy[anatomy==i] = 2
|
| 151 |
+
if i == 4:
|
| 152 |
+
anatomy[anatomy==i] = 3
|
| 153 |
+
if i in [5,6,7,8]:
|
| 154 |
+
anatomy[anatomy==i] = 4
|
| 155 |
+
if i in [9,10]:
|
| 156 |
+
anatomy[anatomy==i] = 5
|
| 157 |
+
if i in [11,12,13,14]:
|
| 158 |
+
anatomy[anatomy==i] = 6
|
| 159 |
+
if i in [15,16]:
|
| 160 |
+
anatomy[anatomy==i] = 7
|
| 161 |
+
return anatomy
|
| 162 |
+
|
| 163 |
+
def convert_anatomy_to_rgb(anatomy):
|
| 164 |
+
anatomy = to_simple_anatomy(anatomy)
|
| 165 |
+
anatomy_rgb = np.zeros((anatomy.shape[0], anatomy.shape[1], 3))
|
| 166 |
+
for k, v in color_labels.items():
|
| 167 |
+
anatomy_rgb[anatomy == k] = v
|
| 168 |
+
return anatomy_rgb.astype(np.uint8)
|
| 169 |
+
|
| 170 |
+
import PIL.Image as pil
|
| 171 |
+
import numpy as np
|
| 172 |
+
import matplotlib as mpl
|
| 173 |
+
import matplotlib.cm as cm
|
| 174 |
+
def convert_depth_to_rgb(depth):
|
| 175 |
+
mask = depth != 0
|
| 176 |
+
disp_map = 1 / depth
|
| 177 |
+
vmax = np.percentile(disp_map[mask], 95)
|
| 178 |
+
vmin = np.percentile(disp_map[mask], 5)
|
| 179 |
+
normalizer = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
|
| 180 |
+
mapper = cm.ScalarMappable(norm=normalizer, cmap='magma')
|
| 181 |
+
mask = np.repeat(np.expand_dims(mask,-1), 3, -1)
|
| 182 |
+
colormapped_im = (mapper.to_rgba(disp_map)[:, :, :3] * 255).astype(np.uint8)
|
| 183 |
+
colormapped_im[~mask] = 255
|
| 184 |
+
return colormapped_im
|
| 185 |
|
| 186 |
def prepare_ds_renderer(
|
| 187 |
randomize,
|
|
|
|
| 221 |
config=None,
|
| 222 |
is_blended=is_blend,
|
| 223 |
)
|
| 224 |
+
# blended3d = Blended3d(
|
| 225 |
+
# mesh_filename=os.path.join(
|
| 226 |
+
# "./DermSynth3D/data/3dbodytex-1.1-highres/",
|
| 227 |
+
# mesh_name,
|
| 228 |
+
# "model_highres_0_normalized.obj",
|
| 229 |
+
# ),
|
| 230 |
+
# device=DEVICE,
|
| 231 |
+
# dir_blended_textures=dir_blended_textures,
|
| 232 |
+
# dir_anatomy=dir_anatomy,
|
| 233 |
+
# extension=extension ,
|
| 234 |
+
# )
|
| 235 |
+
# normal_texture = load_texture_map(
|
| 236 |
+
# mesh, mesh_name, "No Lesion", 0, device
|
| 237 |
+
# ).maps_padded()
|
| 238 |
+
# if num_lesion > 0:
|
| 239 |
+
# blended_texture_image = load_texture_map(
|
| 240 |
+
# mesh, mesh_name, "Blended Lesion", num_lesion, device
|
| 241 |
+
# ).maps_padded()
|
| 242 |
+
# pasted_texture_image = load_texture_map(
|
| 243 |
+
# mesh, mesh_name, "Pasted Lesion", num_lesion, device
|
| 244 |
+
# ).maps_padded()
|
| 245 |
+
# dilated_texture_image = load_texture_map(
|
| 246 |
+
# mesh, mesh_name, "Dilated Lesion", num_lesion, device
|
| 247 |
+
# ).maps_padded()
|
| 248 |
|
| 249 |
# texture_lesion_mask = blended3d.lesion_texture_mask(astensor=True).to(device)
|
| 250 |
# non_skin_texture_mask = blended3d.nonskin_texture_mask(astensor=True).to(device)
|
|
|
|
| 263 |
mat_sh,
|
| 264 |
mat_sc,
|
| 265 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
gr.Info("Successfully prepared renderer.")
|
|
|
|
| 267 |
gr.Info("Rendering Images...")
|
|
|
|
|
|
|
| 268 |
gr.Info(f"Rendering {num_views} views on {DEVICE}. Please wait...")
|
| 269 |
img_count = 0
|
| 270 |
view2d = []
|
|
|
|
| 272 |
anatomy2d = []
|
| 273 |
seg2d = []
|
| 274 |
view_size = (224, 224)
|
| 275 |
+
gen2d.view_size = view_size
|
| 276 |
while img_count < num_views:
|
| 277 |
if randomize:
|
| 278 |
gr.Info("Finding suitable parameters...")
|
| 279 |
+
success = gen2d.randomize_parameters(config=None, view_size=view_size)
|
| 280 |
if not success:
|
| 281 |
gr.Info("Could not find suitable parameters. Trying again.")
|
| 282 |
continue
|
|
|
|
| 284 |
raster_settings = RasterizationSettings(
|
| 285 |
image_size=view_size[0],
|
| 286 |
blur_radius=0.0,
|
| 287 |
+
faces_per_pixel=10,
|
|
|
|
|
|
|
| 288 |
perspective_correct=True,
|
| 289 |
)
|
| 290 |
gen2d.mesh_renderer.cameras = cameras
|
|
|
|
| 303 |
print("***Not enough skin or unable to paste lesion. Skipping.")
|
| 304 |
continue
|
| 305 |
paste_img = (paste_img * 255).astype(np.uint8)
|
| 306 |
+
anatomy_view = target[:, :, 3]
|
| 307 |
depth_view = target[:, :, 4]
|
| 308 |
+
depth_img = convert_depth_to_rgb(depth_view)
|
|
|
|
|
|
|
|
|
|
| 309 |
view2d.append(paste_img)
|
| 310 |
depth2d.append(depth_img)
|
| 311 |
+
anatomy_img = convert_anatomy_to_rgb(anatomy_view)
|
| 312 |
+
anatomy2d.append(anatomy_img)
|
| 313 |
+
mask = target[:, :, 0]
|
| 314 |
+
seg2d.append(mask)
|
| 315 |
gr.Info(f"Successfully rendered {img_count+1}/{num_views} image+annotations.")
|
| 316 |
img_count += 1
|
| 317 |
return view2d, depth2d, anatomy2d, seg2d
|
| 318 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 319 |
# define the list of all the examples
|
| 320 |
def get_examples():
|
| 321 |
# setup_paths()
|
|
|
|
| 395 |
)
|
| 396 |
]
|
| 397 |
)
|
| 398 |
+
# fig.update_layout(scene_aspectmode="manual", scene_aspectratio=dict(x=1, y=1, z=1))
|
| 399 |
fig.update_layout(scene=dict(xaxis=dict(visible=False), yaxis=dict(visible=False)))
|
| 400 |
fig.update_layout(scene=dict(zaxis=dict(visible=False)))
|
| 401 |
fig.update_layout(scene=dict(camera=dict(up=dict(x=1, y=0, z=1))))
|
|
|
|
| 627 |
mesh_path = get_mesh_path(mesh_name)
|
| 628 |
texture_path = get_texture_module(tex_name)(mesh_name, n_lesion)
|
| 629 |
mesh_to_view = plotly_mesh(*get_trimesh_attrs(mesh_name, tex_name, n_lesion))
|
|
|
|
| 630 |
return mesh_to_view, texture_path, n_lesion
|
| 631 |
|
| 632 |
|
|
|
|
| 642 |
)
|
| 643 |
return default_mesh_plot, default_texture, num_lesion
|
| 644 |
mesh_path = get_mesh_path(mesh_name)
|
| 645 |
+
texture_path = Image.open(get_texture_module(texture_name)(mesh_name, num_lesion)).convert("RGB").resize((512, 512))
|
| 646 |
mesh_to_view = plotly_mesh(*get_trimesh_attrs(mesh_name, texture_name, num_lesion))
|
| 647 |
gr.Info("Successfully updated mesh and texture.")
|
| 648 |
return mesh_to_view, texture_path, num_lesion
|
|
|
|
| 854 |
)
|
| 855 |
# rendered views panel
|
| 856 |
with gr.Row(variant="panel"):
|
| 857 |
+
with gr.Tab("Rendered RGB Views"):
|
| 858 |
+
render_block = gr.Gallery(
|
| 859 |
+
label="Rendered Views", columns=4, height="auto", object_fit="contain"
|
| 860 |
+
)
|
| 861 |
+
with gr.Tab("Rendered Depth Views"):
|
| 862 |
+
depth_block = gr.Gallery(
|
| 863 |
+
label="Depth Maps", columns=4, height="auto", object_fit="contain"
|
| 864 |
+
)
|
| 865 |
+
with gr.Tab("Rendered Anatomy Views"):
|
| 866 |
+
anatomy_block = gr.Gallery(
|
| 867 |
+
label="Anatomy Labels", columns=4, height="auto", object_fit="contain"
|
| 868 |
+
)
|
| 869 |
+
with gr.Tab("Rendered Segmentation Views"):
|
| 870 |
+
seg_block = gr.Gallery(
|
| 871 |
+
label="Segmentation Masks", columns=4, height="auto", object_fit="contain"
|
| 872 |
+
)
|
| 873 |
+
#
|
| 874 |
+
# render_block = gr.Gallery(
|
| 875 |
+
# label="Rendered Views", columns=4, height="auto", object_fit="contain"
|
| 876 |
+
# )
|
| 877 |
|
| 878 |
@gr.on(
|
| 879 |
triggers=[render_button.click],
|
|
|
|
| 893 |
mat_sh,
|
| 894 |
mat_sc,
|
| 895 |
],
|
| 896 |
+
outputs=[render_block, depth_block, anatomy_block, seg_block],
|
| 897 |
)
|
| 898 |
def render_views(
|
| 899 |
randomize,
|
|
|
|
| 931 |
# renderer, mesh, lights, cameras, materials, num_views
|
| 932 |
# )
|
| 933 |
# return [_ for _ in images.detach().cpu().numpy()]
|
| 934 |
+
view2d, depth, anatomy, segmentation = prepare_ds_renderer(
|
| 935 |
randomize,
|
| 936 |
select_mesh,
|
| 937 |
select_texture,
|
|
|
|
| 947 |
mat_sh,
|
| 948 |
mat_sc,
|
| 949 |
)
|
| 950 |
+
return view2d, depth, anatomy, segmentation
|
| 951 |
|
| 952 |
# examples panel when the iuser does not want to input
|
| 953 |
with gr.Row(variant="panel"):
|