import os import gradio as gr import pandas as pd import matplotlib.pyplot as plt from PIL import Image from typing import Tuple, List from constants import MODEL_PATH, DATABASE_DIR, DATABASE_PATH from detector import SignatureDetector, download_model def create_gradio_interface(): # Download model if it doesn't exist if not os.path.exists(MODEL_PATH): download_model() # Initialize the detector detector = SignatureDetector(MODEL_PATH) css = """ .custom-button { background-color: #b0ffb8 !important; color: black !important; } .custom-button:hover { background-color: #b0ffb8b3 !important; } .container { max-width: 1200px !important; margin: auto !important; } .main-container { gap: 20px !important; } .metrics-container { padding: 1.5rem !important; border-radius: 0.75rem !important; background-color: #1f2937 !important; margin: 1rem 0 !important; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important; } .metrics-title { font-size: 1.25rem !important; font-weight: 600 !important; color: #1f2937 !important; margin-bottom: 1rem !important; } .metrics-row { display: flex !important; gap: 1rem !important; margin-top: 0.5rem !important; } """ def process_image(image: Image.Image, conf_thres: float, iou_thres: float) -> Tuple[Image.Image, str, plt.Figure, plt.Figure, str, str]: if image is None: return None, None, None, None, None, None output_image, metrics = detector.detect(image, conf_thres, iou_thres) # Create plots data hist_data = pd.DataFrame({"Time (ms)": metrics["times"]}) indices = range( metrics["start_index"], metrics["start_index"] + len(metrics["times"]) ) line_data = pd.DataFrame( { "Inference": indices, "Time (ms)": metrics["times"], "Mean": [metrics["avg_time"]] * len(metrics["times"]), } ) hist_fig, line_fig = detector.create_plots(hist_data, line_data) return ( output_image, gr.update( value=f"{metrics['total_inferences']}", container=True, ), hist_fig, line_fig, f"{metrics['avg_time']:.2f}", f"{metrics['times'][-1]:.2f}", ) def process_folder(files_paths: List[str], conf_thres: float, iou_thres: float): if not files_paths: return None, None, None, None, None, None valid_extensions = [".jpg", ".jpeg", ".png"] image_files = [ f for f in files_paths if os.path.splitext(f.lower())[1] in valid_extensions ] if not image_files: return None, None, None, None, None, None for img_file in image_files: image = Image.open(img_file) yield process_image(image, conf_thres, iou_thres) with gr.Blocks( theme=gr.themes.Soft( primary_hue="indigo", secondary_hue="gray", neutral_hue="gray" ), css=css, ) as iface: gr.HTML( """

Tech4Humans - Signature Detector

Model on HF Dataset on HF GitHub
""" ) gr.Markdown( """ This system uses the [**YOLOv8s**](https://huggingface.co/tech4humans/yolov8s-signature-detector) model, specially fine-tuned for detecting handwritten signatures in document images. 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. --- """ ) with gr.Row(equal_height=True, elem_classes="main-container"): # Left column for controls and information with gr.Column(scale=1): with gr.Tab("Single Image"): input_image = gr.Image(label="Upload your document", type="pil") with gr.Row(): clear_single_btn = gr.ClearButton([input_image], value="Clear") detect_single_btn = gr.Button( "Detect", elem_classes="custom-button" ) with gr.Tab("Image Folder"): input_folder = gr.File( label="Upload a folder with images", file_count="directory", type="filepath", ) with gr.Row(): clear_folder_btn = gr.ClearButton([input_folder], value="Clear") detect_folder_btn = gr.Button( "Detect", elem_classes="custom-button" ) with gr.Group(): confidence_threshold = gr.Slider( minimum=0.0, maximum=1.0, value=0.25, step=0.05, label="Confidence Threshold", info="Adjust the minimum confidence score required for detection.", ) iou_threshold = gr.Slider( minimum=0.0, maximum=1.0, value=0.5, step=0.05, label="IoU Threshold", info="Adjust the Intersection over Union threshold for Non-Maximum Suppression (NMS).", ) with gr.Column(scale=1): output_image = gr.Image(label="Detection Results") with gr.Accordion("Examples", open=True): gr.Examples( label="Image Examples", examples=[ ["assets/images/example_{i}.jpg".format(i=i)] for i in range( 0, len(os.listdir(os.path.join("assets", "images"))) ) ], inputs=input_image, outputs=output_image, fn=detector.detect_example, cache_examples=True, cache_mode="lazy", ) with gr.Row(elem_classes="metrics-container"): with gr.Column(scale=1): total_inferences = gr.Textbox( label="Total Inferences", show_copy_button=True, container=True ) hist_plot = gr.Plot(label="Time Distribution", container=True) with gr.Column(scale=1): line_plot = gr.Plot(label="Time History", container=True) with gr.Row(elem_classes="metrics-row"): avg_inference_time = gr.Textbox( label="Average Inference Time (ms)", show_copy_button=True, container=True, ) last_inference_time = gr.Textbox( label="Last Inference Time (ms)", show_copy_button=True, container=True, ) with gr.Row(elem_classes="container"): gr.Markdown( """ --- ## About the Project 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. ### Key Metrics: - **Precision:** 94.74% - **Recall:** 89.72% - **mAP@50:** 94.50% - **mAP@50-95:** 67.35% - **Inference Time (CPU):** 171.56 ms Complete details on the training process, hyperparameter tuning, model evaluation, dataset creation, and inference server can be found in the links below. --- **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) """ ) clear_single_btn.add([output_image]) clear_folder_btn.add([output_image]) detect_single_btn.click( fn=process_image, inputs=[input_image, confidence_threshold, iou_threshold], outputs=[ output_image, total_inferences, hist_plot, line_plot, avg_inference_time, last_inference_time, ], ) detect_folder_btn.click( fn=process_folder, inputs=[input_folder, confidence_threshold, iou_threshold], outputs=[ output_image, total_inferences, hist_plot, line_plot, avg_inference_time, last_inference_time, ], ) # Carregar métricas iniciais ao carregar a página iface.load( fn=detector.load_initial_metrics, inputs=None, outputs=[ output_image, total_inferences, hist_plot, line_plot, avg_inference_time, last_inference_time, ], ) return iface if __name__ == "__main__": if not os.path.exists(DATABASE_PATH): os.makedirs(DATABASE_DIR, exist_ok=True) iface = create_gradio_interface() iface.launch()