Dtimes commited on
Commit
d74b261
ยท
1 Parent(s): ad552d8
Files changed (1) hide show
  1. app.py +218 -121
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import os
 
2
  import gradio as gr
3
  import numpy as np
4
  import json
@@ -9,6 +10,9 @@ from PIL import Image
9
  from kit import compute_performance, compute_quality
10
  import dotenv
11
  import pandas as pd
 
 
 
12
 
13
  dotenv.load_dotenv()
14
 
@@ -70,41 +74,75 @@ redis_client = redis.Redis(
70
  )
71
 
72
 
73
- def save_to_redis(name, performance, quality):
74
- submission = {
75
- "name": name,
76
- "performance": performance,
77
- "quality": quality,
78
- "timestamp": datetime.now().isoformat(),
79
- }
80
- redis_client.lpush("submissions", json.dumps(submission))
 
 
 
 
81
 
82
 
83
  def get_submissions_from_redis():
84
  submissions = redis_client.lrange("submissions", 0, -1)
85
  submissions = [json.loads(submission) for submission in submissions]
86
  for s in submissions:
87
- s["quality"] = QUALITY_POST_FUNC(s["quality"])
88
- s["performance"] = PERFORMANCE_POST_FUNC(s["performance"])
89
  s["score"] = np.sqrt(float(s["quality"]) ** 2 + float(s["performance"]) ** 2)
90
- return submissions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
 
93
  def update_plot(
94
  submissions,
95
- current_name=None,
96
  ):
97
  names = [sub["name"] for sub in submissions]
98
- performances = [float(sub["performance"]) for sub in submissions]
99
- qualities = [float(sub["quality"]) for sub in submissions]
 
100
 
101
  # Create scatter plot
102
  fig = go.Figure()
103
 
104
- for name, quality, performance in zip(names, qualities, performances):
105
- if name == current_name:
106
- marker = dict(symbol="star", size=15, color="orange")
107
- elif name.startswith("Baseline: "):
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  marker = dict(symbol="square", size=8, color="blue")
109
  else:
110
  marker = dict(symbol="circle", size=10, color="green")
@@ -114,16 +152,15 @@ def update_plot(
114
  x=[quality],
115
  y=[performance],
116
  mode="markers+text",
117
- text=[name if not name.startswith("Baseline: ") else ""],
118
- textposition="top center",
119
  name=name,
120
  marker=marker,
121
- customdata=[
122
- name if name.startswith("Baseline: ") else f"User: {name}",
123
- ],
124
- hovertemplate="<b>%{customdata}</b><br>"
125
- + "Performance: %{y:.3f}<br>"
126
- + "Quality: %{x:.3f}<br>"
127
  + "<extra></extra>",
128
  )
129
  )
@@ -141,9 +178,9 @@ def update_plot(
141
  mode="lines",
142
  line=dict(color="gray", dash="dash"),
143
  showlegend=False,
144
- hovertemplate="Performance: %{x:.3f}<br>"
145
- + "Quality: %{y:.3f}<br>"
146
- + "<extra></extra>",
147
  )
148
  )
149
 
@@ -160,7 +197,6 @@ def update_plot(
160
  width=640,
161
  height=640,
162
  showlegend=False, # Remove legend
163
- modebar=dict(remove=["all"]),
164
  )
165
  fig.update_xaxes(title_font_size=20)
166
  fig.update_yaxes(title_font_size=20)
