Spaces:
Sleeping
Sleeping
import streamlit as st | |
import torch | |
from PIL import Image | |
import matplotlib.pyplot as plt | |
from safetensors.torch import load_model | |
from transformers import pipeline | |
import torch | |
from torch import nn | |
from torch.nn import functional as func_nn | |
from einops import rearrange | |
from huggingface_hub import PyTorchModelHubMixin | |
from torchvision import models | |
# main model network | |
class SiameseNetwork(nn.Module, PyTorchModelHubMixin): | |
def __init__(self): | |
super().__init__() | |
# convolutional layer/block | |
# self.convnet = MobileNet() | |
self.convnet = models.mobilenet_v2(pretrained=True) # pretrained backbone | |
num_ftrs = self.convnet.classifier[1].in_features # get the first deimnesion of model head | |
self.convnet.classifier[1] = nn.Linear(num_ftrs, 512) # change/switch backbone linear head | |
# fully connected layer for classification | |
self.fc_linear = nn.Sequential( | |
nn.Linear(512, 128), | |
nn.ReLU(inplace=True), # actvation layer | |
nn.Linear(128, 2) | |
) | |
def single_pass(self, x) -> torch.Tensor: | |
# sinlge Forward pass for each image | |
x = rearrange(x, 'b h w c -> b c h w') # rearrange to (batch, channels, height, width) to match model input | |
output = self.convnet(x) | |
output = self.fc_linear(output) | |
return output | |
def forward(self, input_1: torch.Tensor, input_2: torch.Tensor) -> torch.Tensor: | |
# forward pass of first image | |
output_1 = self.single_pass(input_1) | |
# forward pass of second contrast image | |
output_2 = self.single_pass(input_2) | |
return output_1, output_2 | |
# pretrained model file | |
model_file = 'best_signature_mobilenet.safetensors' #config.safetensor_file | |
# Function to compute similarity | |
def compute_similarity(output1, output2): | |
return torch.nn.functional.cosine_similarity(output1, output2).item() | |
# Function to visualize feature heatmaps | |
def visualize_heatmap(model, image): | |
model.eval() | |
x = image.unsqueeze(0) # remove batch dimension | |
features = model.convnet(x) # feature heatmap learnt by model | |
heatmap = torch.mean(features, dim=1).squeeze().detach().numpy() # normalize heatmap to ndarray | |
plt.imshow(heatmap, cmap="hot") # display heatmap as plot | |
plt.axis("off") | |
return plt | |
# Load the pre-trained model from safeetesor file | |
def load_pipeline(): | |
model_file = 'best_signature_mobilenet.safetensors' #config.safetensor_file | |
# model_id = 'tensorkelechi/signature_mobilenet' | |
model = SiameseNetwork() # model class/skeleton | |
# model.load_state_dict(torch.load(model_file)) | |
model = load_model(model, model_file) | |
# model = pipeline('image-classification', model=model_id, device='cpu') | |
model.eval() | |
return model.to('cpu') | |
# Streamlit app UI template | |
st.title("Signature Forgery Detection") | |
st.write('Application to run/test signature forgery detecton model') | |
st.subheader('Compare signatures') | |
# File uploaders for the two images | |
original_image = st.file_uploader( | |
"Upload the original signature", type=["png", "jpg", "jpeg"] | |
) | |
comparison_image = st.file_uploader( | |
"Upload the signature to compare", type=["png", "jpg", "jpeg"] | |
) | |
def run_model_pipeline(model, original_image, comparison_image, threshold=0.5): | |
if original_image is not None and comparison_image is not None: # ensure both images are uploaded | |
# Preprocess images | |
img1 = Image.open(original_image).convert("RGB") # load images from file paths to PIL Image | |
img2 = Image.open(comparison_image).convert("RGB") | |
# read/reshape and normalize as numpy array | |
img1 = read_image(img1) | |
img2 = read_image(img2) | |
# convert to tensors and add batch dimensions to match model input shape | |
img1_tensor = torch.unsqueeze(torch.as_tensor(img1), 0) | |
img2_tensor = torch.unsqueeze(torch.as_tensor(img2), 0) | |
# Get model embeddings/probabilites | |
output1, output2 = model(img1_tensor, img2_tensor) | |
st.success('outputs extracted') | |
# Compute similarity | |
similarity = compute_similarity(output1, output2) | |
# Determine if it's a forgery based on determined threshold | |
is_forgery = similarity < threshold | |
# Display results | |
st.subheader("Results") | |
st.write(f"Similarity: {similarity:.2f}") | |
st.write(f"Classification: {'Forgery' if is_forgery else 'Genuine'}") | |
# Display images | |
col1, col2 = st.columns(2) # GUI columns | |
with col1: | |
st.image(img1, caption="Original Signature", use_column_width=True) | |
with col2: | |
st.image(img2, caption="Comparison Signature", use_column_width=True) | |
# Visualize heatmaps from extracted model features | |
st.subheader("Feature Heatmaps") | |
col3, col4 = st.columns(2) | |
with col3: | |
fig1 = visualize_heatmap(model, img1_tensor) | |
st.pyplot(fig1) | |
with col4: | |
fig2 = visualize_heatmap(model, img2_tensor) | |
st.pyplot(fig2) | |
else: | |
st.write("Please upload both the original and comparison signatures.") | |
# Run the model pipeline if a button is clicked | |
if st.button("Run Model Pipeline"): | |
model = load_pipeline() | |
# button click to process images | |
if st.button("Process Images"): | |
run_model_pipeline(model, original_image, comparison_image) | |