awacke1 commited on
Commit
76076f1
1 Parent(s): e168c81

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +71 -243
app.py CHANGED
@@ -1,11 +1,9 @@
1
  import os
2
  import random
3
- import uuid
4
  import base64
5
  import gradio as gr
6
  import numpy as np
7
  from PIL import Image
8
- import spaces
9
  import torch
10
  import glob
11
  from datetime import datetime
@@ -13,44 +11,39 @@ import pandas as pd
13
  import json
14
  import re
15
  import logging
 
16
 
17
  # Set up logging
18
  logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger(__name__)
20
 
21
- from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
22
-
23
  DESCRIPTION = """# 🎨 ArtForge: Community AI Gallery
 
24
 
25
- Create, curate, and compete with AI-generated art. Join our creative multiplayer experience! 🖼️🏆✨
26
- """
27
 
28
  # Global variables
29
  image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
30
- LIKES_CACHE_FILE = "likes_cache.json"
31
 
32
- def load_likes_cache():
33
- if os.path.exists(LIKES_CACHE_FILE):
34
- with open(LIKES_CACHE_FILE, 'r') as f:
35
- return json.load(f)
36
- return {}
37
-
38
- def save_likes_cache(cache):
39
- with open(LIKES_CACHE_FILE, 'w') as f:
40
- json.dump(cache, f)
41
 
42
- likes_cache = load_likes_cache()
 
 
43
 
44
- def create_download_link(filename):
45
- with open(filename, "rb") as file:
46
- encoded_string = base64.b64encode(file.read()).decode('utf-8')
47
- download_link = f'<a href="data:image/png;base64,{encoded_string}" download="{filename}">Download Image</a>'
48
- return download_link
49
 
50
  def save_image(img, prompt):
51
- global image_metadata, likes_cache
52
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
53
- safe_prompt = re.sub(r'[^\w\s-]', '', prompt.lower())[:50] # Limit to 50 characters
54
  safe_prompt = re.sub(r'[-\s]+', '-', safe_prompt).strip('-')
55
  filename = f"{timestamp}_{safe_prompt}.png"
56
  img.save(filename)
@@ -60,84 +53,50 @@ def save_image(img, prompt):
60
  'Likes': [0],
61
  'Dislikes': [0],
62
  'Hearts': [0],
63
- 'Created': [datetime.now()]
64
  })
65
  image_metadata = pd.concat([image_metadata, new_row], ignore_index=True, sort=False)
66
- likes_cache[filename] = {'likes': 0, 'dislikes': 0, 'hearts': 0}
67
- save_likes_cache(likes_cache)
68
  logger.info(f"Saved new image: {filename}")
69
  return filename
70
 
71
- def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
72
- if randomize_seed:
73
- seed = random.randint(0, MAX_SEED)
74
- return seed
75
-
76
  def get_image_gallery():
77
- global image_metadata
78
- image_files = image_metadata['Filename'].tolist()
79
- return [(file, get_image_caption(file)) for file in image_files if os.path.exists(file)]
80
 
81
  def get_image_caption(filename):
82
- global likes_cache, image_metadata
83
- if filename in likes_cache:
84
- likes = likes_cache[filename]['likes']
85
- dislikes = likes_cache[filename]['dislikes']
86
- hearts = likes_cache[filename]['hearts']
87
- prompt = image_metadata[image_metadata['Filename'] == filename]['Prompt'].values[0]
88
- return f"{filename}\nPrompt: {prompt}\n👍 {likes} 👎 {dislikes} ❤️ {hearts}"
89
  return filename
90
 
91
  def delete_all_images():
92
- global image_metadata, likes_cache
93
  for file in image_metadata['Filename']:
94
  if os.path.exists(file):
95
  os.remove(file)
96
  image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
97
- likes_cache = {}
98
- save_likes_cache(likes_cache)
99
  logger.info("All images deleted")
100
  return get_image_gallery(), image_metadata.values.tolist()
101
 
102
  def delete_image(filename):