@@ -170,56 +206,59 @@ def update_plot(
170
 
171
  def update_table(
172
  submissions,
173
- current_name=None,
174
  ):
175
  def tp(timestamp):
176
- return timestamp.replace("T", " ").split(".")[0]
177
-
178
- names = [
179
- (
180
- sub["name"][len("Baseline: ") :]
181
- if sub["name"].startswith("Baseline: ")
182
- else sub["name"]
183
- )
184
- for sub in submissions
185
- ]
186
- times = [
187
- (
188
- ""
189
- if sub["name"].startswith("Baseline: ")
190
- else (
191
- tp(sub["timestamp"]) + " (Current)"
192
- if sub["name"] == current_name
193
- else tp(sub["timestamp"])
194
- )
195
- )
196
- for sub in submissions
197
- ]
198
- performances = ["%.4f" % (float(sub["performance"])) for sub in submissions]
199
- qualities = ["%.4f" % (float(sub["quality"])) for sub in submissions]
200
  scores = ["%.4f" % (float(sub["score"])) for sub in submissions]
 
 
 
 
 
 
 
 
 
 
201
  df = pd.DataFrame(
202
  {
203
- "Name": names,
204
- "Submission Time": times,
205
- "Performance": performances,
 
 
206
  "Quality": qualities,
207
  "Score": scores,
208
  }
209
- ).sort_values(by=["Score"])
210
- df.insert(0, "Rank #", list(np.arange(len(names)) + 1), True)
211
-
 
212
  def highlight_null(s):
213
  con = s.copy()
214
  con[:] = None
215
- if s["Submission Time"] == "":
216
- con[:] = "background-color: darkgrey"
217
  return con
218
-
219
  return df.style.apply(highlight_null, axis=1)
220
 
221
 
222
- def process_submission(name, image):
 
 
223
  original_image = Image.open("./image.png")
224
  progress = gr.Progress()
225
  progress(0, desc="Detecting Watermark")
@@ -227,23 +266,40 @@ def process_submission(name, image):
227
  progress(0.4, desc="Evaluating Image Quality")
228
  quality = compute_quality(image, original_image)
229
  progress(1.0, desc="Uploading Results")
230
-
231
- # Save unprocessed values but display processed values
232
- save_to_redis(name, performance, quality)
233
- quality = QUALITY_POST_FUNC(quality)
234
- performance = PERFORMANCE_POST_FUNC(performance)
235
-
236
- submissions = get_submissions_from_redis()
237
- leaderboard_table = update_table(submissions, current_name=name)
238
- leaderboard_plot = update_plot(submissions, current_name=name)
239
-
 
 
 
 
 
 
 
 
 
 
240
  # Calculate rank
241
  distances = [
242
- np.sqrt(float(s["quality"]) ** 2 + float(s["performance"]) ** 2)
243
- for s in submissions
244
  ]
245
- rank = sorted(distances).index(np.sqrt(quality**2 + performance**2)) + 1
246
- gr.Info(f"You ranked {rank} out of {len(submissions)}!")
 
 
 
 
 
 
 
247
  return (
248
  leaderboard_plot,
249
  leaderboard_table,
@@ -255,12 +311,16 @@ def process_submission(name, image):
255
  )
256
 
257
 
258
- def upload_and_evaluate(name, image):
259
  if name == "":
260
  raise gr.Error("Please enter your name before submitting.")
 
 
 
 
261
  if image is None:
262
  raise gr.Error("Please upload an image before submitting.")
263
- return process_submission(name, image)
264
 
265
 
266
  def create_interface():
@@ -268,25 +328,34 @@ def create_interface():
268
  gr.Markdown(
269
  """
270
  # Erasing the Invisible (Demo of NeurIPS'24 competition)
 
 
 
271
 
272
- Welcome to the demo of the NeurIPS'24 competition [Erasing the Invisible: A Stress-Test Challenge for Image Watermarks](https://erasinginvisible.github.io/).
273
-
274
- You could use this demo to better understand the competition pipeline or just for fun! ๐ŸŽฎ
275
-
276
- Here, we provide an image embedded with an invisible watermark. You only need to:
277
-
278
- 1. **Download** the original watermarked image. ๐ŸŒŠ
279
- 2. **Remove** the invisible watermark using your preferred attack. ๐Ÿงผ
280
- 3. **Upload** your image. We will evaluate and rank your attack. ๐Ÿ“Š
281
-
282
- That's it! ๐Ÿš€
283
-
284
- *Note: This is just a demo. The watermark used here is not necessarily representative of those used for the competition. To officially participate in the competition, please follow the guidelines [here](https://erasinginvisible.github.io/).*
285
  """
286
  )
287
 
288
  with gr.Tabs(elem_classes=["tabs"]) as tabs:
289
- with gr.Tab("Original Watermarked Image", id="download"):
 
 
 
 
 
 
 
 
290
  with gr.Column():
291
  original_image = gr.Image(
292
  value="./image.png",
@@ -317,7 +386,11 @@ def create_interface():
317
  id="submit",
318
  elem_classes="gr-tab-header",
319
  ):
320
-
 
 
 
 
321
  with gr.Column():
322
  uploaded_image = gr.Image(
323
  label="Your Watermark Removed Image",
@@ -335,32 +408,38 @@ def create_interface():
335
  elem_id="uploaded_image",
336
  )
337
  with gr.Row():
338
- name_input = gr.Textbox(
339
- label="Your Name", placeholder="Anonymous"
340
- )
341
- upload_btn = gr.Button("Upload and Evaluate")
 
 
 
 
 
 
 
 
 
342
 
343
  with gr.Tab(
344
  "Evaluation Results",
345
  id="plot",
346
  elem_classes="gr-tab-header",
347
  ):
348
- gr.Markdown(
349
- "The evaluation is based on two metrics, watermark performance ($$A$$) and image quality degradation ($$Q$$).",
350
- latex_delimiters=[{"left": "$$", "right": "$$", "display": False}],
351
- )
352
- gr.Markdown(
353
- "The lower the watermark performance and less quality degradation, the more effective the attack is. The overall score is $$\sqrt{Q^2+A^2}$$, the smaller the better.",
354
- latex_delimiters=[{"left": "$$", "right": "$$", "display": False}],
355
- )
356
  gr.Markdown(
357
  """
358
- <p>
359
- <span style="display: inline-block; width: 20px;"></span>๐ŸŸฆ: Baseline attacks
360
- <span style="display: inline-block; width: 20px;"></span>๐ŸŸข: Users' submissions
361
- <span style="display: inline-block; width: 20px;"></span>โญ: Your current submission
362
- </p>
363
- <p><em>Note: The performance and quality metrics differ from those in the competition (as only one image is used here), but they still give you an idea of how effective your attack is.</em></p>
 
 
 
 
 
364
  """
365
  )
366
  with gr.Column():
@@ -372,29 +451,44 @@ def create_interface():
372
  with gr.Row():
373
  rank_output = gr.Textbox(label="Your Ranking")
374
  name_output = gr.Textbox(label="Your Name")
375
- performance_output = gr.Textbox(label="Watermark Performance")
376
- quality_output = gr.Textbox(label="Quality Degredation")
377
- overall_output = gr.Textbox(label="Overall Score")
 
 
 
 
 
 
378
  with gr.Tab(
379
  "Leaderboard",
380
  id="leaderboard",
381
  elem_classes="gr-tab-header",
382
  ):
383
- gr.Markdown("Find your ranking on the leaderboard!")
384
  gr.Markdown(
385
- "Gray-shaded rows are baseline results provided by the organziers."
 
 
 
 
 
 
 
 
386
  )
387
  with gr.Column():
388
  leaderboard_table = gr.Dataframe(
389
  value=update_table(get_submissions_from_redis()),
 
390
  show_label=False,
391
  elem_id="leaderboard_table",
392
  )
 
393
  submit_btn.click(lambda: gr.Tabs(selected="submit"), None, tabs)
394
 
395
  upload_btn.click(lambda: gr.Tabs(selected="plot"), None, tabs).then(
396
  upload_and_evaluate,
397
- inputs=[name_input, uploaded_image],
398
  outputs=[
399
  leaderboard_plot,
400
  leaderboard_table,
@@ -410,7 +504,10 @@ def create_interface():
410
  lambda: [
411
  gr.Image(value="./image.png", height=512, width=512),
412
  gr.Plot(update_plot(get_submissions_from_redis())),
413
- gr.Dataframe(update_table(get_submissions_from_redis())),
 
 
 
414
  ],
415
  outputs=[original_image, leaderboard_plot, leaderboard_table],
416
  )
 
1
  import os
2
+ import io
3
  import gradio as gr
4
  import numpy as np
5
  import json
 
10
  from kit import compute_performance, compute_quality
11
  import dotenv
12
  import pandas as pd
13
+ from email_validator import validate_email, EmailNotValidError
14
+ import cloudinary
15
+ import cloudinary.uploader
16
 
17
  dotenv.load_dotenv()
18
 
 
74
  )
75
 
76
 
77
+ # Connect to Cloudinary
78
+ cloudinary.config(
79
+ cloud_name = os.getenv("CLOUDINARY_NAME"),
80
+ api_key = os.getenv("CLOUDINARY_KEY"),
81
+ api_secret = os.getenv("CLOUDINARY_SECRET"),
82
+ secure=True
83
+ )
84
+
85
+
86
+ def save_to_redis(current_submission):
87
+ redis_client.lpush("submissions", json.dumps(current_submission))
88
+ return current_submission
89
 
90
 
91
  def get_submissions_from_redis():
92
  submissions = redis_client.lrange("submissions", 0, -1)
93
  submissions = [json.loads(submission) for submission in submissions]
94
  for s in submissions:
95
+ s["quality"] = s["quality"]
96
+ s["performance"] = s["performance"]
97
  s["score"] = np.sqrt(float(s["quality"]) ** 2 + float(s["performance"]) ** 2)
98
+ return filter_submissions(submissions)
99
+
100
+
101
+ def filter_submissions(submissions):
102
+ new_submissions = []
103
+ for sub in submissions:
104
+ flag = True
105
+ for new_sub in new_submissions:
106
+ if sub["name"] == new_sub["name"]:
107
+ flag = False
108
+ if sub["score"] < new_sub["score"]:
109
+ for key in sub.keys():
110
+ new_sub[key] = sub[key]
111
+ break
112
+ if flag:
113
+ new_submissions.append(sub)
114
+ return new_submissions
115
 
116
 
117
  def update_plot(
118
  submissions,
119
+ current_submission=None,
120
  ):
121
  names = [sub["name"] for sub in submissions]
122
+ performances = [float(PERFORMANCE_POST_FUNC(sub["performance"])) for sub in submissions]
123
+ qualities = [float(QUALITY_POST_FUNC(sub["quality"])) for sub in submissions]
124
+ descriptions = [sub["description"] for sub in submissions]
125
 
126
  # Create scatter plot
127
  fig = go.Figure()
128
 
129
+ if current_submission is not None:
130
+ fig.add_trace(
131
+ go.Scatter(
132
+ x=[QUALITY_POST_FUNC(current_submission["quality"])],
133
+ y=[PERFORMANCE_POST_FUNC(current_submission["performance"])],
134
+ mode="markers+text",
135
+ #text=[name if not name.startswith("Baseline: ") else ""],
136
+ #textposition="top center",
137
+ name=current_submission["name"],
138
+ marker=dict(symbol="star", size=15, color="orange"),
139
+ customdata=[current_submission["name"]],
140
+ hovertemplate = "<b>%{customdata}</b><br>" + "Performance: %{y:.3f}<br>" + "Quality: %{x:.3f}<br>" + f"Description: {current_submission['description'] if current_submission['description'] != '' else 'N/A'}" + "<extra></extra>",
141
+ )
142
+ )
143
+
144
+ for name, quality, performance, description in zip(names, qualities, performances, descriptions):
145
+ if name.startswith("Baseline: "):
146
  marker = dict(symbol="square", size=8, color="blue")
147
  else:
148
  marker = dict(symbol="circle", size=10, color="green")
 
152
  x=[quality],
153
  y=[performance],
154
  mode="markers+text",
155
+ #text=[name if not name.startswith("Baseline: ") else ""],
156
+ #textposition="top center",
157
  name=name,
158
  marker=marker,
159
+ customdata=[name if name.startswith("Baseline: ") else f"User: {name}",],
160
+ hovertemplate = "<b>%{customdata}</b><br>"
161
+ + "Performance: %{y:.3f}<br>"
162
+ + "Quality: %{x:.3f}<br>"
163
+ + f"Description: {description if description != '' else 'N/A'}"
 
164
  + "<extra></extra>",
165
  )
