Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- app.py +215 -215
- src/demo/app.py +3 -3
app.py
CHANGED
@@ -1,216 +1,216 @@
|
|
1 |
-
from dataclasses import dataclass, field
|
2 |
-
from typing import List, Any
|
3 |
-
import gradio as gr
|
4 |
-
from gradio_imagemeta import ImageMeta
|
5 |
-
from gradio_imagemeta.helpers import extract_metadata, add_metadata, transfer_metadata
|
6 |
-
from gradio_propertysheet import PropertySheet
|
7 |
-
from gradio_propertysheet.helpers import build_dataclass_fields, create_dataclass_instance
|
8 |
-
from pathlib import Path
|
9 |
-
|
10 |
-
output_dir = Path("outputs")
|
11 |
-
output_dir.mkdir(exist_ok=True)
|
12 |
-
|
13 |
-
@dataclass
|
14 |
-
class ImageSettings:
|
15 |
-
"""Configuration for image metadata settings."""
|
16 |
-
model: str = field(default="", metadata={"label": "Model"})
|
17 |
-
f_number: str = field(default="", metadata={"label": "FNumber"})
|
18 |
-
iso_speed_ratings: str = field(default="", metadata={"label": "ISOSpeedRatings"})
|
19 |
-
s_churn: float = field(
|
20 |
-
default=0.0,
|
21 |
-
metadata={"component": "slider", "label": "Schurn", "minimum": 0.0, "maximum": 1.0, "step": 0.01},
|
22 |
-
)
|
23 |
-
|
24 |
-
@dataclass
|
25 |
-
class PropertyConfig:
|
26 |
-
"""Root configuration for image properties, including nested image settings."""
|
27 |
-
image_settings: ImageSettings = field(default_factory=ImageSettings)
|
28 |
-
description: str = field(default="", metadata={"label": "Description"})
|
29 |
-
|
30 |
-
def process_example_images(img_custom_path: str, img_all_path: str) -> tuple[str, str]:
|
31 |
-
"""
|
32 |
-
Processes example image paths for display in ImageMeta components.
|
33 |
-
|
34 |
-
Args:
|
35 |
-
img_custom_path: File path for the image to display in img_custom.
|
36 |
-
img_all_path: File path for the image to display in img_all.
|
37 |
-
|
38 |
-
Returns:
|
39 |
-
Tuple of file paths for img_custom and img_all outputs.
|
40 |
-
"""
|
41 |
-
# Verify file existence
|
42 |
-
if not Path(img_custom_path.path).is_file():
|
43 |
-
raise FileNotFoundError(f"Image not found: {img_custom_path}")
|
44 |
-
if not Path(img_all_path.path).is_file():
|
45 |
-
raise FileNotFoundError(f"Image not found: {img_all_path}")
|
46 |
-
|
47 |
-
# Return file paths as strings (ImageMeta accepts file paths as input)
|
48 |
-
return img_custom_path.path, img_all_path.path
|
49 |
-
|
50 |
-
def handle_load_metadata(image_data: ImageMeta | None) -> List[Any]:
|
51 |
-
"""
|
52 |
-
Processes image metadata and maps it to output components.
|
53 |
-
|
54 |
-
Args:
|
55 |
-
image_data: ImageMeta object containing image data and metadata, or None.
|
56 |
-
|
57 |
-
Returns:
|
58 |
-
A list of values for output components (Textbox, Slider, or PropertySheet instances).
|
59 |
-
"""
|
60 |
-
if not image_data:
|
61 |
-
return [gr.Textbox(value="") for _ in output_fields]
|
62 |
-
|
63 |
-
metadata = extract_metadata(image_data, only_custom_metadata=True)
|
64 |
-
dataclass_fields = build_dataclass_fields(PropertyConfig)
|
65 |
-
raw_values = transfer_metadata(output_fields, metadata, dataclass_fields)
|
66 |
-
|
67 |
-
output_values = [gr.skip()] * len(output_fields)
|
68 |
-
for i, (component, value) in enumerate(zip(output_fields, raw_values)):
|
69 |
-
if hasattr(component, 'root_label'):
|
70 |
-
output_values[i] = create_dataclass_instance(PropertyConfig, value)
|
71 |
-
else:
|
72 |
-
output_values[i] = gr.Textbox(value=value)
|
73 |
-
|
74 |
-
return output_values
|
75 |
-
|
76 |
-
def save_image_with_metadata(image_data: Any, *inputs: Any) -> str | None:
|
77 |
-
"""
|
78 |
-
Saves an image with updated metadata to a file.
|
79 |
-
|
80 |
-
Args:
|
81 |
-
image_data: Input image data (e.g., file path or PIL Image).
|
82 |
-
*inputs: Variable number of input values from UI components (Textbox, Slider).
|
83 |
-
|
84 |
-
Returns:
|
85 |
-
The file path of the saved image, or None if no image is provided.
|
86 |
-
"""
|
87 |
-
if not image_data:
|
88 |
-
return None
|
89 |
-
|
90 |
-
params = list(inputs)
|
91 |
-
image_params = dict(zip(input_fields.keys(), params))
|
92 |
-
dataclass_fields = build_dataclass_fields(PropertyConfig)
|
93 |
-
metadata = {label: image_params.get(label, "") for label in dataclass_fields.keys()}
|
94 |
-
|
95 |
-
new_filepath = output_dir / "image_with_meta.png"
|
96 |
-
add_metadata(image_data, metadata, new_filepath)
|
97 |
-
|
98 |
-
return str(new_filepath)
|
99 |
-
|
100 |
-
initial_property_from_meta_config = PropertyConfig()
|
101 |
-
|
102 |
-
with gr.Blocks() as demo:
|
103 |
-
gr.Markdown("# ImageMeta Component Demo")
|
104 |
-
gr.Markdown(
|
105 |
-
"""
|
106 |
-
**To Test:**
|
107 |
-
1. Upload an image with EXIF or PNG metadata using either the "Upload Imagem (Custom metadata only)" component or the "Upload Imagem (all metadata)" component.
|
108 |
-
2. Click the 'Info' icon (ⓘ) in the top-left of the image component to view the metadata panel.
|
109 |
-
3. Click 'Load Metadata' in the popup to populate the fields below with metadata values (`Model`, `FNumber`, `ISOSpeedRatings`, `Schurn`, `Description`).
|
110 |
-
4. The section below displays how metadata is rendered in components and the `PropertySheet` custom component, showing the hierarchical structure of the image settings.
|
111 |
-
5. In the "Metadata Viewer" section, you can add field values as metadata to a previously uploaded image in "Upload Image (Custom metadata only)." Then click 'Add metadata and save image' to save a new image with the metadata.
|
112 |
-
"""
|
113 |
-
)
|
114 |
-
property_sheet_state = gr.State(value=initial_property_from_meta_config)
|
115 |
-
with gr.Row():
|
116 |
-
img_custom = ImageMeta(
|
117 |
-
label="Upload Image (Custom metadata only)",
|
118 |
-
type="filepath",
|
119 |
-
width=300,
|
120 |
-
height=400,
|
121 |
-
interactive=
|
122 |
-
)
|
123 |
-
img_all = ImageMeta(
|
124 |
-
label="Upload Image (All metadata)",
|
125 |
-
only_custom_metadata=False,
|
126 |
-
type="filepath",
|
127 |
-
width=300,
|
128 |
-
height=400,
|
129 |
-
popup_metadata_height=400,
|
130 |
-
popup_metadata_width=500,
|
131 |
-
interactive=
|
132 |
-
)
|
133 |
-
|
134 |
-
gr.Markdown("## Metadata Viewer")
|
135 |
-
gr.Markdown("### Individual Components")
|
136 |
-
with gr.Row():
|
137 |
-
model_box = gr.Textbox(label="Model")
|
138 |
-
fnumber_box = gr.Textbox(label="FNumber")
|
139 |
-
iso_box = gr.Textbox(label="ISOSpeedRatings")
|
140 |
-
s_churn = gr.Slider(label="Schurn", value=1.0, minimum=0.0, maximum=1.0, step=0.1)
|
141 |
-
description_box = gr.Textbox(label="Description")
|
142 |
-
|
143 |
-
gr.Markdown("### PropertySheet Component")
|
144 |
-
with gr.Row():
|
145 |
-
property_sheet = PropertySheet(
|
146 |
-
value=initial_property_from_meta_config,
|
147 |
-
label="Image Settings",
|
148 |
-
width=400,
|
149 |
-
height=550,
|
150 |
-
visible=True,
|
151 |
-
root_label="General"
|
152 |
-
)
|
153 |
-
gr.Markdown("## Metadata Editor")
|
154 |
-
with gr.Row():
|
155 |
-
save_button = gr.Button("Add Metadata and Save Image")
|
156 |
-
saved_file_output = gr.File(label="Download Image")
|
157 |
-
|
158 |
-
with gr.Row():
|
159 |
-
gr.Examples(
|
160 |
-
examples=[
|
161 |
-
["./src/examples/image_with_meta.png", "./src/examples/image_with_meta.png"]
|
162 |
-
],
|
163 |
-
fn=process_example_images,
|
164 |
-
inputs=[img_custom, img_all],
|
165 |
-
outputs=[img_custom, img_all],
|
166 |
-
cache_examples=True
|
167 |
-
)
|
168 |
-
|
169 |
-
input_fields = {
|
170 |
-
"Model": model_box,
|
171 |
-
"FNumber": fnumber_box,
|
172 |
-
"ISOSpeedRatings": iso_box,
|
173 |
-
"Schurn": s_churn,
|
174 |
-
"Description": description_box
|
175 |
-
}
|
176 |
-
|
177 |
-
output_fields = [
|
178 |
-
property_sheet,
|
179 |
-
model_box,
|
180 |
-
fnumber_box,
|
181 |
-
iso_box,
|
182 |
-
s_churn,
|
183 |
-
description_box
|
184 |
-
]
|
185 |
-
|
186 |
-
img_custom.load_metadata(handle_load_metadata, inputs=img_custom, outputs=output_fields)
|
187 |
-
img_all.load_metadata(handle_load_metadata, inputs=img_all, outputs=output_fields)
|
188 |
-
|
189 |
-
def handle_render_change(updated_config: PropertyConfig, current_state: PropertyConfig):
|
190 |
-
"""
|
191 |
-
Updates the PropertySheet state when its configuration changes.
|
192 |
-
|
193 |
-
Args:
|
194 |
-
updated_config: The new PropertyConfig instance from the PropertySheet.
|
195 |
-
current_state: The current PropertyConfig state.
|
196 |
-
|
197 |
-
Returns:
|
198 |
-
A tuple of (updated_config, updated_config) or (current_state, current_state) if updated_config is None.
|
199 |
-
"""
|
200 |
-
if updated_config is None:
|
201 |
-
return current_state, current_state
|
202 |
-
return updated_config, updated_config
|
203 |
-
|
204 |
-
property_sheet.change(
|
205 |
-
fn=handle_render_change,
|
206 |
-
inputs=[property_sheet, property_sheet_state],
|
207 |
-
outputs=[property_sheet, property_sheet_state]
|
208 |
-
)
|
209 |
-
save_button.click(
|
210 |
-
save_image_with_metadata,
|
211 |
-
inputs=[img_custom, *input_fields.values()],
|
212 |
-
outputs=[saved_file_output]
|
213 |
-
)
|
214 |
-
|
215 |
-
if __name__ == "__main__":
|
216 |
demo.launch()
|
|
|
1 |
+
from dataclasses import dataclass, field
|
2 |
+
from typing import List, Any
|
3 |
+
import gradio as gr
|
4 |
+
from gradio_imagemeta import ImageMeta
|
5 |
+
from gradio_imagemeta.helpers import extract_metadata, add_metadata, transfer_metadata
|
6 |
+
from gradio_propertysheet import PropertySheet
|
7 |
+
from gradio_propertysheet.helpers import build_dataclass_fields, create_dataclass_instance
|
8 |
+
from pathlib import Path
|
9 |
+
|
10 |
+
output_dir = Path("outputs")
|
11 |
+
output_dir.mkdir(exist_ok=True)
|
12 |
+
|
13 |
+
@dataclass
|
14 |
+
class ImageSettings:
|
15 |
+
"""Configuration for image metadata settings."""
|
16 |
+
model: str = field(default="", metadata={"label": "Model"})
|
17 |
+
f_number: str = field(default="", metadata={"label": "FNumber"})
|
18 |
+
iso_speed_ratings: str = field(default="", metadata={"label": "ISOSpeedRatings"})
|
19 |
+
s_churn: float = field(
|
20 |
+
default=0.0,
|
21 |
+
metadata={"component": "slider", "label": "Schurn", "minimum": 0.0, "maximum": 1.0, "step": 0.01},
|
22 |
+
)
|
23 |
+
|
24 |
+
@dataclass
|
25 |
+
class PropertyConfig:
|
26 |
+
"""Root configuration for image properties, including nested image settings."""
|
27 |
+
image_settings: ImageSettings = field(default_factory=ImageSettings)
|
28 |
+
description: str = field(default="", metadata={"label": "Description"})
|
29 |
+
|
30 |
+
def process_example_images(img_custom_path: str, img_all_path: str) -> tuple[str, str]:
|
31 |
+
"""
|
32 |
+
Processes example image paths for display in ImageMeta components.
|
33 |
+
|
34 |
+
Args:
|
35 |
+
img_custom_path: File path for the image to display in img_custom.
|
36 |
+
img_all_path: File path for the image to display in img_all.
|
37 |
+
|
38 |
+
Returns:
|
39 |
+
Tuple of file paths for img_custom and img_all outputs.
|
40 |
+
"""
|
41 |
+
# Verify file existence
|
42 |
+
if not Path(img_custom_path.path).is_file():
|
43 |
+
raise FileNotFoundError(f"Image not found: {img_custom_path}")
|
44 |
+
if not Path(img_all_path.path).is_file():
|
45 |
+
raise FileNotFoundError(f"Image not found: {img_all_path}")
|
46 |
+
|
47 |
+
# Return file paths as strings (ImageMeta accepts file paths as input)
|
48 |
+
return img_custom_path.path, img_all_path.path
|
49 |
+
|
50 |
+
def handle_load_metadata(image_data: ImageMeta | None) -> List[Any]:
|
51 |
+
"""
|
52 |
+
Processes image metadata and maps it to output components.
|
53 |
+
|
54 |
+
Args:
|
55 |
+
image_data: ImageMeta object containing image data and metadata, or None.
|
56 |
+
|
57 |
+
Returns:
|
58 |
+
A list of values for output components (Textbox, Slider, or PropertySheet instances).
|
59 |
+
"""
|
60 |
+
if not image_data:
|
61 |
+
return [gr.Textbox(value="") for _ in output_fields]
|
62 |
+
|
63 |
+
metadata = extract_metadata(image_data, only_custom_metadata=True)
|
64 |
+
dataclass_fields = build_dataclass_fields(PropertyConfig)
|
65 |
+
raw_values = transfer_metadata(output_fields, metadata, dataclass_fields)
|
66 |
+
|
67 |
+
output_values = [gr.skip()] * len(output_fields)
|
68 |
+
for i, (component, value) in enumerate(zip(output_fields, raw_values)):
|
69 |
+
if hasattr(component, 'root_label'):
|
70 |
+
output_values[i] = create_dataclass_instance(PropertyConfig, value)
|
71 |
+
else:
|
72 |
+
output_values[i] = gr.Textbox(value=value)
|
73 |
+
|
74 |
+
return output_values
|
75 |
+
|
76 |
+
def save_image_with_metadata(image_data: Any, *inputs: Any) -> str | None:
|
77 |
+
"""
|
78 |
+
Saves an image with updated metadata to a file.
|
79 |
+
|
80 |
+
Args:
|
81 |
+
image_data: Input image data (e.g., file path or PIL Image).
|
82 |
+
*inputs: Variable number of input values from UI components (Textbox, Slider).
|
83 |
+
|
84 |
+
Returns:
|
85 |
+
The file path of the saved image, or None if no image is provided.
|
86 |
+
"""
|
87 |
+
if not image_data:
|
88 |
+
return None
|
89 |
+
|
90 |
+
params = list(inputs)
|
91 |
+
image_params = dict(zip(input_fields.keys(), params))
|
92 |
+
dataclass_fields = build_dataclass_fields(PropertyConfig)
|
93 |
+
metadata = {label: image_params.get(label, "") for label in dataclass_fields.keys()}
|
94 |
+
|
95 |
+
new_filepath = output_dir / "image_with_meta.png"
|
96 |
+
add_metadata(image_data, metadata, new_filepath)
|
97 |
+
|
98 |
+
return str(new_filepath)
|
99 |
+
|
100 |
+
initial_property_from_meta_config = PropertyConfig()
|
101 |
+
|
102 |
+
with gr.Blocks() as demo:
|
103 |
+
gr.Markdown("# ImageMeta Component Demo")
|
104 |
+
gr.Markdown(
|
105 |
+
"""
|
106 |
+
**To Test:**
|
107 |
+
1. Upload an image with EXIF or PNG metadata using either the "Upload Imagem (Custom metadata only)" component or the "Upload Imagem (all metadata)" component.
|
108 |
+
2. Click the 'Info' icon (ⓘ) in the top-left of the image component to view the metadata panel.
|
109 |
+
3. Click 'Load Metadata' in the popup to populate the fields below with metadata values (`Model`, `FNumber`, `ISOSpeedRatings`, `Schurn`, `Description`).
|
110 |
+
4. The section below displays how metadata is rendered in components and the `PropertySheet` custom component, showing the hierarchical structure of the image settings.
|
111 |
+
5. In the "Metadata Viewer" section, you can add field values as metadata to a previously uploaded image in "Upload Image (Custom metadata only)." Then click 'Add metadata and save image' to save a new image with the metadata.
|
112 |
+
"""
|
113 |
+
)
|
114 |
+
property_sheet_state = gr.State(value=initial_property_from_meta_config)
|
115 |
+
with gr.Row():
|
116 |
+
img_custom = ImageMeta(
|
117 |
+
label="Upload Image (Custom metadata only)",
|
118 |
+
type="filepath",
|
119 |
+
width=300,
|
120 |
+
height=400,
|
121 |
+
interactive=True
|
122 |
+
)
|
123 |
+
img_all = ImageMeta(
|
124 |
+
label="Upload Image (All metadata)",
|
125 |
+
only_custom_metadata=False,
|
126 |
+
type="filepath",
|
127 |
+
width=300,
|
128 |
+
height=400,
|
129 |
+
popup_metadata_height=400,
|
130 |
+
popup_metadata_width=500,
|
131 |
+
interactive=True
|
132 |
+
)
|
133 |
+
|
134 |
+
gr.Markdown("## Metadata Viewer")
|
135 |
+
gr.Markdown("### Individual Components")
|
136 |
+
with gr.Row():
|
137 |
+
model_box = gr.Textbox(label="Model")
|
138 |
+
fnumber_box = gr.Textbox(label="FNumber")
|
139 |
+
iso_box = gr.Textbox(label="ISOSpeedRatings")
|
140 |
+
s_churn = gr.Slider(label="Schurn", value=1.0, minimum=0.0, maximum=1.0, step=0.1)
|
141 |
+
description_box = gr.Textbox(label="Description")
|
142 |
+
|
143 |
+
gr.Markdown("### PropertySheet Component")
|
144 |
+
with gr.Row():
|
145 |
+
property_sheet = PropertySheet(
|
146 |
+
value=initial_property_from_meta_config,
|
147 |
+
label="Image Settings",
|
148 |
+
width=400,
|
149 |
+
height=550,
|
150 |
+
visible=True,
|
151 |
+
root_label="General"
|
152 |
+
)
|
153 |
+
gr.Markdown("## Metadata Editor")
|
154 |
+
with gr.Row():
|
155 |
+
save_button = gr.Button("Add Metadata and Save Image")
|
156 |
+
saved_file_output = gr.File(label="Download Image")
|
157 |
+
|
158 |
+
with gr.Row():
|
159 |
+
gr.Examples(
|
160 |
+
examples=[
|
161 |
+
["./src/examples/image_with_meta.png", "./src/examples/image_with_meta.png"]
|
162 |
+
],
|
163 |
+
fn=process_example_images,
|
164 |
+
inputs=[img_custom, img_all],
|
165 |
+
outputs=[img_custom, img_all],
|
166 |
+
cache_examples=True
|
167 |
+
)
|
168 |
+
|
169 |
+
input_fields = {
|
170 |
+
"Model": model_box,
|
171 |
+
"FNumber": fnumber_box,
|
172 |
+
"ISOSpeedRatings": iso_box,
|
173 |
+
"Schurn": s_churn,
|
174 |
+
"Description": description_box
|
175 |
+
}
|
176 |
+
|
177 |
+
output_fields = [
|
178 |
+
property_sheet,
|
179 |
+
model_box,
|
180 |
+
fnumber_box,
|
181 |
+
iso_box,
|
182 |
+
s_churn,
|
183 |
+
description_box
|
184 |
+
]
|
185 |
+
|
186 |
+
img_custom.load_metadata(handle_load_metadata, inputs=img_custom, outputs=output_fields)
|
187 |
+
img_all.load_metadata(handle_load_metadata, inputs=img_all, outputs=output_fields)
|
188 |
+
|
189 |
+
def handle_render_change(updated_config: PropertyConfig, current_state: PropertyConfig):
|
190 |
+
"""
|
191 |
+
Updates the PropertySheet state when its configuration changes.
|
192 |
+
|
193 |
+
Args:
|
194 |
+
updated_config: The new PropertyConfig instance from the PropertySheet.
|
195 |
+
current_state: The current PropertyConfig state.
|
196 |
+
|
197 |
+
Returns:
|
198 |
+
A tuple of (updated_config, updated_config) or (current_state, current_state) if updated_config is None.
|
199 |
+
"""
|
200 |
+
if updated_config is None:
|
201 |
+
return current_state, current_state
|
202 |
+
return updated_config, updated_config
|
203 |
+
|
204 |
+
property_sheet.change(
|
205 |
+
fn=handle_render_change,
|
206 |
+
inputs=[property_sheet, property_sheet_state],
|
207 |
+
outputs=[property_sheet, property_sheet_state]
|
208 |
+
)
|
209 |
+
save_button.click(
|
210 |
+
save_image_with_metadata,
|
211 |
+
inputs=[img_custom, *input_fields.values()],
|
212 |
+
outputs=[saved_file_output]
|
213 |
+
)
|
214 |
+
|
215 |
+
if __name__ == "__main__":
|
216 |
demo.launch()
|
src/demo/app.py
CHANGED
@@ -118,7 +118,7 @@ with gr.Blocks() as demo:
|
|
118 |
type="filepath",
|
119 |
width=300,
|
120 |
height=400,
|
121 |
-
interactive=
|
122 |
)
|
123 |
img_all = ImageMeta(
|
124 |
label="Upload Image (All metadata)",
|
@@ -128,7 +128,7 @@ with gr.Blocks() as demo:
|
|
128 |
height=400,
|
129 |
popup_metadata_height=400,
|
130 |
popup_metadata_width=500,
|
131 |
-
interactive=
|
132 |
)
|
133 |
|
134 |
gr.Markdown("## Metadata Viewer")
|
@@ -158,7 +158,7 @@ with gr.Blocks() as demo:
|
|
158 |
with gr.Row():
|
159 |
gr.Examples(
|
160 |
examples=[
|
161 |
-
["./examples/image_with_meta.png", "./examples/image_with_meta.png"]
|
162 |
],
|
163 |
fn=process_example_images,
|
164 |
inputs=[img_custom, img_all],
|
|
|
118 |
type="filepath",
|
119 |
width=300,
|
120 |
height=400,
|
121 |
+
interactive=True
|
122 |
)
|
123 |
img_all = ImageMeta(
|
124 |
label="Upload Image (All metadata)",
|
|
|
128 |
height=400,
|
129 |
popup_metadata_height=400,
|
130 |
popup_metadata_width=500,
|
131 |
+
interactive=True
|
132 |
)
|
133 |
|
134 |
gr.Markdown("## Metadata Viewer")
|
|
|
158 |
with gr.Row():
|
159 |
gr.Examples(
|
160 |
examples=[
|
161 |
+
["./src/examples/image_with_meta.png", "./src/examples/image_with_meta.png"]
|
162 |
],
|
163 |
fn=process_example_images,
|
164 |
inputs=[img_custom, img_all],
|