103
- global image_metadata, likes_cache
104
  if filename and os.path.exists(filename):
105
  os.remove(filename)
106
  image_metadata = image_metadata[image_metadata['Filename'] != filename]
107
- if filename in likes_cache:
108
- del likes_cache[filename]
109
- save_likes_cache(likes_cache)
110
  logger.info(f"Deleted image: {filename}")
111
  return get_image_gallery(), image_metadata.values.tolist()
112
 
113
  def vote(filename, vote_type):
114
- global likes_cache, image_metadata
115
- logger.info(f"Voting on {filename} with {vote_type}")
116
- if filename in likes_cache:
117
- likes_cache[filename][vote_type.lower()] += 1
118
- save_likes_cache(likes_cache)
119
- # Update image_metadata as well
120
- if filename in image_metadata['Filename'].values:
121
- image_metadata.loc[image_metadata['Filename'] == filename, vote_type] += 1
122
- logger.info(f"Updated vote count for {filename}: {likes_cache[filename]}")
123
  else:
124
- logger.warning(f"File {filename} not found in likes_cache")
125
- return get_image_gallery(), image_metadata.values.tolist()
126
-
127
- def get_random_style():
128
- styles = [
129
- "Impressionist", "Cubist", "Surrealist", "Abstract Expressionist",
130
- "Pop Art", "Minimalist", "Baroque", "Art Nouveau", "Pointillist", "Fauvism"
131
- ]
132
- return random.choice(styles)
133
-
134
- MAX_SEED = np.iinfo(np.int32).max
135
-
136
- if not torch.cuda.is_available():
137
- DESCRIPTION += "\n<p>Running on CPU 🥶 This demo may not work on CPU.</p>"
138
-
139
- USE_TORCH_COMPILE = 0
140
- ENABLE_CPU_OFFLOAD = 0
141
 
142
  if torch.cuda.is_available():
143
  pipe = StableDiffusionXLPipeline.from_pretrained(
@@ -146,30 +105,22 @@ if torch.cuda.is_available():
146
  use_safetensors=True,
147
  )
148
  pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
149
-
150
  pipe.load_lora_weights("ehristoforu/dalle-3-xl-v2", weight_name="dalle-3-xl-lora-v2.safetensors", adapter_name="dalle")
151
  pipe.set_adapters("dalle")
152
-
153
  pipe.to("cuda")
 
 
154
 
155
- @spaces.GPU(enable_queue=True)
156
  def generate(
157
- prompt: str,
158
- negative_prompt: str = "",
159
- use_negative_prompt: bool = False,
160
- seed: int = 0,
161
- width: int = 1024,
162
- height: int = 1024,
163
- guidance_scale: float = 3,
164
- randomize_seed: bool = False,
165
- progress=gr.Progress(track_tqdm=True),
166
- **kwargs # Add this line to accept any additional arguments
167
  ):
168
- seed = int(randomize_seed_fn(seed, randomize_seed))
169
-
170
  if not use_negative_prompt:
171
  negative_prompt = ""