166
  )
 
178
  mode="lines",
179
  line=dict(color="gray", dash="dash"),
180
  showlegend=False,
181
+ hovertemplate = "Performance: %{x:.3f}<br>"
182
+ + "Quality: %{y:.3f}<br>"
183
+ + "<extra></extra>"
184
  )
185
  )
186
 
 
197
  width=640,
198
  height=640,
199
  showlegend=False, # Remove legend
 
200
  )
201
  fig.update_xaxes(title_font_size=20)
202
  fig.update_yaxes(title_font_size=20)
 
206
 
207
  def update_table(
208
  submissions,
209
+ current_submission=None,
210
  ):
211
  def tp(timestamp):
212
+ return timestamp.replace("T", " ").split('.')[0]
213
+ def get_name(name, is_published, url_image):
214
+ text = name[len("Baseline: "):] if name.startswith("Baseline: ") else name
215
+ if not is_published or url_image == "":
216
+ return text
217
+ else:
218
+ return f"[{text}]({url_image})"
219
+ names = [get_name(sub["name"], sub["is_published"], sub["url_image"]) for sub in submissions]
220
+ emails = [sub["email"] for sub in submissions]
221
+ descriptions = [sub["description"] for sub in submissions]
222
+ times = ["" if sub["name"].startswith("Baseline: ") else tp(sub["timestamp"]) for sub in submissions]
223
+ performances = ["%.4f" % (float(PERFORMANCE_POST_FUNC(sub["performance"]))) for sub in submissions]
224
+ qualities = ["%.4f" % (float(QUALITY_POST_FUNC(sub["quality"]))) for sub in submissions]
 
 
 
 
 
 
 
 
 
 
 
