vichetkao commited on
Commit
c7d43c7
·
verified ·
1 Parent(s): acb9651

Upload 8 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,9 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ images/testing_1.png filter=lfs diff=lfs merge=lfs -text
37
+ images/testing_2.png filter=lfs diff=lfs merge=lfs -text
38
+ images/testing_3.png filter=lfs diff=lfs merge=lfs -text
39
+ images/testing_4.png filter=lfs diff=lfs merge=lfs -text
40
+ images/testing_5.png filter=lfs diff=lfs merge=lfs -text
41
+ images/testing_6.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,426 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import io
4
+ import os
5
+ from PIL import Image, ImageDraw, ImageFont
6
+ from pathlib import Path
7
+
8
+ API_URL = os.getenv("API_URL")
9
+ API_KEY = os.getenv("API_KEY")
10
+ IMAGE_FOLDER = os.getenv("IMAGE_FOLDER", "images")
11
+
12
+ def get_test_images():
13
+ images = []
14
+ if os.path.exists(IMAGE_FOLDER):
15
+ for file in sorted(Path(IMAGE_FOLDER).glob("*")):
16
+ if file.suffix.lower() in [".jpg", ".jpeg", ".png", ".bmp", ".gif"]:
17
+ images.append((str(file), file.name))
18
+ return images
19
+
20
+ def load_test_image(image_path):
21
+ if image_path and os.path.exists(image_path):
22
+ return Image.open(image_path)
23
+ return None
24
+
25
+ CLASS_NAMES = {0: "figure"}
26
+ CLASS_COLORS = {
27
+ 0: (255, 165, 0),
28
+ }
29
+
30
+ def draw_boxes_on_image(image, detections):
31
+ if not detections:
32
+ return image
33
+
34
+ img_copy = image.copy()
35
+ draw = ImageDraw.Draw(img_copy)
36
+ img_width, img_height = img_copy.size
37
+
38
+ min_dimension = min(img_width, img_height)
39
+ font_size = max(int(min_dimension * 0.02), 24)
40
+ line_width = max(int(min_dimension * 0.008), 3)
41
+
42
+ try:
43
+ label_font = ImageFont.truetype("arial.ttf", font_size)
44
+ except:
45
+ label_font = ImageFont.load_default()
46
+
47
+ for detection in detections:
48
+ confidence = detection.get("confidence", 0)
49
+ class_id = detection.get("class", 0)
50
+ box = detection.get("box", {})
51
+
52
+ color = CLASS_COLORS.get(class_id, (255, 165, 0))
53
+
54
+ x1 = int(box.get("x1", 0))
55
+ y1 = int(box.get("y1", 0))
56
+ x2 = int(box.get("x2", 0))
57
+ y2 = int(box.get("y2", 0))
58
+
59
+ if x1 > 0 and y1 > 0 and x2 > x1 and y2 > y1:
60
+ draw.rectangle([x1, y1, x2, y2], outline=color, width=line_width)
61
+
62
+ label = f"Figure {confidence:.1%}"
63
+
64
+ bbox = draw.textbbox((0, 0), label, font=label_font)
65
+ text_width = bbox[2] - bbox[0]
66
+ text_height = bbox[3] - bbox[1]
67
+
68
+ center_x = (x1 + x2) / 2
69
+ label_x = int(center_x - text_width / 2)
70
+ label_y = max(0, y1 - text_height - 5)
71
+
72
+ if label_x < 0:
73
+ label_x = 2
74
+ if label_x + text_width > img_width:
75
+ label_x = img_width - text_width - 2
76
+
77
+ bg_padding = 4
78
+ bg_box = [
79
+ label_x - bg_padding,
80
+ label_y - bg_padding,
81
+ label_x + text_width + bg_padding,
82
+ label_y + text_height + bg_padding
83
+ ]
84
+ draw.rectangle(bg_box, outline=color, fill=(0, 0, 0))
85
+
86
+ draw.text((label_x, label_y), label, font=label_font, fill=color)
87
+
88
+ return img_copy
89
+
90
+ def predict_image(image, confidence, iou, imgsz):
91
+ if image is None:
92
+ return None, "#### Please upload an image to begin detection"
93
+
94
+ try:
95
+ img_bytes = io.BytesIO()
96
+ image.save(img_bytes, format='JPEG')
97
+ img_bytes.seek(0)
98
+
99
+ params = {
100
+ "conf": confidence,
101
+ "iou": iou,
102
+ "imgsz": imgsz
103
+ }
104
+
105
+ headers = {"Authorization": f"Bearer {API_KEY}"}
106
+ files = {"file": ("image.jpg", img_bytes, "image/jpeg")}
107
+
108
+ response = requests.post(API_URL, headers=headers, data=params, files=files, timeout=30)
109
+ response.raise_for_status()
110
+
111
+ result = response.json()
112
+ formatted_result = format_results(result)
113
+
114
+ detections = []
115
+ if "images" in result and len(result["images"]) > 0:
116
+ detections = result["images"][0].get("results", [])
117
+
118
+ image_with_boxes = draw_boxes_on_image(image, detections)
119
+
120
+ return image_with_boxes, formatted_result
121
+
122
+ except requests.exceptions.Timeout:
123
+ return None, "#### Error: Request timeout. Please try again."
124
+ except requests.exceptions.ConnectionError:
125
+ return None, "#### Error: Unable to connect to detection service. Please check API configuration."
126
+ except requests.exceptions.HTTPError as e:
127
+ return None, f"#### Error: API returned status {e.response.status_code}"
128
+ except Exception as e:
129
+ return None, f"#### Error: {str(e)}"
130
+
131
+ def format_results(result):
132
+ if isinstance(result, dict):
133
+ output = "## Detection Results\n\n"
134
+
135
+ if "images" in result and len(result["images"]) > 0:
136
+ img_data = result["images"][0]
137
+ shape = img_data.get("shape", [])
138
+ detections = img_data.get("results", [])
139
+
140
+ output += f"**Image Size:** {shape[0]} x {shape[1]} (W x H)\n"
141
+ output += f"**Detections Found:** {len(detections)}\n\n"
142
+
143
+ speed = img_data.get("speed", {})
144
+ if speed:
145
+ output += "\n### Performance Metrics\n"
146
+ output += "| Metric | Time (ms) |\n"
147
+ output += "|--------|----------|\n"
148
+ output += f"| Preprocess | {speed.get('preprocess', 'N/A')} |\n"
149
+ output += f"| Inference | {speed.get('inference', 'N/A')} |\n"
150
+ output += f"| Postprocess | {speed.get('postprocess', 'N/A')} |\n"
151
+
152
+ if detections:
153
+ output += "### Detected Objects\n"
154
+ output += "| Label | Class | Confidence |\n"
155
+ output += "|-------|-------|------------|\n"
156
+
157
+ for det in detections:
158
+ name = det.get("name", "Unknown")
159
+ class_id = det.get("class", "N/A")
160
+ conf = det.get("confidence", 0)
161
+ output += f"| {name} | {class_id} | {conf:.2%} |\n"
162
+
163
+ return output
164
+
165
+ return str(result)
166
+
167
+ dark_theme = gr.themes.Monochrome(
168
+ primary_hue="slate",
169
+ secondary_hue="slate",
170
+ ).set(
171
+ body_text_color="#e0e0e0",
172
+ background_fill_primary="#0f0f0f",
173
+ background_fill_secondary="#1a1a1a",
174
+ )
175
+
176
+ with gr.Blocks(
177
+ title="Figure Detection",
178
+ theme=dark_theme,
179
+ css="""
180
+ footer {display: none !important;}
181
+ .gradio-container {border-radius: 12px;}
182
+ .gr-card {border-radius: 12px;}
183
+ .block {border-radius: 12px;}
184
+ .form {border-radius: 12px;}
185
+ button {border-radius: 12px;}
186
+ .gr-button {border-radius: 12px;}
187
+ #imageModal {
188
+ display: none;
189
+ position: fixed;
190
+ z-index: 10000;
191
+ left: 0;
192
+ top: 0;
193
+ width: 100%;
194
+ height: 100%;
195
+ background-color: rgba(0, 0, 0, 0.9);
196
+ animation: fadeIn 0.3s;
197
+ }
198
+ @keyframes fadeIn {
199
+ from {opacity: 0;}
200
+ to {opacity: 1;}
201
+ }
202
+ #modalImage {
203
+ position: absolute;
204
+ top: 50%;
205
+ left: 50%;
206
+ transform: translate(-50%, -50%);
207
+ max-width: 95%;
208
+ max-height: 95%;
209
+ object-fit: contain;
210
+ touch-action: pinch-zoom;
211
+ cursor: zoom-out;
212
+ }
213
+ .modal-open {
214
+ overflow: hidden;
215
+ }
216
+ .closeBtn {
217
+ position: absolute;
218
+ top: 20px;
219
+ right: 30px;
220
+ font-size: 40px;
221
+ font-weight: bold;
222
+ color: white;
223
+ cursor: pointer;
224
+ z-index: 10001;
225
+ }
226
+ .closeBtn:hover {
227
+ color: #bbb;
228
+ }
229
+ """
230
+ ) as demo:
231
+ with gr.Column():
232
+ gr.Markdown("""
233
+ # Figure Detection
234
+ Detect figures in your documents. Upload an image and adjust parameters to detect figures with custom inference settings.
235
+ """)
236
+
237
+ with gr.Row():
238
+ with gr.Column(scale=1, min_width=400):
239
+ gr.Markdown("### Input")
240
+ image_input = gr.Image(
241
+ label="Image",
242
+ type="pil",
243
+ sources=["upload"],
244
+ interactive=True
245
+ )
246
+
247
+ test_images = get_test_images()
248
+ if test_images:
249
+ test_image_radio = gr.Radio(
250
+ choices=[img[1] for img in test_images],
251
+ label="Select test image",
252
+ info="Click to load"
253
+ )
254
+ test_image_radio.change(
255
+ fn=lambda name: load_test_image(next((img[0] for img in test_images if img[1] == name), None)),
256
+ inputs=[test_image_radio],
257
+ outputs=[image_input]
258
+ )
259
+ else:
260
+ gr.Markdown("No test images found. Add images to the 'images' folder.")
261
+
262
+ gr.Markdown("### Configuration")
263
+
264
+ confidence_slider = gr.Slider(
265
+ label="Confidence Threshold",
266
+ minimum=0.0,
267
+ maximum=1.0,
268
+ value=0.25,
269
+ step=0.01,
270
+ info="Detection confidence level"
271
+ )
272
+
273
+ iou_slider = gr.Slider(
274
+ label="IOU Threshold",
275
+ minimum=0.0,
276
+ maximum=1.0,
277
+ value=0.7,
278
+ step=0.01,
279
+ info="Intersection over union threshold"
280
+ )
281
+
282
+ imgsz_slider = gr.Slider(
283
+ label="Image Size",
284
+ minimum=320,
285
+ maximum=1280,
286
+ value=640,
287
+ step=32,
288
+ info="Inference image resolution"
289
+ )
290
+
291
+ predict_btn = gr.Button(
292
+ "Detect Objects",
293
+ variant="primary",
294
+ size="lg",
295
+ scale=1
296
+ )
297
+
298
+ with gr.Column(scale=1, min_width=400):
299
+ gr.Markdown("### Results")
300
+
301
+ image_output = gr.Image(
302
+ label="Detections (Click to fullscreen)",
303
+ type="pil",
304
+ interactive=False,
305
+ scale=1
306
+ )
307
+
308
+ results_output = gr.Markdown(
309
+ value="Detection results will appear here.",
310
+ label="Detection Results"
311
+ )
312
+
313
+ gr.HTML("""
314
+ <div id="imageModal">
315
+ <span class="closeBtn">&times;</span>
316
+ <img id="modalImage" src="" alt="Fullscreen Detection">
317
+ </div>
318
+ <script>
319
+ const modal = document.getElementById('imageModal');
320
+ const modalImg = document.getElementById('modalImage');
321
+ const closeBtn = document.querySelector('.closeBtn');
322
+ let touchStartX = 0;
323
+ let touchStartY = 0;
324
+ let scale = 1;
325
+ const observeImageChanges = () => {
326
+ const imageContainer = document.querySelector('[data-testid="image"]') ||
327
+ document.querySelector('img[alt="Image"]');
328
+ if (imageContainer) {
329
+ const images = imageContainer.querySelectorAll('img');
330
+ images.forEach(img => {
331
+ if (img.src && !img.hasClickListener) {
332
+ img.style.cursor = 'pointer';
333
+ img.addEventListener('click', (e) => {
334
+ if (e.target.src && !e.target.src.includes('data:image/svg')) {
335
+ modalImg.src = e.target.src;
336
+ modal.style.display = 'block';
337
+ document.body.classList.add('modal-open');
338
+ scale = 1;
339
+ modalImg.style.transform = 'translate(-50%, -50%) scale(1)';
340
+ }
341
+ });
342
+ img.hasClickListener = true;
343
+ }
344
+ });
345
+ }
346
+ };
347
+ setInterval(observeImageChanges, 500);
348
+ observeImageChanges();
349
+ modal.addEventListener('click', (e) => {
350
+ if (e.target === modal) {
351
+ modal.style.display = 'none';
352
+ document.body.classList.remove('modal-open');
353
+ scale = 1;
354
+ }
355
+ });
356
+ closeBtn.addEventListener('click', () => {
357
+ modal.style.display = 'none';
358
+ document.body.classList.remove('modal-open');
359
+ scale = 1;
360
+ });
361
+ document.addEventListener('keydown', (e) => {
362
+ if (e.key === 'Escape' && modal.style.display === 'block') {
363
+ modal.style.display = 'none';
364
+ document.body.classList.remove('modal-open');
365
+ scale = 1;
366
+ }
367
+ });
368
+ let lastDistance = 0;
369
+ modalImg.addEventListener('touchstart', (e) => {
370
+ if (e.touches.length === 2) {
371
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
372
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
373
+ lastDistance = Math.sqrt(dx * dx + dy * dy);
374
+ }
375
+ touchStartX = e.touches[0].clientX;
376
+ touchStartY = e.touches[0].clientY;
377
+ });
378
+ modalImg.addEventListener('touchmove', (e) => {
379
+ if (e.touches.length === 2) {
380
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
381
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
382
+ const distance = Math.sqrt(dx * dx + dy * dy);
383
+ const scaleChange = distance / lastDistance;
384
+ scale = Math.max(1, Math.min(scale * scaleChange, 4));
385
+ modalImg.style.transform = `translate(-50%, -50%) scale(${scale})`;
386
+ lastDistance = distance;
387
+ }
388
+ });
389
+ modalImg.addEventListener('touchend', () => {
390
+ lastDistance = 0;
391
+ });
392
+ </script>
393
+ """)
394
+
395
+ predict_btn.click(
396
+ fn=predict_image,
397
+ inputs=[image_input, confidence_slider, iou_slider, imgsz_slider],
398
+ outputs=[image_output, results_output]
399
+ )
400
+
401
+ image_input.change(
402
+ fn=predict_image,
403
+ inputs=[image_input, confidence_slider, iou_slider, imgsz_slider],
404
+ outputs=[image_output, results_output]
405
+ )
406
+
407
+ confidence_slider.change(
408
+ fn=predict_image,
409
+ inputs=[image_input, confidence_slider, iou_slider, imgsz_slider],
410
+ outputs=[image_output, results_output]
411
+ )
412
+
413
+ iou_slider.change(
414
+ fn=predict_image,
415
+ inputs=[image_input, confidence_slider, iou_slider, imgsz_slider],
416
+ outputs=[image_output, results_output]
417
+ )
418
+
419
+ imgsz_slider.change(
420
+ fn=predict_image,
421
+ inputs=[image_input, confidence_slider, iou_slider, imgsz_slider],
422
+ outputs=[image_output, results_output]
423
+ )
424
+
425
+ if __name__ == "__main__":
426
+ demo.launch(share=False, show_error=True)
images/testing_1.png ADDED

