umuth commited on
Commit
a09fcbd
·
verified ·
1 Parent(s): a89b2ec

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +308 -0
app.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from PIL import Image, ExifTags
3
+ import pandas as pd
4
+ import os
5
+ import piexif
6
+ import numpy as np
7
+ import io
8
+ import logging
9
+ import shutil
10
+ import datetime
11
+ from PIL import PngImagePlugin
12
+
13
+ logging.basicConfig(filename='metadata_update.log', level=logging.INFO,
14
+ format='%(asctime)s - %(levelname)s - %(message)s')
15
+ logger = logging.getLogger(__name__)
16
+
17
+ SUPPORTED_FORMATS = ('.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp') # Add formats as needed
18
+
19
+ def extract_metadata(uploaded_files, progress=gr.Progress()):
20
+ outputs = []
21
+ num_files = min(len(uploaded_files), 100) # Limit to 100 images
22
+ for idx in range(100):
23
+ if idx < num_files:
24
+ image_path = uploaded_files[idx]
25
+ filename = os.path.basename(image_path)
26
+
27
+ # Check if the file is in a supported format
28
+ if not image_path.lower().endswith(SUPPORTED_FORMATS):
29
+ outputs.extend([None, image_path, filename, "Unsupported format", "", ""])
30
+ continue # Skip unsupported formats
31
+
32
+ try:
33
+ img = Image.open(image_path)
34
+ img.thumbnail((600, 600)) # Adjusted thumbnail size
35
+
36
+ if img.format == 'PNG':
37
+ title = img.info.get('Title', '')
38
+ description = img.info.get('Description', '')
39
+ keywords = img.info.get('Keywords', '')
40
+ else:
41
+ exif_data = img.getexif() if img else {}
42
+ exif = {}
43
+ for tag_id in exif_data:
44
+ tag = ExifTags.TAGS.get(tag_id, tag_id)
45
+ data_value = exif_data.get(tag_id)
46
+ if isinstance(data_value, bytes):
47
+ try:
48
+ data_value = data_value.decode('utf-16-le').strip('\x00')
49
+ except UnicodeDecodeError:
50
+ data_value = data_value.decode(errors='replace')
51
+ exif[tag] = data_value
52
+
53
+ title = exif.get('XPTitle', '')
54
+ description = exif.get('ImageDescription', '')
55
+ keywords = exif.get('XPKeywords', '')
56
+
57
+ # Decode if still bytes
58
+ if isinstance(title, bytes):
59
+ title = title.decode('utf-16-le').strip('\x00')
60
+ if isinstance(description, bytes):
61
+ description = description.decode('utf-8')
62
+ if isinstance(keywords, bytes):
63
+ keywords = keywords.decode('utf-16-le').strip('\x00')
64
+
65
+ except Exception as e:
66
+ logger.error(f"Error processing {image_path}: {str(e)}")
67
+ img, title, description, keywords = None, '', '', ''
68
+
69
+ outputs.extend([img, image_path, filename, title, description, keywords])
70
+ else:
71
+ outputs.extend([None, '', '', '', '', ''])
72
+ progress((idx + 1) / 100, desc=f"Processing image {idx + 1} of {num_files}")
73
+ return outputs
74
+
75
+ def write_metadata_to_image(image_path, title, description, keywords):
76
+ try:
77
+ logger.info(f"Attempting to write metadata to {image_path}")
78
+
79
+ # Open the image file
80
+ with Image.open(image_path) as img:
81
+ # Get the original format
82
+ original_format = img.format
83
+
84
+ # Prepare EXIF data for JPEG
85
+ if original_format in ['JPEG', 'JPG']:
86
+ exif_dict = piexif.load(img.info.get("exif", b""))
87
+
88
+ # Update EXIF data
89
+ exif_dict['0th'][piexif.ImageIFD.XPTitle] = title.encode('utf-16le')
90
+ exif_dict['0th'][piexif.ImageIFD.ImageDescription] = description.encode('utf-8')
91
+ exif_dict['0th'][piexif.ImageIFD.XPKeywords] = keywords.encode('utf-16le')
92
+
93
+ # Convert EXIF data to bytes
94
+ exif_bytes = piexif.dump(exif_dict)
95
+
96
+ # Save with EXIF data
97
+ img.save(image_path, exif=exif_bytes)
98
+ logger.info(f"Metadata successfully written to {image_path}")
99
+
100
+ # Prepare metadata for PNG
101
+ elif original_format == 'PNG':
102
+ metadata = PngImagePlugin.PngInfo()
103
+ metadata.add_text("Title", title)
104
+ metadata.add_text("Description", description)
105
+ metadata.add_text("Keywords", keywords)
106
+
107
+ # Save the image with metadata
108
+ img.save(image_path, format='PNG', pnginfo=metadata)
109
+ logger.info(f"Metadata successfully written to {image_path}")
110
+
111
+ else:
112
+ logger.error(f"Unsupported format: {original_format}")
113
+ return False
114
+
115
+ return True
116
+ except Exception as e:
117
+ logger.error(f"Error writing metadata to {image_path}: {str(e)}")
118
+ return False
119
+
120
+ def update_image_metadata(*args):
121
+ success_count = 0
122
+ error_count = 0
123
+ image_paths = []
124
+
125
+ # First, extract all image paths
126
+ for i in range(100):
127
+ idx = i * 6
128
+ image_path = args[idx + 1]
129
+ if image_path:
130
+ image_paths.append(image_path)
131
+
132
+ # Copy images to project directory
133
+ copied_paths = copy_images_to_project_dir(image_paths)
134
+
135
+ # Now update metadata for copied images
136
+ for i, copied_path in enumerate(copied_paths):
137
+ if copied_path:
138
+ idx = i * 6
139
+ title = args[idx + 3]
140
+ description = args[idx + 4]
141
+ keywords = args[idx + 5]
142
+ if write_metadata_to_image(copied_path, title, description, keywords):
143
+ success_count += 1
144
+ else:
145
+ error_count += 1
146
+
147
+ return f"Images copied and metadata updated. Successful updates: {success_count}, Failed updates: {error_count}. Check the 'edited_images' folder for results."
148
+
149
+ def copy_images_to_project_dir(image_paths):
150
+ copied_paths = []
151
+ for path in image_paths:
152
+ if path and os.path.isfile(path):
153
+ filename = os.path.basename(path)
154
+ new_path = os.path.join("edited_images", filename)
155
+ os.makedirs("edited_images", exist_ok=True)
156
+ shutil.copy2(path, new_path)
157
+ copied_paths.append(new_path)
158
+ else:
159
+ copied_paths.append(None)
160
+ return copied_paths
161
+
162
+ def save_csv(*args):
163
+ data = []
164
+ for i in range(100):
165
+ idx = i * 6
166
+ img = args[idx] # Not used, but kept for consistency
167
+ image_path = args[idx + 1]
168
+ filename = args[idx + 2]
169
+ title = args[idx + 3]
170
+ description = args[idx + 4]
171
+ keywords = args[idx + 5]
172
+ if filename:
173
+ data.append({
174
+ 'Filename': filename,
175
+ 'Title': title,
176
+ 'Description': description,
177
+ 'Keywords': keywords,
178
+ })
179
+ df = pd.DataFrame(data)
180
+ try:
181
+ # Generate timestamp
182
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
183
+ csv_filename = f'edited_metadata_{timestamp}.csv'
184
+ df.to_csv(csv_filename, index=False)
185
+ return f"Data saved successfully to '{csv_filename}'!"
186
+ except Exception as e:
187
+ return f"Error saving data: {e}"
188
+
189
+ # Functions to update character and keyword counts
190
+ def update_title_info(text):
191
+ return f"{len(text)} characters"
192
+
193
+ def update_description_info(text):
194
+ return f"{len(text)} characters"
195
+
196
+ def update_keywords_info(text):
197
+ keywords = [k.strip() for k in text.split(',') if k.strip()]
198
+ return f"{len(keywords)} keywords"
199
+
200
+ with gr.Blocks(css="""
201
+ .counter {
202
+ font-size: 12px;
203
+ color: #555;
204
+ margin-top: -8px;
205
+ margin-bottom: 8px;
206
+ }
207
+ .metadata-section {
208
+ border: 1px solid #ccc;
209
+ padding: 10px;
210
+ margin-bottom: 10px;
211
+ border-radius: 5px;
212
+ }
213
+ .thumbnail {
214
+ flex-shrink: 0;
215
+ }
216
+ .metadata-fields {
217
+ flex-grow: 1;
218
+ margin-left: 10px;
219
+ }
220
+ .container {
221
+ max-width: 1200px;
222
+ margin: auto;
223
+ }
224
+ """) as demo:
225
+ gr.Markdown("## Image Metadata Editor")
226
+ with gr.Column():
227
+ file_input = gr.File(label="Select Images", file_count="multiple", type="filepath")
228
+ extract_button = gr.Button("Extract Metadata")
229
+
230
+ # Lists to hold component references
231
+ image_components = []
232
+ filename_components = []
233
+ title_components = []
234
+ title_info_components = []
235
+ description_components = []
236
+ description_info_components = []
237
+ keywords_components = []
238
+ keywords_info_components = []
239
+
240
+ for i in range(100):
241
+ with gr.Row():
242
+ # Thumbnail Image
243
+ with gr.Column(scale=1):
244
+ image = gr.Image(label="Thumbnail", interactive=False, width=600, height=600)
245
+ image_components.append(image)
246
+ # Metadata Fields
247
+ with gr.Column(scale=3):
248
+ filename = gr.Textbox(label="Filename", interactive=False, placeholder="Filename")
249
+ filename_components.append(filename)
250
+
251
+ title = gr.Textbox(label="Title", lines=1, placeholder="Enter title")
252
+ title_components.append(title)
253
+ title_info = gr.Text(label="", value="0 characters", interactive=False, elem_classes="counter")
254
+ title_info_components.append(title_info)
255
+
256
+ description = gr.Textbox(label="Description", lines=2, placeholder="Enter description")
257
+ description_components.append(description)
258
+ description_info = gr.Text(label="", value="0 characters", interactive=False, elem_classes="counter")
259
+ description_info_components.append(description_info)
260
+
261
+ keywords = gr.Textbox(label="Keywords", lines=1, placeholder="Enter keywords separated by commas")
262
+ keywords_components.append(keywords)
263
+ keywords_info = gr.Text(label="", value="0 keywords", interactive=False, elem_classes="counter")
264
+ keywords_info_components.append(keywords_info)
265
+
266
+ # Link change events to update counters
267
+ title.change(fn=update_title_info, inputs=title, outputs=title_info)
268
+ description.change(fn=update_description_info, inputs=description, outputs=description_info)
269
+ keywords.change(fn=update_keywords_info, inputs=keywords, outputs=keywords_info)
270
+
271
+ # Save Buttons and Output
272
+ with gr.Row():
273
+ save_metadata_button = gr.Button("Save Metadata to Images")
274
+ save_csv_button = gr.Button("Save CSV")
275
+ save_output = gr.Textbox(label="Save Status", interactive=False)
276
+
277
+ # Collect all output components for extraction and saving
278
+ all_outputs = []
279
+ for i in range(100):
280
+ all_outputs.extend([
281
+ image_components[i],
282
+ gr.Textbox(visible=False), # Hidden textbox to store full image path
283
+ filename_components[i],
284
+ title_components[i],
285
+ description_components[i],
286
+ keywords_components[i]
287
+ ])
288
+
289
+ # Define the interactions
290
+ extract_button.click(
291
+ fn=extract_metadata,
292
+ inputs=file_input,
293
+ outputs=all_outputs
294
+ )
295
+
296
+ save_metadata_button.click(
297
+ fn=update_image_metadata,
298
+ inputs=all_outputs,
299
+ outputs=save_output
300
+ )
301
+
302
+ save_csv_button.click(
303
+ fn=save_csv,
304
+ inputs=all_outputs,
305
+ outputs=save_output
306
+ )
307
+
308
+ demo.launch()