225
  scores = ["%.4f" % (float(sub["score"])) for sub in submissions]
226
+
227
+ if current_submission is not None:
228
+ names.append(get_name(current_submission["name"], current_submission["is_published"], current_submission["url_image"]))
229
+ emails.append(current_submission["email"])
230
+ descriptions.append(current_submission["description"])
231
+ times.append(current_submission["timestamp"]+" (Current)")
232
+ performances.append("%.4f" % (float(PERFORMANCE_POST_FUNC(current_submission["performance"]))))
233
+ qualities.append("%.4f" % (float(QUALITY_POST_FUNC(current_submission["quality"]))))
234
+ scores.append("%.4f" % (float(np.sqrt(float(QUALITY_POST_FUNC(current_submission["quality"])) ** 2 + float(PERFORMANCE_POST_FUNC(current_submission["performance"])) ** 2))))
235
+
236
  df = pd.DataFrame(
237
  {
238
+ "Name":names,
239
+ "Email":emails,
240
+ "Description":descriptions,
241
+ "Submission Time":times,
242
+ "Performance":performances,
243
  "Quality": qualities,
244
  "Score": scores,
245
  }
246
+ ).sort_values(
247
+ by=["Score"]
248
+ )
249
+ df.insert(0, "Rank #", list(np.arange(len(names))+1), True)
250
  def highlight_null(s):
