asdfaman commited on
Commit
e0a9107
·
verified ·
1 Parent(s): a27ca8f

Update app1.py

Browse files
Files changed (1) hide show
  1. app1.py +295 -0
app1.py CHANGED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+ import pandas as pd
5
+ import matplotlib.pyplot as plt
6
+ from PIL import Image
7
+ from typing import Tuple, List
8
+
9
+ from constants import MODEL_PATH, DATABASE_DIR, DATABASE_PATH
10
+ from detector import SignatureDetector, download_model
11
+
12
+
13
+ def create_gradio_interface():
14
+ # Download model if it doesn't exist
15
+ if not os.path.exists(MODEL_PATH):
16
+ download_model()
17
+
18
+ # Initialize the detector
19
+ detector = SignatureDetector(MODEL_PATH)
20
+
21
+ css = """
22
+ .custom-button {
23
+ background-color: #b0ffb8 !important;
24
+ color: black !important;
25
+ }
26
+ .custom-button:hover {
27
+ background-color: #b0ffb8b3 !important;
28
+ }
29
+ .container {
30
+ max-width: 1200px !important;
31
+ margin: auto !important;
32
+ }
33
+ .main-container {
34
+ gap: 20px !important;
35
+ }
36
+ .metrics-container {
37
+ padding: 1.5rem !important;
38
+ border-radius: 0.75rem !important;
39
+ background-color: #1f2937 !important;
40
+ margin: 1rem 0 !important;
41
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
42
+ }
43
+ .metrics-title {
44
+ font-size: 1.25rem !important;
45
+ font-weight: 600 !important;
46
+ color: #1f2937 !important;
47
+ margin-bottom: 1rem !important;
48
+ }
49
+ .metrics-row {
50
+ display: flex !important;
51
+ gap: 1rem !important;
52
+ margin-top: 0.5rem !important;
53
+ }
54
+ """
55
+
56
+ def process_image(image: Image.Image, conf_thres: float, iou_thres: float) -> Tuple[Image.Image, str, plt.Figure, plt.Figure, str, str]:
57
+ if image is None:
58
+ return None, None, None, None, None, None
59
+
60
+ output_image, metrics = detector.detect(image, conf_thres, iou_thres)
61
+
62
+ # Create plots data
63
+ hist_data = pd.DataFrame({"Time (ms)": metrics["times"]})
64
+ indices = range(
65
+ metrics["start_index"], metrics["start_index"] + len(metrics["times"])
66
+ )
67
+
68
+ line_data = pd.DataFrame(
69
+ {
70
+ "Inference": indices,
71
+ "Time (ms)": metrics["times"],
72
+ "Mean": [metrics["avg_time"]] * len(metrics["times"]),
73
+ }
74
+ )
75
+
76
+ hist_fig, line_fig = detector.create_plots(hist_data, line_data)
77
+
78
+ return (
79
+ output_image,
80
+ gr.update(
81
+ value=f"{metrics['total_inferences']}",
82
+ container=True,
83
+ ),
84
+ hist_fig,
85
+ line_fig,
86
+ f"{metrics['avg_time']:.2f}",
87
+ f"{metrics['times'][-1]:.2f}",
88
+ )
89
+
90
+ def process_folder(files_paths: List[str], conf_thres: float, iou_thres: float):
91
+ if not files_paths:
92
+ return None, None, None, None, None, None
93
+
94
+ valid_extensions = [".jpg", ".jpeg", ".png"]
95
+ image_files = [
96
+ f for f in files_paths if os.path.splitext(f.lower())[1] in valid_extensions
97
+ ]
98
+
99
+ if not image_files:
100
+ return None, None, None, None, None, None
101
+
102
+ for img_file in image_files:
103
+ image = Image.open(img_file)
104
+
105
+ yield process_image(image, conf_thres, iou_thres)
106
+
107
+ with gr.Blocks(
108
+ theme=gr.themes.Soft(
109
+ primary_hue="indigo", secondary_hue="gray", neutral_hue="gray"
110
+ ),
111
+ css=css,
112
+ ) as iface:
113
+ gr.HTML(
114
+ """
115
+ <h1>Tech4Humans - Signature Detector</h1>
116
+
117
+ <div style="display: flex; align-items: center; gap: 10px;">
118
+ <a href="https://huggingface.co/tech4humans/yolov8s-signature-detector">
119
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/model-on-hf-md-dark.svg" alt="Model on HF">
120
+ </a>
121
+ <a href="https://huggingface.co/datasets/tech4humans/signature-detection">
122
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/dataset-on-hf-md-dark.svg" alt="Dataset on HF">
123
+ </a>
124
+ <a href="https://github.com/tech4ai/t4ai-signature-detect-server">
125
+ <img src="https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white" alt="GitHub">
126
+ </a>
127
+ <a href="https://huggingface.co/blog/samuellimabraz/signature-detection-model">
128
+ <img src="https://huggingface.co/datasets/huggingface/badges/resolve/main/paper-page-md-dark.svg" alt="Article">
129
+ </a>
130
+ </div>
131
+ """
132
+ )
133
+ gr.Markdown(
134
+ """
135
+ This system uses the [**YOLOv8s**](https://huggingface.co/tech4humans/yolov8s-signature-detector) model, specially fine-tuned for detecting handwritten signatures in document images.
136
+
137
+ With this detector, it is possible to identify signatures in digital documents with high accuracy in real time, making it ideal for applications involving validation, organization, and document processing.
138
+
139
+ ---
140
+ """
141
+ )
142
+
143
+ with gr.Row(equal_height=True, elem_classes="main-container"):
144
+ # Left column for controls and information
145
+ with gr.Column(scale=1):
146
+ with gr.Tab("Single Image"):
147
+ input_image = gr.Image(label="Upload your document", type="pil")
148
+ with gr.Row():
149
+ clear_single_btn = gr.ClearButton([input_image], value="Clear")
150
+ detect_single_btn = gr.Button(
151
+ "Detect", elem_classes="custom-button"
152
+ )
153
+
154
+ with gr.Tab("Image Folder"):
155
+ input_folder = gr.File(
156
+ label="Upload a folder with images",
157
+ file_count="directory",
158
+ type="filepath",
159
+ )
160
+ with gr.Row():
161
+ clear_folder_btn = gr.ClearButton([input_folder], value="Clear")
162
+ detect_folder_btn = gr.Button(
163
+ "Detect", elem_classes="custom-button"
164
+ )
165
+
166
+ with gr.Group():
167
+ confidence_threshold = gr.Slider(
168
+ minimum=0.0,
169
+ maximum=1.0,
170
+ value=0.25,
171
+ step=0.05,
172
+ label="Confidence Threshold",
173
+ info="Adjust the minimum confidence score required for detection.",
174
+ )
175
+ iou_threshold = gr.Slider(
176
+ minimum=0.0,
177
+ maximum=1.0,
178
+ value=0.5,
179
+ step=0.05,
180
+ label="IoU Threshold",
181
+ info="Adjust the Intersection over Union threshold for Non-Maximum Suppression (NMS).",
182
+ )
183
+
184
+ with gr.Column(scale=1):
185
+ output_image = gr.Image(label="Detection Results")
186
+
187
+ with gr.Accordion("Examples", open=True):
188
+ gr.Examples(
189
+ label="Image Examples",
190
+ examples=[
191
+ ["assets/images/example_{i}.jpg".format(i=i)]
192
+ for i in range(
193
+ 0, len(os.listdir(os.path.join("assets", "images")))
194
+ )
195
+ ],
196
+ inputs=input_image,
197
+ outputs=output_image,
198
+ fn=detector.detect_example,
199
+ cache_examples=True,
200
+ cache_mode="lazy",
201
+ )
202
+
203
+ with gr.Row(elem_classes="metrics-container"):
204
+ with gr.Column(scale=1):
205
+ total_inferences = gr.Textbox(
206
+ label="Total Inferences", show_copy_button=True, container=True
207
+ )
208
+ hist_plot = gr.Plot(label="Time Distribution", container=True)
209
+
210
+ with gr.Column(scale=1):
211
+ line_plot = gr.Plot(label="Time History", container=True)
212
+ with gr.Row(elem_classes="metrics-row"):
213
+ avg_inference_time = gr.Textbox(
214
+ label="Average Inference Time (ms)",
215
+ show_copy_button=True,
216
+ container=True,
217
+ )
218
+ last_inference_time = gr.Textbox(
219
+ label="Last Inference Time (ms)",
220
+ show_copy_button=True,
221
+ container=True,
222
+ )
223
+
224
+ with gr.Row(elem_classes="container"):
225
+
226
+ gr.Markdown(
227
+ """
228
+ ---
229
+ ## About the Project
230
+ This project uses the YOLOv8s model fine-tuned for detecting handwritten signatures in document images. It was trained with data from the [Tobacco800](https://paperswithcode.com/dataset/tobacco-800) and [signatures-xc8up](https://universe.roboflow.com/roboflow-100/signatures-xc8up) datasets, undergoing preprocessing and data augmentation processes.
231
+ ### Key Metrics:
232
+ - **Precision:** 94.74%
233
+ - **Recall:** 89.72%
234
+ - **mAP@50:** 94.50%
235
+ - **mAP@50-95:** 67.35%
236
+ - **Inference Time (CPU):** 171.56 ms
237
+ Complete details on the training process, hyperparameter tuning, model evaluation, dataset creation, and inference server can be found in the links below.
238
+
239
+ ---
240
+ **Developed by [Tech4Humans](https://www.tech4h.com.br/)** | **Model:** [YOLOv8s](https://huggingface.co/tech4humans/yolov8s-signature-detector) | **Dataset:** [Tobacco800 + signatures-xc8up](https://huggingface.co/datasets/tech4humans/signature-detection)
241
+ """
242
+ )
243
+
244
+ clear_single_btn.add([output_image])
245
+ clear_folder_btn.add([output_image])
246
+
247
+ detect_single_btn.click(
248
+ fn=process_image,
249
+ inputs=[input_image, confidence_threshold, iou_threshold],
250
+ outputs=[
251
+ output_image,
252
+ total_inferences,
253
+ hist_plot,
254
+ line_plot,
255
+ avg_inference_time,
256
+ last_inference_time,
257
+ ],
258
+ )
259
+
260
+ detect_folder_btn.click(
261
+ fn=process_folder,
262
+ inputs=[input_folder, confidence_threshold, iou_threshold],
263
+ outputs=[
264
+ output_image,
265
+ total_inferences,
266
+ hist_plot,
267
+ line_plot,
268
+ avg_inference_time,
269
+ last_inference_time,
270
+ ],
271
+ )
272
+
273
+ # Carregar métricas iniciais ao carregar a página
274
+ iface.load(
275
+ fn=detector.load_initial_metrics,
276
+ inputs=None,
277
+ outputs=[
278
+ output_image,
279
+ total_inferences,
280
+ hist_plot,
281
+ line_plot,
282
+ avg_inference_time,
283
+ last_inference_time,
284
+ ],
285
+ )
286
+
287
+ return iface
288
+
289
+
290
+ if __name__ == "__main__":
291
+ if not os.path.exists(DATABASE_PATH):
292
+ os.makedirs(DATABASE_DIR, exist_ok=True)
293
+
294
+ iface = create_gradio_interface()
295
+ iface.launch(ssr_mode=False, share=True)