MnLgt's picture
"added example cache"
8e67fbc
"""
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()