251
  con = s.copy()
252
  con[:] = None
253
+ if s['Submission Time'] == '':
254
+ con[:] = 'background-color: lightgrey'
255
  return con
 
256
  return df.style.apply(highlight_null, axis=1)
257
 
258
 
259
+ def process_submission(name, email, description, is_published, image):
260
+ submissions = get_submissions_from_redis()
261
+
262
  original_image = Image.open("./image.png")
263
  progress = gr.Progress()
264
  progress(0, desc="Detecting Watermark")
 
266
  progress(0.4, desc="Evaluating Image Quality")
267
  quality = compute_quality(image, original_image)
268
  progress(1.0, desc="Uploading Results")
269
+ b = io.BytesIO()
270
+ image.save(b, 'png')
271
+ im_bytes = b.getvalue()
272
+ upload_result = cloudinary.uploader.upload(im_bytes, public_id=email)
273
+ url_image = upload_result["secure_url"]
274
+
275
+ current_submission = {
276
+ "name": name,
277
+ "performance": performance,
278
+ "quality": quality,
279
+ "timestamp": datetime.now().isoformat(),
280
+ "email": email,
281
+ "description": description,
282
+ "is_published": is_published,
283
+ "url_image": url_image,
284
+ }
285
+
286
+ leaderboard_table = update_table(submissions, current_submission=current_submission)
287
+ leaderboard_plot = update_plot(submissions, current_submission=current_submission)
288
+
289
  # Calculate rank