172
-
173
  images = pipe(
174
  prompt=prompt,
175
  negative_prompt=negative_prompt,
@@ -182,29 +133,12 @@ def generate(
182
  output_type="pil",
183
  ).images
184
  image_paths = [save_image(img, prompt) for img in images]
185
- download_links = [create_download_link(path) for path in image_paths]
186
-
187
- return image_paths, seed, download_links, get_image_gallery(), image_metadata.values.tolist()
188
-
189
- examples = [
190
- f"{get_random_style()} painting of a majestic lighthouse on a rocky coast. Use bold brushstrokes and a vibrant color palette to capture the interplay of light and shadow as the lighthouse beam cuts through a stormy night sky.",
191
- f"{get_random_style()} still life featuring a pair of vintage eyeglasses. Focus on the intricate details of the frames and lenses, using a warm color scheme to evoke a sense of nostalgia and wisdom.",
192
- f"{get_random_style()} depiction of a rustic wooden stool in a sunlit artist's studio. Emphasize the texture of the wood and the interplay of light and shadow, using a mix of earthy tones and highlights.",
193
- f"{get_random_style()} scene viewed through an ornate window frame. Contrast the intricate details of the window with a dreamy, soft-focus landscape beyond, using a palette that transitions from cool interior tones to warm exterior hues.",
194
- f"{get_random_style()} close-up study of interlaced fingers. Use a monochromatic color scheme to emphasize the form and texture of the hands, with dramatic lighting to create depth and emotion.",
195
- f"{get_random_style()} composition featuring a set of dice in motion. Capture the energy and randomness of the throw, using a dynamic color palette and blurred lines to convey movement.",
196
- f"{get_random_style()} interpretation of heaven. Create an ethereal atmosphere with soft, billowing clouds and radiant light, using a palette of celestial blues, golds, and whites.",
197
- f"{get_random_style()} portrayal of an ancient, mystical gate. Combine architectural details with elements of fantasy, using a rich, jewel-toned palette to create an air of mystery and magic.",
198
- f"{get_random_style()} portrait of a curious cat. Focus on capturing the feline's expressive eyes and sleek form, using a mix of bold and subtle colors to bring out the cat's personality.",
199
- f"{get_random_style()} abstract representation of toes in sand. Use textured brushstrokes to convey the feeling of warm sand, with a palette inspired by a sun-drenched beach."
200
- ]
201
 
202
  css = '''
203
  .gradio-container{max-width: 1024px !important}
204
  h1{text-align:center}
205
- footer {
206
- visibility: hidden
207
- }
208
  '''
209
 
210
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
@@ -212,153 +146,47 @@ with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
212
 
213
  with gr.Tab("Generate Images"):
214
  with gr.Group():
215
- with gr.Row():
216
- prompt = gr.Text(
217
- label="Prompt",
218
- show_label=False,
219
- max_lines=1,
220
- placeholder="Enter your prompt",
221
- container=False,
222
- )
223
- run_button = gr.Button("Run", scale=0)
224
- result = gr.Gallery(label="Result", columns=1, preview=True, show_label=False)
225
  with gr.Accordion("Advanced options", open=False):
226
- use_negative_prompt = gr.Checkbox(label="Use negative prompt", value=True)
227
- negative_prompt = gr.Text(
228
- label="Negative prompt",
229
- lines=4,
230
- max_lines=6,
231
- value="""(deformed, distorted, disfigured:1.3), poorly drawn, bad anatomy, wrong anatomy, extra limb, missing limb, floating limbs, (mutated hands and fingers:1.4), disconnected limbs, mutation, mutated, ugly, disgusting, blurry, amputation, (NSFW:1.25)""",
232
- placeholder="Enter a negative prompt",
233
- visible=True,
234
- )
235
- seed = gr.Slider(
236
- label="Seed",
237
- minimum=0,
238
- maximum=MAX_SEED,
239
- step=1,
240
- value=0,
241
- visible=True
242
- )
243
  randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
244
- with gr.Row(visible=True):
245
- width = gr.Slider(
246
- label="Width",
247
- minimum=512,
248
- maximum=2048,
249
- step=8,
250
- value=1920,
251
- )
252
- height = gr.Slider(
253
- label="Height",
254
- minimum=512,
255
- maximum=2048,
256
- step=8,
257
- value=1080,
258
- )
259
- with gr.Row():
260
- guidance_scale = gr.Slider(
261
- label="Guidance Scale",
262
- minimum=0.1,
263
- maximum=20.0,
264
- step=0.1,
265
- value=20.0,
266
- )
267
-
268
- gr.Examples(
269
- examples=examples,
270
- inputs=prompt,
271
- outputs=[result, seed],
272
- fn=generate,
273
- cache_examples=False,
274
- )
275
 
276
- with gr.Tab("Gallery and Voting"):
277
- image_gallery = gr.Gallery(label="Generated Images", show_label=True, columns=4, height="auto")
278
-
 
 
 
 
279
  with gr.Row():
280
  like_button = gr.Button("👍 Like")
281
  dislike_button = gr.Button("👎 Dislike")
282
  heart_button = gr.Button("❤️ Heart")
283
- delete_image_button = gr.Button("🗑️ Delete Selected Image")
284
-
285
- selected_image = gr.State(None)
286
-
287
- with gr.Tab("Metadata and Management"):
288
- metadata_df = gr.Dataframe(
289
- label="Image Metadata",
290
- headers=["Filename", "Prompt", "Likes", "Dislikes", "Hearts", "Created"],
291
- interactive=False
292
- )
293
  delete_all_button = gr.Button("🗑️ Delete All Images")
294
 
295
- use_negative_prompt.change(
296
- fn=lambda x: gr.update(visible=x),
297
- inputs=use_negative_prompt,
298
- outputs=negative_prompt,
299
- api_name=False,
300
- )
301
-
302
- delete_all_button.click(
303
- fn=delete_all_images,
304
- inputs=[],
305
- outputs=[image_gallery, metadata_df],
306
- )
307
-
308
- image_gallery.select(
309
- fn=lambda evt: evt,
310
- inputs=[],
311
- outputs=[selected_image],
312
- )
313
 
314
- like_button.click(
315
- fn=lambda x: vote(x, 'Likes') if x else (get_image_gallery(), image_metadata.values.tolist()),
316
- inputs=[selected_image],
317
- outputs=[image_gallery, metadata_df],
318
- )
319
-
320
- dislike_button.click(
321
- fn=lambda x: vote(x, 'Dislikes') if x else (get_image_gallery(), image_metadata.values.tolist()),
322
- inputs=[selected_image],
323
- outputs=[image_gallery, metadata_df],
324
- )
325
-
326
- heart_button.click(
327
- fn=lambda x: vote(x, 'Hearts') if x else (get_image_gallery(), image_metadata.values.tolist()),
328
- inputs=[selected_image],
329
- outputs=[image_gallery, metadata_df],
330
- )
331
-
332
- delete_image_button.click(
333
- fn=delete_image,
334
- inputs=[selected_image],
335
- outputs=[image_gallery, metadata_df],
336
  )
337
 
338
- def update_gallery_and_metadata():
339
- return gr.update(value=get_image_gallery()), gr.update(value=image_metadata.values.tolist())
 
340
 
341
- gr.on(
342
- triggers=[
343
- prompt.submit,
344
- negative_prompt.submit,
345
- run_button.click,
346
- ],
347
- fn=generate,
348
- inputs=[
349
- prompt,
350
- negative_prompt,
351
- use_negative_prompt,
352
- seed,
353
- width,
354
- height,
355
- guidance_scale,
356
- randomize_seed],
357
- outputs=[result, seed, gr.HTML(visible=False), image_gallery, metadata_df],
358
- api_name="run",
359
- )
360
 
361
- demo.load(fn=update_gallery_and_metadata, outputs=[image_gallery, metadata_df])
362
 
363
  if __name__ == "__main__":
364
  demo.queue(max_size=20).launch(share=True, debug=False)
 
1
  import os
2
  import random
 
3
  import base64
4
  import gradio as gr
5
  import numpy as np
6
  from PIL import Image
 
7
  import torch
8
  import glob
9
  from datetime import datetime
 
11
  import json
12
  import re
13
  import logging
14
+ from diffusers import StableDiffusionXLPipeline, EulerAncestralDiscreteScheduler
15
 
16
  # Set up logging
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger(__name__)
19
 
 
 
20
  DESCRIPTION = """# 🎨 ArtForge: Community AI Gallery
21
+ Create, curate, and compete with AI-generated art. Join our creative multiplayer experience! 🖼️🏆✨"""
22
 
23
+ METADATA_FILE = "image_metadata.json"
24
+ MAX_SEED = np.iinfo(np.int32).max
25
 
26
  # Global variables
27
  image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
 
28
 
29
+ def load_metadata():
30
+ global image_metadata
31
+ if os.path.exists(METADATA_FILE):
32
+ with open(METADATA_FILE, 'r') as f:
33
+ image_metadata = pd.DataFrame(json.load(f))
34
+ else:
35
+ image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
 
 
36
 
37
+ def save_metadata():
38
+ with open(METADATA_FILE, 'w') as f:
39
+ json.dump(image_metadata.to_dict('records'), f)
40
 
41
+ load_metadata()
 
 
 
 
42
 
43
  def save_image(img, prompt):
44
+ global image_metadata
45
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
46
+ safe_prompt = re.sub(r'[^\w\s-]', '', prompt.lower())[:50]
47
  safe_prompt = re.sub(r'[-\s]+', '-', safe_prompt).strip('-')
48
  filename = f"{timestamp}_{safe_prompt}.png"
49
  img.save(filename)
 
53
  'Likes': [0],
54
  'Dislikes': [0],
55
  'Hearts': [0],
56
+ 'Created': [str(datetime.now())]
57
  })
