davidberenstein1957 commited on
Commit
a94c604
·
1 Parent(s): 89dc802

Add image comparison slider functionality using gradio-imageslider. Update layout for frame display and enhance overall quality metric description. Update project dependencies in pyproject.toml and uv.lock.

Browse files
Files changed (3) hide show
  1. app.py +45 -19
  2. pyproject.toml +1 -0
  3. uv.lock +16 -1
app.py CHANGED
@@ -6,6 +6,7 @@ import gradio as gr
6
  import imagehash
7
  import numpy as np
8
  import plotly.graph_objects as go
 
9
  from PIL import Image
10
  from scipy.stats import pearsonr
11
  from skimage.metrics import mean_squared_error as mse_skimage
@@ -1532,13 +1533,28 @@ def create_app():
1532
  with gr.Column():
1533
  gr.Markdown("### Video 1 - Current Frame")
1534
  frame1_output = gr.Image(
1535
- label="Video 1 Frame", type="numpy", interactive=False, height=400
 
 
 
 
 
 
 
 
 
 
 
 
1536
  )
1537
 
1538
  with gr.Column():
1539
  gr.Markdown("### Video 2 - Current Frame")
1540
  frame2_output = gr.Image(
1541
- label="Video 2 Frame", type="numpy", interactive=False, height=400
 
 
 
1542
  )
1543
 
1544
  # Frame navigation (initially hidden) - moved underneath frames
@@ -1586,7 +1602,7 @@ def create_app():
1586
  - **pHash**: Perceptual Hash similarity (1.0 = visually identical)
1587
  - **Color Histogram**: Color distribution correlation (1.0 = identical color patterns)
1588
  - **Sharpness**: Laplacian variance per video (higher = sharper/more detailed images)
1589
- - **Overall Quality**: Combined metric averaging SSIM, normalized PSNR, and pHash (when available)
1590
  """)
1591
  with gr.Column() as info_section:
1592
  status_output = gr.Textbox(
@@ -1691,6 +1707,7 @@ def create_app():
1691
  status, # status_output
1692
  slider_update, # frame_slider
1693
  frame1, # frame1_output
 
1694
  frame2, # frame2_output
1695
  info, # frame_info
1696
  ssim_fig, # ssim_plot
@@ -1709,6 +1726,7 @@ def create_app():
1709
  def update_frames(frame_index):
1710
  if comparator.max_frames == 0:
1711
  return (
 
1712
  None,
1713
  None,
1714
  "No videos loaded",
@@ -1718,6 +1736,7 @@ def create_app():
1718
  None,
1719
  None,
1720
  None,
 
1721
  )
1722
 
1723
  frame1, frame2 = comparator.get_frames_at_index(frame_index)
@@ -1735,6 +1754,7 @@ def create_app():
1735
 
1736
  return (
1737
  frame1,
 
1738
  frame2,
1739
  info,
1740
  ssim_fig,
@@ -1763,6 +1783,7 @@ def create_app():
1763
  minimum=0, maximum=0, step=1, value=0, interactive=False
1764
  ), # frame_slider
1765
  None, # frame1_output
 
1766
  None, # frame2_output
1767
  "", # frame_info
1768
  None, # ssim_plot
@@ -1792,22 +1813,23 @@ def create_app():
1792
  print("DEBUG: Same video pair already processed, skipping...")
1793
  # Return current state without recomputing
1794
  return (
1795
- gr.update(),
1796
- gr.update(),
1797
- gr.update(),
1798
- gr.update(),
1799
- gr.update(),
1800
- gr.update(),
1801
- gr.update(),
1802
- gr.update(),
1803
- gr.update(),
1804
- gr.update(),
1805
- gr.update(),
1806
- gr.update(),
1807
- gr.update(),
1808
- gr.update(),
1809
- gr.update(),
1810
- gr.update(),
 
1811
  )
1812
 
1813
  last_processed_pair["video1"] = video1
@@ -1823,6 +1845,7 @@ def create_app():
1823
  status_output,
1824
  frame_slider,
1825
  frame1_output,
 
1826
  frame2_output,
1827
  frame_info,
1828
  ssim_plot,
@@ -1846,6 +1869,7 @@ def create_app():
1846
  status_output,
1847
  frame_slider,
1848
  frame1_output,
 
1849
  frame2_output,
1850
  frame_info,
1851
  ssim_plot,
@@ -1874,6 +1898,7 @@ def create_app():
1874
  status_output,
1875
  frame_slider,
1876
  frame1_output,
 
1877
  frame2_output,
1878
  frame_info,
1879
  ssim_plot,
@@ -1895,6 +1920,7 @@ def create_app():
1895
  inputs=[frame_slider],
1896
  outputs=[
1897
  frame1_output,
 
1898
  frame2_output,
1899
  frame_info,
1900
  ssim_plot,
 
6
  import imagehash
7
  import numpy as np
8
  import plotly.graph_objects as go
9
+ from gradio_imageslider import ImageSlider
10
  from PIL import Image
11
  from scipy.stats import pearsonr
12
  from skimage.metrics import mean_squared_error as mse_skimage
 
1533
  with gr.Column():
1534
  gr.Markdown("### Video 1 - Current Frame")
1535
  frame1_output = gr.Image(
1536
+ label="Video 1 Frame",
1537
+ type="numpy",
1538
+ interactive=False,
1539
+ # height=400,
1540
+ )
1541
+
1542
+ with gr.Column():
1543
+ gr.Markdown("### Frame Comparison Slider")
1544
+ image_slider = ImageSlider(
1545
+ label="Drag to compare frames",
1546
+ type="numpy",
1547
+ interactive=True,
1548
+ # height=400,
1549
  )
1550
 
1551
  with gr.Column():
1552
  gr.Markdown("### Video 2 - Current Frame")
1553
  frame2_output = gr.Image(
1554
+ label="Video 2 Frame",
1555
+ type="numpy",
1556
+ interactive=False,
1557
+ # height=400,
1558
  )
1559
 
1560
  # Frame navigation (initially hidden) - moved underneath frames
 
1602
  - **pHash**: Perceptual Hash similarity (1.0 = visually identical)
1603
  - **Color Histogram**: Color distribution correlation (1.0 = identical color patterns)
1604
  - **Sharpness**: Laplacian variance per video (higher = sharper/more detailed images)
1605
+ - **Overall Quality**: Combined metric averaging SSIM, min-max normalized PSNR, and pHash
1606
  """)