290
  distances = [
291
+ np.sqrt(float(QUALITY_POST_FUNC(s["quality"])) ** 2 + float(PERFORMANCE_POST_FUNC(s["performance"])) ** 2)
292
+ for s in submissions+[current_submission]
293
  ]
294
+ rank = (
295
+ sorted(distances, reverse=True).index(
296
+ np.sqrt(float(QUALITY_POST_FUNC(quality))**2 + float(PERFORMANCE_POST_FUNC(performance))**2)
297
+ ) + 1
298
+ )
299
+ gr.Info(f"You ranked {rank} out of {len(submissions)+1}!")
300
+
301
+ save_to_redis(current_submission)
302
+
303
  return (
304
  leaderboard_plot,
305
  leaderboard_table,
 
311
  )
312
 
313
 
314
+ def upload_and_evaluate(name, email, description, is_published, image):
315
  if name == "":
316
  raise gr.Error("Please enter your name before submitting.")
317
+ try:
318
+ email = validate_email(email)["email"]
319
+ except EmailNotValidError as e:
320
+ raise gr.Error(f"Please enter a valid email before submitting.")
321
  if image is None:
322
  raise gr.Error("Please upload an image before submitting.")
323
+ return process_submission(name, email, description, is_published, image)
324
 
325
 
326
  def create_interface():
 
328
  gr.Markdown(
329
  """
330
  # Erasing the Invisible (Demo of NeurIPS'24 competition)
331
+ ### Welcome to the demo of the NeurIPS'24 competition [Erasing the Invisible: A Stress-Test Challenge for Image Watermarks](https://erasinginvisible.github.io/).
332
+
333
+ ### You could use this demo to better understand the competition pipeline or just for fun! ๐ŸŽฎ
334
 
335
+ ### Here, we provide a image embedded with invisible watermark, you only need to:
336
+
337
+ ### Step 1: **Download** the original watermarked image. ๐ŸŒŠ
338
+
339
+ ### Step 2: **Remove** the invisible watermark using your preferred attack. ๐Ÿงผ
340
+
341
+ ### Step 3: **Upload** your image. We will evaluate and rank your attack. ๐Ÿ“Š
342
+
343
+ ### That's it! ๐Ÿš€
344
+
345
+ ### *Note: This is just a demo. The watermark used here is not necessarily representative of those used for the competition. To officially participate in the competition, please follow the guidelines [here](https://erasinginvisible.github.io/).*
 
 
346
  """
347
  )
348
 
349
  with gr.Tabs(elem_classes=["tabs"]) as tabs:
350
+ with gr.Tab(
351
+ "Original Watermarked Image",
352
+ id="download"
353
+ ):
354
+ # gr.Markdown(
355
+ # """
356
+ # TODO: Add descriptions
357
+ # """
358
+ # )
359
  with gr.Column():
360
  original_image = gr.Image(
361
  value="./image.png",
 
386
  id="submit",
387
  elem_classes="gr-tab-header",
388
  ):