58
  image_metadata = pd.concat([image_metadata, new_row], ignore_index=True, sort=False)
59
+ save_metadata()
 
60
  logger.info(f"Saved new image: {filename}")
61
  return filename
62
 
 
 
 
 
 
63
  def get_image_gallery():
64
+ return [(file, get_image_caption(file)) for file in image_metadata['Filename'] if os.path.exists(file)]
 
 
65
 
66
  def get_image_caption(filename):
67
+ if filename in image_metadata['Filename'].values:
68
+ row = image_metadata[image_metadata['Filename'] == filename].iloc[0]
69
+ return f"{filename}\nPrompt: {row['Prompt']}\n👍 {row['Likes']} 👎 {row['Dislikes']} ❤️ {row['Hearts']}"
 
 
 
 
70
  return filename
71
 
72
  def delete_all_images():
73
+ global image_metadata
74
  for file in image_metadata['Filename']:
75
  if os.path.exists(file):
76
  os.remove(file)
77
  image_metadata = pd.DataFrame(columns=['Filename', 'Prompt', 'Likes', 'Dislikes', 'Hearts', 'Created'])
78
+ save_metadata()
 
79
  logger.info("All images deleted")
80
  return get_image_gallery(), image_metadata.values.tolist()
81
 
82
  def delete_image(filename):