1607
  with gr.Column() as info_section:
1608
  status_output = gr.Textbox(
 
1707
  status, # status_output
1708
  slider_update, # frame_slider
1709
  frame1, # frame1_output
1710
+ (frame1, frame2), # image_slider
1711
  frame2, # frame2_output
1712
  info, # frame_info
1713
  ssim_fig, # ssim_plot
 
1726
  def update_frames(frame_index):
1727
  if comparator.max_frames == 0:
1728
  return (
1729
+ None,
1730
  None,
1731
  None,
1732
  "No videos loaded",
 
1736
  None,
1737
  None,
1738
  None,
1739
+ None,
1740
  )
1741
 
1742
  frame1, frame2 = comparator.get_frames_at_index(frame_index)
 
1754
 
1755
  return (
1756
  frame1,
1757
+ (frame1, frame2),
1758
  frame2,
1759
  info,
1760
  ssim_fig,
 
1783
  minimum=0, maximum=0, step=1, value=0, interactive=False
1784
  ), # frame_slider
1785
  None, # frame1_output
1786
+ (None, None), # image_slider
1787
  None, # frame2_output
1788
  "", # frame_info
1789
  None, # ssim_plot
 
1813
  print("DEBUG: Same video pair already processed, skipping...")
1814
  # Return current state without recomputing
1815
  return (
1816
+ gr.update(), # status_output
1817
+ gr.update(), # frame_slider
1818
+ gr.update(), # frame1_output
1819
+ gr.update(), # image_slider
1820
+ gr.update(), # frame2_output
1821
+ gr.update(), # frame_info
1822
+ gr.update(), # ssim_plot
1823
+ gr.update(), # psnr_plot
1824
+ gr.update(), # mse_plot
1825
+ gr.update(), # phash_plot
1826
+ gr.update(), # color_plot
1827
+ gr.update(), # sharpness_plot
1828
+ gr.update(), # overall_plot
1829
+ gr.update(), # frame_controls
1830
+ gr.update(), # frame_display
1831
+ gr.update(), # metrics_section
1832
+ gr.update(), # info_section
1833
  )
1834
 
1835
  last_processed_pair["video1"] = video1
 
1845
  status_output,
1846
  frame_slider,
1847
  frame1_output,
1848
+ image_slider,
1849
  frame2_output,
1850
  frame_info,
1851
  ssim_plot,
 
1869
  status_output,
1870
  frame_slider,
1871
  frame1_output,
1872
+ image_slider,
1873
  frame2_output,
1874
  frame_info,
1875
  ssim_plot,
 
1898
  status_output,
1899
  frame_slider,
1900
  frame1_output,
1901
+ image_slider,
1902
  frame2_output,
1903
  frame_info,
1904
  ssim_plot,
 
1920
  inputs=[frame_slider],
1921
  outputs=[
1922
  frame1_output,
1923
+ image_slider,
1924
  frame2_output,
1925
  frame_info,
1926
  ssim_plot,
pyproject.toml CHANGED
@@ -13,4 +13,5 @@ dependencies = [
13
  "plotly>=5.17.0",
14
  "imagehash>=4.3.1",
15
  "scipy>=1.11.0",
 
16
  ]
 
13
  "plotly>=5.17.0",
14
  "imagehash>=4.3.1",
15
  "scipy>=1.11.0",
16
+ "gradio-imageslider>=0.0.20",
17
  ]
uv.lock CHANGED
@@ -217,11 +217,12 @@ wheels = [
217
  ]
218
 
219
  [[package]]
220
- name = "framelens"
221
  version = "0.1.0"
222
  source = { virtual = "." }
223
  dependencies = [
224
  { name = "gradio" },
 
225
  { name = "imagehash" },
226
  { name = "numpy" },
227
  { name = "opencv-python" },
@@ -234,6 +235,7 @@ dependencies = [
234
  [package.metadata]
235
  requires-dist = [
236
  { name = "gradio", specifier = ">=5.38.2" },
 
237
  { name = "imagehash", specifier = ">=4.3.1" },
238
  { name = "numpy", specifier = ">=1.24.0" },
239
  { name = "opencv-python", specifier = ">=4.8.0" },
@@ -310,6 +312,19 @@ wheels = [
310
  { url = "https://files.pythonhosted.org/packages/e0/38/7f50ae95de8fa419276742230f57a34e8c0f47231da0ad54479dd0088972/gradio_client-1.11.0-py3-none-any.whl", hash = "sha256:afb714aea50224f6f04679fe2ce79c1be75011012d0dc3b3ee575610a0dc8eb2", size = 324452 },
311
  ]
312
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  [[package]]
314
  name = "groovy"
315
  version = "0.1.2"
 
217
  ]
218
 
219
  [[package]]
220
+ name = "framearena"
221
  version = "0.1.0"
222
  source = { virtual = "." }
223
  dependencies = [
224
  { name = "gradio" },
225
+ { name = "gradio-imageslider" },
226
  { name = "imagehash" },
227
  { name = "numpy" },
228
  { name = "opencv-python" },
 
235
  [package.metadata]
236
  requires-dist = [
237
  { name = "gradio", specifier = ">=5.38.2" },
238
+ { name = "gradio-imageslider", specifier = ">=0.0.20" },
239
  { name = "imagehash", specifier = ">=4.3.1" },
240
  { name = "numpy", specifier = ">=1.24.0" },
241
  { name = "opencv-python", specifier = ">=4.8.0" },
 
312
  { url = "https://files.pythonhosted.org/packages/e0/38/7f50ae95de8fa419276742230f57a34e8c0f47231da0ad54479dd0088972/gradio_client-1.11.0-py3-none-any.whl", hash = "sha256:afb714aea50224f6f04679fe2ce79c1be75011012d0dc3b3ee575610a0dc8eb2", size = 324452 },
313
  ]
314
 
315
+ [[package]]
316
+ name = "gradio-imageslider"
317
+ version = "0.0.20"
318
+ source = { registry = "https://pypi.org/simple" }
319
+ dependencies = [
320
+ { name = "gradio" },
321
+ { name = "pillow" },
322
+ ]
323
+ sdist = { url = "https://files.pythonhosted.org/packages/4e/20/aadd2089f4b45abb8f8a407ad124c6a0bda35298bb44af56cc160ec6d945/gradio_imageslider-0.0.20.tar.gz", hash = "sha256:a1421c3239cce2a01160852cdc0962292230418384a0cff3b0307a95a451643f", size = 256548 }
324
+ wheels = [
325
+ { url = "https://files.pythonhosted.org/packages/38/e5/c33f36a4fd0bd8207bdb0761513387bfbd28b54f51d31265e93fff2d1b1b/gradio_imageslider-0.0.20-py3-none-any.whl", hash = "sha256:c73d155ce14a63f3fceb7547e8238e93c12c9acaecf7445cf8f281aa880cac79", size = 101455 },
326
+ ]
327
+
328
  [[package]]
329
  name = "groovy"
330
  version = "0.1.2"