Spaces:
Running
Running
import base64 | |
import io | |
from loguru import logger as log | |
from pathlib import Path | |
import gradio as gr | |
from PIL import Image | |
import iscc_core as ic | |
import iscc_sdk as idk | |
import iscc_sci as sci | |
import plotly.graph_objects as go | |
import pandas as pd | |
idk.sdk_opts.image_thumbnail_size = 265 | |
idk.sdk_opts.image_thumbnail_quality = 80 | |
HERE = Path(__file__).parent.absolute() | |
IMAGES1 = HERE / "images1" | |
IMAGES2 = HERE / "images2" | |
custom_css = """ | |
.fixed-height { | |
height: 240px; /* Fixed height */ | |
object-fit: contain; /* Scale the image to fit within the element */ | |
} | |
#examples-a, #examples-b { | |
height: 140px; /* Fixed height */ | |
object-fit: contain; /* Scale the image to fit within the element */ | |
} | |
""" | |
def iscc_semantic(filepath: str) -> idk.IsccMeta: | |
"""Generate ISCC-CODE extended with Semantic-Code for supported modalities (Image)""" | |
imeta = idk.code_iscc(filepath) | |
if imeta.mode == "image": | |
# Inject Semantic-Code | |
sci_code = sci.code_image_semantic(filepath, bits=64)["iscc"] | |
units = ic.iscc_decompose(imeta.iscc) | |
units.append(sci_code) | |
iscc_code_s = ic.gen_iscc_code(units)["iscc"] | |
imeta.iscc = iscc_code_s | |
return imeta | |
def dist_to_sim(data, dim=64): | |
result = {} | |
for k, v in data.items(): | |
if k == "instance_match": | |
result[k.split("_")[0].title()] = 1.0 if v is True else -1.0 | |
else: | |
result[k.split("_")[0].title()] = hamming_to_cosine(v, dim) | |
return result | |
def hamming_to_cosine(hamming_distance: int, dim: int) -> float: | |
"""Aproximate the cosine similarity for a given hamming distance and dimension""" | |
result = 1 - (2 * hamming_distance) / dim | |
log.debug(f"Hamming distance: {hamming_distance} - Dim: {dim} - Result: {result}") | |
return result | |
def similarity_plot(sim_data): | |
# type: (dict) -> go.Figure | |
# Convert input dictionary to DataFrame, sort by value for visual consistency | |
data_df = pd.DataFrame(reversed(sim_data.items()), columns=["Category", "Value"]) | |
data_df["Percentage"] = data_df["Value"] * 100 # Convert to percentage | |
# Define color for bars based on value | |
# data_df["Color"] = ["red" if x < 0 else "green" for x in data_df["Value"]] | |
data_df["Color"] = [ | |
f"rgba(224,122,95,{abs(x)})" if x < 0 else f"rgba(118,185,71,{x})" | |
for x in data_df["Value"] | |
] | |
# Create Plotly Figure | |
fig = go.Figure() | |
fig.add_trace( | |
go.Bar( | |
x=data_df["Value"], | |
y=data_df["Category"], | |
orientation="h", | |
marker_color=data_df["Color"], | |
text=data_df["Percentage"].apply(lambda x: f"{x:.2f}%"), | |
textposition="inside", | |
) | |
) # Change made here | |
# Update layout for aesthetics | |
fig.update_layout( | |
title={"text": "Approximate ISCC-UNIT Similarities", "x": 0.5}, | |
xaxis=dict(title="Similarity", tickformat=",.0%"), | |
yaxis=dict(title=""), | |
plot_bgcolor="rgba(0,0,0,0)", | |
height=len(sim_data) * 70, | |
showlegend=False, | |
autosize=True, | |
margin=dict(l=50, r=50, t=50, b=50), | |
) | |
# Adjust the x-axis to accommodate percentage labels | |
fig.update_xaxes(range=[-1.1, 1.1]) | |
return fig | |
with gr.Blocks(css=custom_css) as demo: | |
gr.Markdown("## 🖼️ ISCC Similarity Comparison") | |
with gr.Row(variant="default", equal_height=True): | |
with gr.Column(variant="compact"): | |
in_file_a = gr.File( | |
label="Media File A", type="filepath", elem_classes=["fixed-height"] | |
) | |
out_thumb_a = gr.Image( | |
label="Extracted Thumbnail", | |
visible=False, | |
height=240, | |
elem_classes=["fixed-height"], | |
interactive=True, | |
show_download_button=False, | |
sources=["upload"], | |
) | |
# Proxy component to patch image example selection -> gr.File | |
dumy_image_a = gr.Image(visible=False, type="filepath", height=240) | |
gr.Examples( | |
examples=IMAGES1.as_posix(), | |
cache_examples=False, | |
inputs=[dumy_image_a], | |
elem_id="examples-a", | |
) | |
out_iscc_a = gr.Text(label="ISCC", show_copy_button=True) | |
with gr.Accordion(label="ISCC Metadata", open=False): | |
out_meta_a = gr.Code(language="json", label="JSON-LD") | |
with gr.Column(variant="compact"): | |
in_file_b = gr.File( | |
label="Media File B", type="filepath", elem_classes=["fixed-height"] | |
) | |
out_thumb_b = gr.Image( | |
label="Extracted Thumbnail", | |
visible=False, | |
height=240, | |
elem_classes=["fixed-height"], | |
interactive=True, | |
show_download_button=False, | |
sources=["upload"], | |
) | |
# Proxy component to patch image example selection -> gr.File | |
dumy_image_b = gr.Image(visible=False, type="filepath", height=240) | |
gr.Examples( | |
examples=IMAGES2.as_posix(), | |
cache_examples=False, | |
inputs=[dumy_image_b], | |
elem_id="examples-b", | |
) | |
out_iscc_b = gr.Text(label="ISCC", show_copy_button=True) | |
with gr.Accordion(label="ISCC Metadata", open=False): | |
out_meta_b = gr.Code(language="json", label="JSON-LD") | |
with gr.Row(variant="panel"): | |
out_compare = gr.Plot( | |
label="Approximate ISCC-UNIT Similarities", container=False | |
) | |
def rewrite_uri(filepath, sample_set): | |
# type: (str, str) -> str | |
"""Rewrites temporary image URI to original sample URI""" | |
if filepath: | |
inpath = Path(filepath) | |
outpath = HERE / f"{sample_set}/{inpath.name.replace('jpeg', 'jpg')}" | |
log.info(filepath) | |
return outpath.as_posix() | |
def process_upload(filepath, suffix): | |
# type: (str, str) -> dict | |
"""Generate extended ISCC with experimental Semantic Code (for images)""" | |
# Map to active component group | |
in_file_func = globals().get(f"in_file_{suffix}") | |
out_thumb_func = globals().get(f"out_thumb_{suffix}") | |
out_iscc_func = globals().get(f"out_iscc_{suffix}") | |
out_meta_func = globals().get(f"out_meta_{suffix}") | |
# Handle emtpy filepath | |
if not filepath: | |
return { | |
in_file_func: None, | |
} | |
imeta = iscc_semantic(filepath) | |
# Pop Thumbnail for Preview | |
thumbnail = None | |
if imeta.thumbnail: | |
header, encoded = imeta.thumbnail.split(",", 1) | |
data = base64.b64decode(encoded) | |
thumbnail = Image.open(io.BytesIO(data)) | |
imeta.thumbnail = None | |
result = { | |
in_file_func: gr.File(visible=False, value=None), | |
out_thumb_func: gr.Image(visible=True, value=thumbnail), | |
out_iscc_func: imeta.iscc, | |
out_meta_func: imeta.json(exclude_unset=False, by_alias=True, indent=2), | |
} | |
return result | |
def iscc_compare(iscc_a, iscc_b): | |
# type: (str, str) -> dict | None | |
"""Compare two ISCCs""" | |
if not all([iscc_a, iscc_b]): | |
return None | |
dist_data = ic.iscc_compare(iscc_a, iscc_b) | |
sim_data = dist_to_sim(dist_data, dim=64) | |
sim_plot = similarity_plot(sim_data) | |
return sim_plot | |
# Events | |
in_file_a.change( | |
lambda file: process_upload(file, "a"), | |
inputs=[in_file_a], | |
outputs=[in_file_a, out_thumb_a, out_iscc_a, out_meta_a], | |
show_progress="full", | |
) | |
in_file_b.change( | |
lambda file: process_upload(file, "b"), | |
inputs=[in_file_b], | |
outputs=[in_file_b, out_thumb_b, out_iscc_b, out_meta_b], | |
show_progress="full", | |
) | |
out_thumb_a.clear( | |
lambda: (gr.File(visible=True), gr.Image(visible=False), "", ""), | |
inputs=[], | |
outputs=[in_file_a, out_thumb_a, out_iscc_a, out_meta_a], | |
show_progress="hidden", | |
) | |
out_thumb_b.clear( | |
lambda: (gr.File(visible=True), gr.Image(visible=False), "", ""), | |
inputs=[], | |
outputs=[in_file_b, out_thumb_b, out_iscc_b, out_meta_b], | |
show_progress="hidden", | |
) | |
out_iscc_a.change( | |
iscc_compare, | |
inputs=[out_iscc_a, out_iscc_b], | |
outputs=[out_compare], | |
show_progress="hidden", | |
) | |
out_iscc_b.change( | |
iscc_compare, | |
inputs=[out_iscc_a, out_iscc_b], | |
outputs=[out_compare], | |
show_progress="hidden", | |
) | |
dumy_image_a.change( | |
lambda file: rewrite_uri(file, "images1"), | |
inputs=[dumy_image_a], | |
outputs=[in_file_a], | |
show_progress="hidden", | |
) | |
dumy_image_b.change( | |
lambda file: rewrite_uri(file, "images2"), | |
inputs=[dumy_image_b], | |
outputs=[in_file_b], | |
show_progress="hidden", | |
) | |
if __name__ == "__main__": | |
demo.launch(debug=True) | |