83
+ global image_metadata
84
  if filename and os.path.exists(filename):
85
  os.remove(filename)
86
  image_metadata = image_metadata[image_metadata['Filename'] != filename]
87
+ save_metadata()
 
 
88
  logger.info(f"Deleted image: {filename}")
89
  return get_image_gallery(), image_metadata.values.tolist()
90
 
91
  def vote(filename, vote_type):
92
+ global image_metadata
93
+ if filename in image_metadata['Filename'].values:
94
+ image_metadata.loc[image_metadata['Filename'] == filename, vote_type] += 1
95
+ save_metadata()
96
+ logger.info(f"Updated {vote_type} count for {filename}")
 
 
 
 
97
  else:
98
+ logger.warning(f"File {filename} not found in metadata")
99
+ return image_metadata.values.tolist()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
101
  if torch.cuda.is_available():
102
  pipe = StableDiffusionXLPipeline.from_pretrained(
 
105
  use_safetensors=True,
106
  )
107
  pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config)
 
108
  pipe.load_lora_weights("ehristoforu/dalle-3-xl-v2", weight_name="dalle-3-xl-lora-v2.safetensors", adapter_name="dalle")
109
  pipe.set_adapters("dalle")
 
110
  pipe.to("cuda")
111
+ else:
112
+ DESCRIPTION += "\n<p>Running on CPU 🥶 This demo may not work on CPU.</p>"
113
 
 
114
  def generate(
115
+ prompt, negative_prompt="", use_negative_prompt=False,
116
+ seed=0, width=1024, height=1024, guidance_scale=3, randomize_seed=False,
117
+ progress=gr.Progress(track_tqdm=True)
 
 
 
 
 
 
 
118
  ):
