Spaces:
Running
Running
""" | |
This script creates a Gradio GUI for detecting and classifying signature blocks in document images | |
using the SignatureBlockModel. It loads example images from the /assets directory, displays | |
bounding boxes in the result image, and shows signature crops with labels in a gallery. | |
""" | |
import gradio as gr | |
import numpy as np | |
from PIL import Image | |
from typing import Tuple, List | |
import os | |
from scripts.signature_blocks import SignatureBlockModel | |
ASSETS_DIR = os.path.join(os.path.dirname(__file__), "assets") | |
def process_image( | |
image: np.ndarray, | |
) -> Tuple[np.ndarray, str, List[Tuple[np.ndarray, str]]]: | |
""" | |
Process an input image using the SignatureBlockModel. | |
Args: | |
image (np.ndarray): Input image as a numpy array. | |
Returns: | |
Tuple[np.ndarray, str, List[Tuple[np.ndarray, str]]]: Processed image, status, and list of crops with labels. | |
""" | |
# Convert numpy array to PIL Image | |
pil_image = Image.fromarray(image) | |
# Initialize the model | |
model = SignatureBlockModel(pil_image) | |
# Get processed image with boxes | |
image_with_boxes = model.draw_boxes() | |
# Get signature crops | |
signature_crops = create_signature_crops(model) | |
# Determine status | |
labels = model.get_labels() | |
if not labels.any(): | |
status = "Unsigned" | |
elif all(label == 1 for label in labels): | |
status = "Fully Executed" | |
elif all(label == 2 for label in labels): | |
status = "Unsigned" | |
else: | |
status = "Partially Executed" | |
return np.array(image_with_boxes), status, signature_crops | |
def create_signature_crops(model: SignatureBlockModel) -> List[Tuple[np.ndarray, str]]: | |
""" | |
Create individual images for each signature crop with label and score information. | |
Args: | |
model (SignatureBlockModel): The initialized SignatureBlockModel. | |
Returns: | |
List[Tuple[np.ndarray, str]]: List of tuples containing crop images and labels. | |
""" | |
boxes = model.get_boxes() | |
scores = model.get_scores() | |
labels = model.get_labels() | |
classes = model.classes | |
crop_data = [] | |
for box, label, score in zip(boxes, labels, scores): | |
crop = model.extract_box(box) | |
# resized_crop = resize_crop(crop) | |
label_text = f"{classes[label]}, Score: {score:.2f}" | |
crop_data.append((crop, label_text)) | |
return crop_data | |
def resize_crop(crop: np.ndarray, max_height: int = 200) -> np.ndarray: | |
""" | |
Resize a crop to a maximum height while maintaining aspect ratio. | |
Args: | |
crop (np.ndarray): Input crop as a numpy array. | |
max_height (int): Maximum height for the crop. | |
Returns: | |
np.ndarray: Resized crop. | |
""" | |
crop_image = Image.fromarray(crop) | |
aspect_ratio = crop_image.width / crop_image.height | |
new_height = min(crop_image.height, max_height) | |
new_width = int(new_height * aspect_ratio) | |
resized_crop = crop_image.resize((new_width, new_height), Image.LANCZOS) | |
return np.array(resized_crop) | |
def load_examples(): | |
""" | |
Load example images from the /assets directory. | |
Returns: | |
List[List[str]]: List of example image paths. | |
""" | |
examples = [] | |
for filename in os.listdir(ASSETS_DIR): | |
if filename.lower().endswith((".png", ".jpg", ".jpeg")): | |
examples.append([os.path.join(ASSETS_DIR, filename)]) | |
return examples | |
with gr.Blocks() as demo: | |
gr.Markdown("# Signature Block Detection") | |
gr.Markdown("Upload a document image to detect and classify signature blocks.") | |
with gr.Row(): | |
input_image = gr.Image(label="Upload Document Image", type="numpy", height=500) | |
output_image = gr.Image(label="Processed Image", type="numpy", height=500) | |
signature_crops = gr.Gallery( | |
label="Signature Crops", | |
show_label=True, | |
elem_id="gallery", | |
columns=[6], | |
rows=[1], | |
allow_preview=True, | |
object_fit="contain", | |
height=250, | |
) | |
with gr.Row(): | |
status_box = gr.Textbox(label="Document Status") | |
process_btn = gr.Button("Process Image") | |
examples = gr.Examples( | |
examples=load_examples(), | |
fn=process_image, | |
inputs=input_image, | |
outputs=[output_image, status_box, signature_crops], | |
cache_examples=True, | |
) | |
process_btn.click( | |
fn=process_image, | |
inputs=input_image, | |
outputs=[output_image, status_box, signature_crops], | |
) | |
if __name__ == "__main__": | |
demo.launch() | |