hasan.aldhahi commited on
Commit
568c173
Β·
1 Parent(s): 0124ddb

added my whole app

Browse files
README.md CHANGED
@@ -1,12 +1,13 @@
1
  ---
2
- title: Layout
3
- emoji: πŸ‘
4
  colorFrom: yellow
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 5.47.0
8
  app_file: app.py
9
  pinned: false
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: Medieval Yolo
3
+ emoji: 🐠
4
  colorFrom: yellow
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 5.23.1
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,821 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Dict, List, Union
2
+ import gradio as gr
3
+ import supervision as sv
4
+ import numpy as np
5
+ from PIL import Image, ImageDraw, ImageFont
6
+ from ultralytics import YOLO
7
+ import zipfile
8
+ import os
9
+ import tempfile
10
+ import cv2
11
+ import json
12
+ from datetime import datetime
13
+ import io
14
+
15
+ # Define custom models
16
+ MODEL_FILES = {
17
+ "Line Detection": "best_line_detection_yoloe (1).pt",
18
+ "Border Detection": "border_model_weights.pt",
19
+ "Zones Detection": "zones_model_weights.pt"
20
+ }
21
+
22
+ # Dictionary to store loaded models
23
+ models: Dict[str, YOLO] = {}
24
+
25
+ # Global variables to store results for download
26
+ current_results = []
27
+ current_images = []
28
+
29
+ # Load all custom models
30
+ for name, model_file in MODEL_FILES.items():
31
+ model_path = os.path.join(os.getcwd(), model_file)
32
+ if os.path.exists(model_path):
33
+ try:
34
+ models[name] = YOLO(model_path)
35
+ print(f"βœ“ Loaded {name} model from {model_path}")
36
+ except Exception as e:
37
+ print(f"βœ— Error loading {name} model: {e}")
38
+ models[name] = None
39
+ else:
40
+ print(f"βœ— Warning: Model file {model_path} not found")
41
+ models[name] = None
42
+
43
+ # Create annotators
44
+ LABEL_ANNOTATOR = sv.LabelAnnotator(text_color=sv.Color.BLACK)
45
+ BOX_ANNOTATOR = sv.BoxAnnotator()
46
+
47
+ def detect_and_annotate_combined(
48
+ image: np.ndarray,
49
+ conf_threshold: float,
50
+ iou_threshold: float,
51
+ return_annotations: bool = False
52
+ ) -> Union[np.ndarray, Tuple[np.ndarray, Dict]]:
53
+ """Run all three models and combine their outputs in a single annotated image"""
54
+ print(f"πŸ” Starting detection on image shape: {image.shape}")
55
+
56
+ # Colors for different models - more distinct colors
57
+ colors = {
58
+ "Line Detection": sv.Color.from_hex("#FF0000"), # Bright Red
59
+ "Border Detection": sv.Color.from_hex("#00FF00"), # Bright Green
60
+ "Zones Detection": sv.Color.from_hex("#0080FF") # Bright Blue
61
+ }
62
+
63
+ # Model prefixes for clear labeling
64
+ model_prefixes = {
65
+ "Line Detection": "[LINE]",
66
+ "Border Detection": "[BORDER]",
67
+ "Zones Detection": "[ZONE]"
68
+ }
69
+
70
+ annotated_image = image.copy()
71
+ total_detections = 0
72
+ detections_data = {}
73
+
74
+ # Run each model and annotate with different colors
75
+ for model_name, model in models.items():
76
+ if model is None:
77
+ print(f"⏭️ Skipping {model_name} (model not loaded)")
78
+ detections_data[model_name] = []
79
+ continue
80
+
81
+ print(f"πŸ€– Running {model_name} model...")
82
+
83
+ # Perform inference
84
+ results = model.predict(
85
+ image,
86
+ conf=conf_threshold,
87
+ iou=iou_threshold
88
+ )[0]
89
+
90
+ model_detections = []
91
+
92
+ if len(results.boxes) > 0:
93
+ print(f" Found {len(results.boxes)} detections")
94
+ total_detections += len(results.boxes)
95
+
96
+ # Convert results to supervision Detections
97
+ boxes = results.boxes.xyxy.cpu().numpy()
98
+ confidence = results.boxes.conf.cpu().numpy()
99
+ class_ids = results.boxes.cls.cpu().numpy().astype(int)
100
+
101
+ # Store detection data for COCO format
102
+ for i, (box, conf, class_id) in enumerate(zip(boxes, confidence, class_ids)):
103
+ x1, y1, x2, y2 = box
104
+ width = x2 - x1
105
+ height = y2 - y1
106
+
107
+ model_detections.append({
108
+ "bbox": [float(x1), float(y1), float(width), float(height)], # COCO format: [x, y, width, height]
109
+ "class_name": results.names[class_id],
110
+ "confidence": float(conf)
111
+ })
112
+
113
+ # Create Detections object for visualization
114
+ detections = sv.Detections(
115
+ xyxy=boxes,
116
+ confidence=confidence,
117
+ class_id=class_ids
118
+ )
119
+
120
+ # Create labels with clear model prefixes and confidence scores
121
+ model_prefix = model_prefixes[model_name]
122
+ labels = [
123
+ f"{model_prefix} {results.names[class_id]} ({conf:.2f})"
124
+ for class_id, conf
125
+ in zip(class_ids, confidence)
126
+ ]
127
+
128
+ # Create annotators with specific colors and improved styling
129
+ box_annotator = sv.BoxAnnotator(
130
+ color=colors[model_name],
131
+ thickness=3 # Thicker boxes for better visibility
132
+ )
133
+ label_annotator = sv.LabelAnnotator(
134
+ text_color=sv.Color.WHITE,
135
+ color=colors[model_name],
136
+ text_thickness=2,
137
+ text_scale=0.6,
138
+ text_padding=8
139
+ )
140
+
141
+ # Annotate image
142
+ annotated_image = box_annotator.annotate(scene=annotated_image, detections=detections)
143
+ annotated_image = label_annotator.annotate(scene=annotated_image, detections=detections, labels=labels)
144
+ print(f" βœ… Annotated with {len(boxes)} {model_name} detections")
145
+ else:
146
+ print(f" No detections found for {model_name}")
147
+
148
+ detections_data[model_name] = model_detections
149
+
150
+ print(f"🎯 Detection completed. Total detections: {total_detections}")
151
+
152
+ if return_annotations:
153
+ return annotated_image, detections_data
154
+ else:
155
+ return annotated_image
156
+
157
+ def process_zip_file(zip_file_path: str, conf_threshold: float, iou_threshold: float) -> Tuple[List[Tuple[str, np.ndarray]], List[Tuple[str, Dict]], Dict]:
158
+ """Process all images in a zip file and return annotated images, detection data, and image info"""
159
+ print(f"πŸ“ Opening ZIP file: {zip_file_path}")
160
+ results = []
161
+ annotations_data = []
162
+ image_info = {}
163
+
164
+ try:
165
+ with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
166
+ print(f"πŸ“‹ ZIP file contents: {zip_ref.namelist()}")
167
+
168
+ # Create temporary directory to extract files
169
+ with tempfile.TemporaryDirectory() as temp_dir:
170
+ print(f"πŸ“‚ Extracting to temporary directory: {temp_dir}")
171
+ zip_ref.extractall(temp_dir)
172
+
173
+ # List all files in temp directory
174
+ all_files = os.listdir(temp_dir)
175
+ print(f"πŸ“„ Files extracted: {all_files}")
176
+
177
+ # Process each image file (recursively search through folders)
178
+ image_count = 0
179
+
180
+ # Walk through all directories and subdirectories
181
+ for root, dirs, files in os.walk(temp_dir):
182
+ print(f"πŸ“‚ Searching in directory: {root}")
183
+
184
+ for filename in files:
185
+ # Skip macOS hidden files
186
+ if filename.startswith('._') or filename.startswith('.DS_Store'):
187
+ print(f"⏭️ Skipping system file: {filename}")
188
+ continue
189
+
190
+ if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tiff')):
191
+ image_count += 1
192
+ image_path = os.path.join(root, filename)
193
+ print(f"πŸ–ΌοΈ Processing image {image_count}: {filename} (from {os.path.relpath(root, temp_dir)})")
194
+
195
+ # Load image
196
+ image = cv2.imread(image_path)
197
+ if image is not None:
198
+ print(f"βœ… Image loaded successfully: {image.shape}")
199
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
200
+
201
+ # Store image info
202
+ height, width = image.shape[:2]
203
+ image_info[filename] = (height, width)
204
+
205
+ # Process with all models and get annotation data
206
+ print(f"πŸ” Running detection models on {filename}...")
207
+ annotated_image, detections_data = detect_and_annotate_combined(
208
+ image, conf_threshold, iou_threshold, return_annotations=True
209
+ )
210
+ print(f"βœ… Detection completed for {filename}")
211
+
212
+ results.append((filename, annotated_image))
213
+ annotations_data.append((filename, detections_data))
214
+ else:
215
+ print(f"❌ Failed to load image: {filename}")
216
+ else:
217
+ print(f"⏭️ Skipping non-image file: {filename}")
218
+
219
+ print(f"πŸ“Š Total images processed: {len(results)} out of {image_count} image files found")
220
+ print(f"πŸ“ Searched through all subdirectories recursively")
221
+
222
+ print(f"πŸŽ‰ ZIP processing completed successfully! Processed {len(results)} images")
223
+ return results, annotations_data, image_info
224
+
225
+ except Exception as e:
226
+ print(f"πŸ’₯ ERROR in process_zip_file: {str(e)}")
227
+ import traceback
228
+ traceback.print_exc()
229
+ return [], [], {}
230
+
231
+ def create_coco_annotations(results_data: List, image_info: Dict) -> Dict:
232
+ """Convert detection results to COCO JSON format"""
233
+ coco_data = {
234
+ "info": {
235
+ "description": "Medieval Manuscript Detection Results",
236
+ "version": "1.0",
237
+ "year": datetime.now().year,
238
+ "contributor": "Medieval YOLO Models",
239
+ "date_created": datetime.now().isoformat()
240
+ },
241
+ "licenses": [
242
+ {
243
+ "id": 1,
244
+ "name": "Custom License",
245
+ "url": ""
246
+ }
247
+ ],
248
+ "images": [],
249
+ "annotations": [],
250
+ "categories": []
251
+ }
252
+
253
+ # Create categories from all models
254
+ category_id = 1
255
+ category_map = {}
256
+
257
+ # Add categories for each model type
258
+ for model_name in ["Line Detection", "Border Detection", "Zones Detection"]:
259
+ if model_name in models and models[model_name] is not None:
260
+ model = models[model_name]
261
+ for class_id, class_name in model.names.items():
262
+ full_name = f"{model_name}_{class_name}"
263
+ if full_name not in category_map:
264
+ category_map[full_name] = category_id
265
+ coco_data["categories"].append({
266
+ "id": category_id,
267
+ "name": full_name,
268
+ "supercategory": model_name
269
+ })
270
+ category_id += 1
271
+
272
+ annotation_id = 1
273
+
274
+ for image_idx, (filename, detections_by_model) in enumerate(results_data):
275
+ # Add image info
276
+ image_id = image_idx + 1
277
+ img_height, img_width = image_info.get(filename, (0, 0))
278
+
279
+ coco_data["images"].append({
280
+ "id": image_id,
281
+ "file_name": filename,
282
+ "width": img_width,
283
+ "height": img_height,
284
+ "license": 1
285
+ })
286
+
287
+ # Add annotations for each model
288
+ for model_name, detections in detections_by_model.items():
289
+ if detections:
290
+ for detection in detections:
291
+ bbox = detection["bbox"] # [x, y, width, height]
292
+ class_name = detection["class_name"]
293
+ confidence = detection["confidence"]
294
+
295
+ full_category_name = f"{model_name}_{class_name}"
296
+ category_id = category_map.get(full_category_name, 1)
297
+
298
+ coco_data["annotations"].append({
299
+ "id": annotation_id,
300
+ "image_id": image_id,
301
+ "category_id": category_id,
302
+ "bbox": bbox,
303
+ "area": bbox[2] * bbox[3],
304
+ "iscrowd": 0,
305
+ "score": confidence
306
+ })
307
+ annotation_id += 1
308
+
309
+ return coco_data
310
+
311
+ def create_download_zip(images: List[Tuple[str, np.ndarray]], annotations: Dict) -> str:
312
+ """Create a ZIP file with images and annotations"""
313
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
314
+ zip_filename = f"medieval_detection_results_{timestamp}.zip"
315
+ zip_path = os.path.join(tempfile.gettempdir(), zip_filename)
316
+
317
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
318
+ # Add images
319
+ for filename, image_array in images:
320
+ # Convert numpy array to PIL Image and save as bytes
321
+ pil_image = Image.fromarray(image_array.astype('uint8'))
322
+ img_bytes = io.BytesIO()
323
+
324
+ # Determine format from filename
325
+ if filename.lower().endswith('.png'):
326
+ pil_image.save(img_bytes, format='PNG')
327
+ else:
328
+ pil_image.save(img_bytes, format='JPEG')
329
+
330
+ # Add to ZIP
331
+ zipf.writestr(f"images/{filename}", img_bytes.getvalue())
332
+
333
+ # Add annotations
334
+ annotations_json = json.dumps(annotations, indent=2)
335
+ zipf.writestr("annotations.json", annotations_json)
336
+
337
+ # Add README
338
+ readme_content = f"""Medieval Manuscript Detection Results
339
+ =============================================
340
+
341
+ Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
342
+
343
+ Contents:
344
+ - images/: Annotated images with detection results
345
+ - annotations.json: COCO format annotations
346
+
347
+ Models and Color Coding:
348
+ - Line Detection (Red boxes with [LINE] prefix)
349
+ - Border Detection (Green boxes with [BORDER] prefix)
350
+ - Zones Detection (Blue boxes with [ZONE] prefix)
351
+
352
+ Label format: [MODEL] class_name (confidence_score)
353
+ Annotation format: COCO JSON
354
+ For more info: https://cocodataset.org/#format-data
355
+ """
356
+ zipf.writestr("README.txt", readme_content)
357
+
358
+ return zip_path
359
+
360
+ # Create Gradio interface
361
+ with gr.Blocks() as demo:
362
+ gr.Markdown("# Medieval Manuscript Detection with Custom YOLO Models")
363
+ gr.Markdown("""
364
+ **Models and Color Coding:**
365
+ - πŸ”΄ **Line Detection** - Red boxes with [LINE] prefix
366
+ - 🟒 **Border Detection** - Green boxes with [BORDER] prefix
367
+ - πŸ”΅ **Zones Detection** - Blue boxes with [ZONE] prefix
368
+
369
+ Each detection shows: **[MODEL] class_name (confidence_score)**
370
+ """)
371
+
372
+ with gr.Tabs():
373
+ # Single Image Tab
374
+ with gr.TabItem("Single Image"):
375
+ with gr.Row():
376
+ with gr.Column():
377
+ input_image = gr.Image(
378
+ label="Input Image",
379
+ type='numpy'
380
+ )
381
+ with gr.Accordion("Detection Settings", open=True):
382
+ with gr.Row():
383
+ conf_threshold = gr.Slider(
384
+ label="Confidence Threshold",
385
+ minimum=0.0,
386
+ maximum=1.0,
387
+ step=0.05,
388
+ value=0.25,
389
+ )
390
+ iou_threshold = gr.Slider(
391
+ label="IoU Threshold",
392
+ minimum=0.0,
393
+ maximum=1.0,
394
+ step=0.05,
395
+ value=0.45,
396
+ info="Decrease for stricter detection, increase for more overlapping boxes"
397
+ )
398
+ with gr.Row():
399
+ clear_btn = gr.Button("Clear")
400
+ detect_btn = gr.Button("Detect with All Models", variant="primary")
401
+
402
+ with gr.Column():
403
+ output_image = gr.Image(
404
+ label="Combined Detection Result",
405
+ type='numpy'
406
+ )
407
+
408
+ # Single image download buttons
409
+ with gr.Row():
410
+ single_download_json_btn = gr.Button(
411
+ "πŸ“„ Download Annotations (JSON)",
412
+ variant="secondary",
413
+ size="sm"
414
+ )
415
+ single_download_image_btn = gr.Button(
416
+ "πŸ–ΌοΈ Download Image",
417
+ variant="secondary",
418
+ size="sm"
419
+ )
420
+
421
+ # Single image file outputs
422
+ single_json_output = gr.File(
423
+ label="πŸ“„ JSON Download",
424
+ visible=True,
425
+ height=50
426
+ )
427
+ single_image_output = gr.File(
428
+ label="πŸ–ΌοΈ Image Download",
429
+ visible=True,
430
+ height=50
431
+ )
432
+
433
+ # Batch Processing Tab
434
+ with gr.TabItem("Batch Processing (ZIP)"):
435
+ with gr.Row():
436
+ with gr.Column():
437
+ zip_file = gr.File(
438
+ label="Upload ZIP file with images",
439
+ file_types=[".zip"]
440
+ )
441
+ with gr.Accordion("Detection Settings", open=True):
442
+ with gr.Row():
443
+ batch_conf_threshold = gr.Slider(
444
+ label="Confidence Threshold",
445
+ minimum=0.0,
446
+ maximum=1.0,
447
+ step=0.05,
448
+ value=0.25,
449
+ )
450
+ batch_iou_threshold = gr.Slider(
451
+ label="IoU Threshold",
452
+ minimum=0.0,
453
+ maximum=1.0,
454
+ step=0.05,
455
+ value=0.45,
456
+ )
457
+
458
+ # Add status message box
459
+ batch_status = gr.Textbox(
460
+ label="Processing Status",
461
+ value="Ready to process ZIP file...",
462
+ interactive=False,
463
+ max_lines=3
464
+ )
465
+
466
+ with gr.Row():
467
+ clear_batch_btn = gr.Button("Clear")
468
+ process_batch_btn = gr.Button("Process ZIP", variant="primary")
469
+
470
+ with gr.Column():
471
+ batch_gallery = gr.Gallery(
472
+ label="Batch Processing Results",
473
+ show_label=True,
474
+ elem_id="gallery",
475
+ columns=2,
476
+ rows=2,
477
+ height="auto",
478
+ type="numpy" # Explicitly handle numpy arrays
479
+ )
480
+
481
+ # Download buttons
482
+ with gr.Row():
483
+ download_json_btn = gr.Button(
484
+ "πŸ“„ Download COCO Annotations (JSON)",
485
+ variant="secondary"
486
+ )
487
+ download_zip_btn = gr.Button(
488
+ "πŸ“¦ Download Results (ZIP)",
489
+ variant="secondary"
490
+ )
491
+
492
+ # File outputs for downloads
493
+ json_file_output = gr.File(
494
+ label="πŸ“„ JSON Download",
495
+ visible=True,
496
+ height=50
497
+ )
498
+ zip_file_output = gr.File(
499
+ label="πŸ“¦ ZIP Download",
500
+ visible=True,
501
+ height=50
502
+ )
503
+
504
+ # Global variables for single image results
505
+ single_image_result = None
506
+ single_image_annotations = None
507
+ single_image_filename = None
508
+
509
+ def process_single_image(
510
+ image: np.ndarray,
511
+ conf_threshold: float,
512
+ iou_threshold: float
513
+ ) -> Tuple[np.ndarray, np.ndarray]:
514
+ global single_image_result, single_image_annotations, single_image_filename
515
+
516
+ if image is None:
517
+ single_image_result = None
518
+ single_image_annotations = None
519
+ single_image_filename = None
520
+ return None, None
521
+
522
+ # Process with annotations
523
+ annotated_image, detections_data = detect_and_annotate_combined(
524
+ image, conf_threshold, iou_threshold, return_annotations=True
525
+ )
526
+
527
+ # Store results globally for download
528
+ single_image_result = annotated_image
529
+ single_image_annotations = detections_data
530
+ single_image_filename = f"detection_result_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
531
+
532
+ return image, annotated_image
533
+
534
+ def process_batch_images_with_status(
535
+ zip_file,
536
+ conf_threshold: float,
537
+ iou_threshold: float
538
+ ):
539
+ print("πŸš€ ========== BATCH PROCESSING STARTED ==========")
540
+
541
+ if zip_file is None:
542
+ print("❌ No ZIP file provided")
543
+ return [], "Please upload a ZIP file first."
544
+
545
+ print(f"πŸ“ ZIP file received: {zip_file.name}")
546
+ print(f"βš™οΈ Settings: conf_threshold={conf_threshold}, iou_threshold={iou_threshold}")
547
+
548
+ try:
549
+ # Process zip file
550
+ print("πŸ”„ Starting ZIP file processing...")
551
+ results, annotations_data, image_info = process_zip_file(zip_file.name, conf_threshold, iou_threshold)
552
+
553
+ if not results:
554
+ error_msg = "No valid images found in ZIP file."
555
+ print(f"❌ {error_msg}")
556
+ return [], error_msg
557
+
558
+ # Store data globally for download
559
+ global current_results, current_images
560
+ current_images = results
561
+ current_results = annotations_data
562
+
563
+ print(f"πŸ“Š ZIP processing returned {len(results)} results")
564
+
565
+ # Convert results to format expected by Gallery
566
+ print("πŸ”„ Converting results for Gradio Gallery...")
567
+ gallery_images = []
568
+
569
+ for i, (filename, annotated_image) in enumerate(results):
570
+ print(f"πŸ–ΌοΈ Converting image {i+1}/{len(results)}: {filename}")
571
+ print(f" Image shape: {annotated_image.shape}, dtype: {annotated_image.dtype}")
572
+
573
+ # Ensure the image is in the right format and range
574
+ if annotated_image.dtype != 'uint8':
575
+ print(f" Converting dtype from {annotated_image.dtype} to uint8")
576
+ # Normalize if needed
577
+ if annotated_image.max() <= 1.0:
578
+ annotated_image = (annotated_image * 255).astype('uint8')
579
+ print(f" Normalized from [0,1] to [0,255]")
580
+ else:
581
+ annotated_image = annotated_image.astype('uint8')
582
+ print(f" Cast to uint8")
583
+
584
+ print(f" Final image shape: {annotated_image.shape}, dtype: {annotated_image.dtype}")
585
+
586
+ # For Gradio gallery, we can pass numpy arrays directly
587
+ # Format: (image_data, caption)
588
+ gallery_images.append((annotated_image, filename))
589
+ print(f" βœ… Added {filename} to gallery")
590
+
591
+ success_msg = f"βœ… Successfully processed {len(gallery_images)} images!"
592
+ print(f"πŸŽ‰ {success_msg}")
593
+ print(f"πŸ“‹ Gallery contains {len(gallery_images)} items")
594
+ print("🏁 ========== BATCH PROCESSING COMPLETED ==========\n")
595
+
596
+ return gallery_images, success_msg
597
+
598
+ except Exception as e:
599
+ error_msg = f"❌ Error: {str(e)}"
600
+ print(f"πŸ’₯ EXCEPTION in process_batch_images_with_status: {error_msg}")
601
+ import traceback
602
+ traceback.print_exc()
603
+ print("πŸ’€ ========== BATCH PROCESSING FAILED ==========\n")
604
+ return [], error_msg
605
+
606
+ def clear_single():
607
+ return None, None
608
+
609
+ def clear_batch():
610
+ global current_results, current_images
611
+ current_results = []
612
+ current_images = []
613
+ return None, [], "Ready to process ZIP file..."
614
+
615
+ def download_annotations():
616
+ """Create and return COCO JSON annotations file"""
617
+ global current_results, current_images
618
+
619
+ if not current_results:
620
+ print("❌ No annotation data available for download")
621
+ return None
622
+
623
+ try:
624
+ # Create image info dictionary
625
+ image_info = {}
626
+ for filename, image_array in current_images:
627
+ height, width = image_array.shape[:2]
628
+ image_info[filename] = (height, width)
629
+
630
+ # Create COCO annotations
631
+ coco_data = create_coco_annotations(current_results, image_info)
632
+
633
+ # Save to temporary file with proper name
634
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
635
+ json_filename = f"medieval_annotations_{timestamp}.json"
636
+ json_path = os.path.join(tempfile.gettempdir(), json_filename)
637
+
638
+ with open(json_path, 'w') as f:
639
+ json.dump(coco_data, f, indent=2)
640
+
641
+ print(f"πŸ’Ύ Created annotations file: {json_path}")
642
+ print(f"πŸ“ File size: {os.path.getsize(json_path)} bytes")
643
+
644
+ # Verify file exists and is readable
645
+ if os.path.exists(json_path) and os.path.getsize(json_path) > 0:
646
+ return json_path
647
+ else:
648
+ print(f"❌ File verification failed: {json_path}")
649
+ return None
650
+
651
+ except Exception as e:
652
+ print(f"❌ Error creating annotations: {e}")
653
+ import traceback
654
+ traceback.print_exc()
655
+ return None
656
+
657
+ def download_results_zip():
658
+ """Create and return ZIP file with images and annotations"""
659
+ global current_results, current_images
660
+
661
+ if not current_results or not current_images:
662
+ print("❌ No results data available for ZIP download")
663
+ return None
664
+
665
+ try:
666
+ # Create image info dictionary
667
+ image_info = {}
668
+ for filename, image_array in current_images:
669
+ height, width = image_array.shape[:2]
670
+ image_info[filename] = (height, width)
671
+
672
+ # Create COCO annotations
673
+ coco_data = create_coco_annotations(current_results, image_info)
674
+
675
+ # Create ZIP file
676
+ zip_path = create_download_zip(current_images, coco_data)
677
+
678
+ print(f"πŸ’Ύ Created results ZIP: {zip_path}")
679
+ print(f"πŸ“ ZIP file size: {os.path.getsize(zip_path)} bytes")
680
+
681
+ # Verify file exists and is readable
682
+ if os.path.exists(zip_path) and os.path.getsize(zip_path) > 0:
683
+ return zip_path
684
+ else:
685
+ print(f"❌ ZIP file verification failed: {zip_path}")
686
+ return None
687
+
688
+ except Exception as e:
689
+ print(f"❌ Error creating ZIP file: {e}")
690
+ import traceback
691
+ traceback.print_exc()
692
+ return None
693
+
694
+ def download_single_annotations():
695
+ """Download COCO annotations for single image"""
696
+ global single_image_annotations, single_image_result, single_image_filename
697
+
698
+ if single_image_annotations is None or single_image_result is None:
699
+ print("❌ No single image annotation data available")
700
+ return None
701
+
702
+ try:
703
+ # Create image info
704
+ height, width = single_image_result.shape[:2]
705
+ image_info = {single_image_filename: (height, width)}
706
+
707
+ # Create annotations data in the expected format
708
+ annotations_data = [(single_image_filename, single_image_annotations)]
709
+
710
+ # Create COCO annotations
711
+ coco_data = create_coco_annotations(annotations_data, image_info)
712
+
713
+ # Save to temporary file
714
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
715
+ json_filename = f"single_image_annotations_{timestamp}.json"
716
+ json_path = os.path.join(tempfile.gettempdir(), json_filename)
717
+
718
+ with open(json_path, 'w') as f:
719
+ json.dump(coco_data, f, indent=2)
720
+
721
+ print(f"πŸ’Ύ Created single image annotations: {json_path}")
722
+ print(f"πŸ“ File size: {os.path.getsize(json_path)} bytes")
723
+
724
+ # Verify file exists
725
+ if os.path.exists(json_path) and os.path.getsize(json_path) > 0:
726
+ return json_path
727
+ else:
728
+ print(f"❌ Single image file verification failed: {json_path}")
729
+ return None
730
+
731
+ except Exception as e:
732
+ print(f"❌ Error creating single image annotations: {e}")
733
+ import traceback
734
+ traceback.print_exc()
735
+ return None
736
+
737
+ def download_single_image():
738
+ """Download processed single image"""
739
+ global single_image_result, single_image_filename
740
+
741
+ if single_image_result is None:
742
+ print("❌ No single image result available")
743
+ return None
744
+
745
+ try:
746
+ # Convert to PIL and save
747
+ pil_image = Image.fromarray(single_image_result.astype('uint8'))
748
+
749
+ # Save to temporary file
750
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
751
+ img_filename = f"processed_image_{timestamp}.jpg"
752
+ img_path = os.path.join(tempfile.gettempdir(), img_filename)
753
+
754
+ pil_image.save(img_path, 'JPEG', quality=95)
755
+
756
+ print(f"πŸ’Ύ Created single image file: {img_path}")
757
+ print(f"πŸ“ Image file size: {os.path.getsize(img_path)} bytes")
758
+
759
+ # Verify file exists
760
+ if os.path.exists(img_path) and os.path.getsize(img_path) > 0:
761
+ return img_path
762
+ else:
763
+ print(f"❌ Single image file verification failed: {img_path}")
764
+ return None
765
+
766
+ except Exception as e:
767
+ print(f"❌ Error creating single image file: {e}")
768
+ import traceback
769
+ traceback.print_exc()
770
+ return None
771
+
772
+ # Connect buttons to functions for single image
773
+ detect_btn.click(
774
+ process_single_image,
775
+ inputs=[input_image, conf_threshold, iou_threshold],
776
+ outputs=[input_image, output_image]
777
+ )
778
+ clear_btn.click(
779
+ clear_single,
780
+ inputs=None,
781
+ outputs=[input_image, output_image]
782
+ )
783
+
784
+ # Connect buttons to functions for batch processing
785
+ process_batch_btn.click(
786
+ process_batch_images_with_status,
787
+ inputs=[zip_file, batch_conf_threshold, batch_iou_threshold],
788
+ outputs=[batch_gallery, batch_status]
789
+ )
790
+ clear_batch_btn.click(
791
+ clear_batch,
792
+ inputs=None,
793
+ outputs=[zip_file, batch_gallery, batch_status]
794
+ )
795
+
796
+ # Connect download buttons
797
+ download_json_btn.click(
798
+ fn=download_annotations,
799
+ inputs=[],
800
+ outputs=[json_file_output]
801
+ )
802
+ download_zip_btn.click(
803
+ fn=download_results_zip,
804
+ inputs=[],
805
+ outputs=[zip_file_output]
806
+ )
807
+
808
+ # Connect single image download buttons
809
+ single_download_json_btn.click(
810
+ fn=download_single_annotations,
811
+ inputs=[],
812
+ outputs=[single_json_output]
813
+ )
814
+ single_download_image_btn.click(
815
+ fn=download_single_image,
816
+ inputs=[],
817
+ outputs=[single_image_output]
818
+ )
819
+
820
+ if __name__ == "__main__":
821
+ demo.launch(debug=True, show_error=True)
best_line_detection_yoloe (1).pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6e4ea55599d3ed8a9249cc10c672fb0ccc8f416ee8cb9cf478181bec1f2e8ad
3
+ size 20491236
border_model_weights.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:24224a9b968f38e9963afcd2cf46968edeb0946c5ccd0b04d306e7024af6ac99
3
+ size 12582912
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ supervision
3
+ ultralytics
4
+ opencv-python
5
+ pillow
6
+ numpy<2.0
zones_model_weights.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:228f5d76f78276a751389a8b1710f718fe82291c70624f22c196563e45d97a3b
3
+ size 19190618