119
+ if randomize_seed:
120
+ seed = random.randint(0, MAX_SEED)
121
  if not use_negative_prompt:
122
  negative_prompt = ""
123
+
124
  images = pipe(
125
  prompt=prompt,
126
  negative_prompt=negative_prompt,
 
133
  output_type="pil",
134
  ).images
135
  image_paths = [save_image(img, prompt) for img in images]
136
+ return image_paths, seed, get_image_gallery(), image_metadata.values.tolist()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  css = '''
139
  .gradio-container{max-width: 1024px !important}
140
  h1{text-align:center}
141
+ footer {visibility: hidden}
 
 
142
  '''
143
 
144
  with gr.Blocks(theme=gr.themes.Soft(), css=css) as demo:
 
146
 
147
  with gr.Tab("Generate Images"):
148
  with gr.Group():
149
+ prompt = gr.Text(label="Prompt", placeholder="Enter your prompt")
150
+ run_button = gr.Button("Generate")
151
+ result = gr.Gallery(label="Result", columns=1, preview=True)
 
 
 
 
 
 
 
152
  with gr.Accordion("Advanced options", open=False):
153
+ use_negative_prompt = gr.Checkbox(label="Use negative prompt", value=False)
154
+ negative_prompt = gr.Text(label="Negative prompt", visible=False)
155
+ seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
157
+ width = gr.Slider(label="Width", minimum=512, maximum=2048, step=8, value=1024)
158
+ height = gr.Slider(label="Height", minimum=512, maximum=2048, step=8, value=1024)
159
+ guidance_scale = gr.Slider(label="Guidance Scale", minimum=0.1, maximum=20.0, step=0.1, value=7.5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ with gr.Tab("Gallery"):
162
+ image_gallery = gr.Gallery(label="Generated Images", columns=4, height="auto")
163
+ delete_image_button = gr.Button("🗑️ Delete Selected Image")
164
+ selected_image = gr.State(None)
165
+
166
+ with gr.Tab("Metadata and Management"):
167
+ metadata_df = gr.Dataframe(label="Image Metadata", headers=["Filename", "Prompt", "Likes", "Dislikes", "Hearts", "Created"])
168
  with gr.Row():
169
  like_button = gr.Button("👍 Like")
170
  dislike_button = gr.Button("👎 Dislike")
171
  heart_button = gr.Button("❤️ Heart")
 
 
 
 
 
 
 
 
 
 
172
  delete_all_button = gr.Button("🗑️ Delete All Images")
173
 
174
+ use_negative_prompt.change(lambda x: gr.update(visible=x), inputs=use_negative_prompt, outputs=negative_prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
+ run_button.click(
177
+ fn=generate,
178
+ inputs=[prompt, negative_prompt, use_negative_prompt, seed, width, height, guidance_scale, randomize_seed],
179
+ outputs=[result, seed, image_gallery, metadata_df]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  )
181
 
182
+ delete_all_button.click(fn=delete_all_images, outputs=[image_gallery, metadata_df])
183
+ image_gallery.select(fn=lambda evt: evt, outputs=selected_image)
184
+ delete_image_button.click(fn=delete_image, inputs=[selected_image], outputs=[image_gallery, metadata_df])
185
 
186
+ for button, vote_type in [(like_button, 'Likes'), (dislike_button, 'Dislikes'), (heart_button, 'Hearts')]:
187
+ button.click(fn=lambda x, vt=vote_type: vote(x, vt) if x else image_metadata.values.tolist(), inputs=[selected_image], outputs=[metadata_df])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ demo.load(fn=lambda: (get_image_gallery(), image_metadata.values.tolist()), outputs=[image_gallery, metadata_df])
190
 
191
  if __name__ == "__main__":
192
  demo.queue(max_size=20).launch(share=True, debug=False)