389
+ # gr.Markdown(
390
+ # """
391
+ # TODO: Add descriptions
392
+ # """
393
+ # )
394
  with gr.Column():
395
  uploaded_image = gr.Image(
396
  label="Your Watermark Removed Image",
 
408
  elem_id="uploaded_image",
409
  )
410
  with gr.Row():
411
+ with gr.Column():
412
+ description_input = gr.Textbox(
413
+ label="Method Description (optional)", placeholder="You could provide here a brief description of the attack", lines=6
414
+ )
415
+ is_published_input = gr.Checkbox(label="Would you like to publish your image?")
416
+ with gr.Column():
417
+ name_input = gr.Textbox(
418
+ label="Your Name", placeholder="Anonymous"
419
+ )
420
+ email_input = gr.Textbox(
421
+ label="Your Email", placeholder="Anonymous"
422
+ )
423
+ upload_btn = gr.Button("Upload and Evaluate")
424
 
425
  with gr.Tab(
426
  "Evaluation Results",
427
  id="plot",
428
  elem_classes="gr-tab-header",
429
  ):
 
 
 
 
 
 
 
 
430
  gr.Markdown(
431
  """
432
+ <h3> The evaluation is based on two metrics, watermark performance (A) and image quality degradation (Q).
433
+ The lower the watermark performance and less quality degradation, the more effective the attack is.
434
+ The overall score is $$\large \sqrt{Q^2+A^2}$$, the smaller the better.
435
+
436
+ ๐ŸŸฆ: Baseline attacks
437
+
438
+ ๐ŸŸข: Users' submissions
439
+
440
+ โญ: Your current submission
441
+
442
+ Note: The performance and quality metrics differ from those in the competition (as only one image is used here), but they still give you an idea of how effective your attack is.
443
  """
444
  )
445
  with gr.Column():
 
451
  with gr.Row():
452
  rank_output = gr.Textbox(label="Your Ranking")
453
  name_output = gr.Textbox(label="Your Name")
454
+ performance_output = gr.Textbox(
455
+ label="Watermark Performance (lower is better)"
456
+ )
457
+ quality_output = gr.Textbox(
458
+ label="Quality Degredation (lower is better)"
459
+ )
460
+ overall_output = gr.Textbox(
461
+ label="Overall Score (lower is better)"
462
+ )
463
  with gr.Tab(
464
  "Leaderboard",
465
  id="leaderboard",
466
  elem_classes="gr-tab-header",
467
  ):
 
468
  gr.Markdown(
469
+ """
470
+ <h3> Find your ranking on the leaderboard!
471
+
472
+ <h3> Gray-shaded rows are baseline results provided by the organziers.
473
+
474
+ <h3> To check the pulished attacked images, click on the links in the "Name" column.
475
+
476
+ <h3> For multiple submissions with the same name, only the best (lowest) score is shown.
477
+ """
478
  )
479
  with gr.Column():
480
  leaderboard_table = gr.Dataframe(
481
  value=update_table(get_submissions_from_redis()),
482
+ datatype=["str", "markdown", "str", "str", "str", "str", "str"],
483
  show_label=False,
484
  elem_id="leaderboard_table",
485
  )
486
+
487
  submit_btn.click(lambda: gr.Tabs(selected="submit"), None, tabs)
488
 
489
  upload_btn.click(lambda: gr.Tabs(selected="plot"), None, tabs).then(
490
  upload_and_evaluate,
491
+ inputs=[name_input, email_input, description_input, is_published_input, uploaded_image],
492
  outputs=[
493
  leaderboard_plot,
494
  leaderboard_table,
 
504
  lambda: [
505
  gr.Image(value="./image.png", height=512, width=512),
506
  gr.Plot(update_plot(get_submissions_from_redis())),
507
+ gr.Dataframe(
508
+ update_table(get_submissions_from_redis()),
509
+ datatype=["str", "markdown", "str", "str", "str", "str", "str"]
510
+ ),
511
  ],
512
  outputs=[original_image, leaderboard_plot, leaderboard_table],
513
  )