Git LFS Details

  • SHA256: 789e162e711880d557805538e45f114571fb1a04a01d7d4f2ff34863fce4d6d0
  • Pointer size: 131 Bytes
  • Size of remote file: 449 kB
images/testing_2.png ADDED

Git LFS Details

  • SHA256: 7fc2a52103b968b7b9d21cecbde7fb8edf6afd68428467eee4d7bb876f45ebb6
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
images/testing_3.png ADDED

Git LFS Details

  • SHA256: 1c3b18266664fa51a6a047244ccc89d23632771892e2c3a78887283f005874ab
  • Pointer size: 132 Bytes
  • Size of remote file: 1.09 MB
images/testing_4.png ADDED

Git LFS Details

  • SHA256: ba9ecec39a12b83fd9d94e78d1119664e20f0c5405fbcff7dd3a0d26719fc092
  • Pointer size: 132 Bytes
  • Size of remote file: 1.35 MB
images/testing_5.png ADDED

Git LFS Details

  • SHA256: 792ae0a7a9500ade7017987afd0671c41764aaa56f209bb36a5f19bde6dc5531
  • Pointer size: 132 Bytes
  • Size of remote file: 1 MB
images/testing_6.png ADDED

Git LFS Details

  • SHA256: 89254d16271d49152581d6a6fc8dc08e6baaa174212aaddf63af020d923b8998
  • Pointer size: 132 Bytes
  • Size of remote file: 1.01 MB
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio
2
+ pandas
3
+ pillow