|
import streamlit as st |
|
from PIL import Image |
|
import torch |
|
import torchvision.transforms as transforms |
|
import matplotlib.pyplot as plt |
|
import torch.nn as nn |
|
import torch.nn.functional as F |
|
|
|
|
|
def accuracy(outputs, labels): |
|
_, preds = torch.max(outputs, dim=1) |
|
return torch.tensor(torch.sum(preds == labels).item() / len(preds)) |
|
|
|
class ImageClassificationBase(nn.Module): |
|
def training_step(self, batch): |
|
images, labels = batch |
|
out = self(images) |
|
loss = F.cross_entropy(out, labels) |
|
return loss |
|
|
|
def validation_step(self, batch): |
|
images, labels = batch |
|
out = self(images) |
|
loss = F.cross_entropy(out, labels) |
|
acc = accuracy(out, labels) |
|
return {'val_loss': loss.detach(), 'val_acc': acc} |
|
|
|
def validation_epoch_end(self, outputs): |
|
batch_losses = [x['val_loss'] for x in outputs] |
|
epoch_loss = torch.stack(batch_losses).mean() |
|
batch_accs = [x['val_acc'] for x in outputs] |
|
epoch_acc = torch.stack(batch_accs).mean() |
|
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()} |
|
|
|
def epoch_end(self, epoch, result): |
|
print("Epoch [{}], train_loss: {:.4f}, val_loss: {:.4f}, val_acc: {:.4f}".format( |
|
epoch, result['train_loss'], result['val_loss'], result['val_acc'])) |
|
|
|
|
|
class CnnModel(ImageClassificationBase): |
|
def __init__(self): |
|
super().__init__() |
|
self.network = nn.Sequential( |
|
nn.Conv2d(3, 16, kernel_size=3, padding=1), |
|
nn.BatchNorm2d(16), |
|
nn.ReLU(), |
|
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=1), |
|
nn.BatchNorm2d(32), |
|
nn.ReLU(), |
|
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1), |
|
nn.ReLU(), |
|
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1), |
|
nn.BatchNorm2d(128), |
|
nn.ReLU(), |
|
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1), |
|
nn.ReLU(), |
|
nn.Conv2d(256, 16, kernel_size=1, stride=1, padding=0), |
|
nn.MaxPool2d(2, 2), |
|
nn.Dropout(p=0.25), |
|
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=0), |
|
nn.ReLU(), |
|
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=0), |
|
nn.BatchNorm2d(64), |
|
nn.ReLU(), |
|
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=0), |
|
nn.ReLU(), |
|
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=0), |
|
nn.BatchNorm2d(256), |
|
nn.ReLU(), |
|
nn.Conv2d(256, 10, kernel_size=1, stride=1, padding=1), |
|
nn.AvgPool2d(kernel_size=6), |
|
nn.Flatten(), |
|
nn.Softmax(dim=1) |
|
) |
|
|
|
def forward(self, xb): |
|
return self.network(xb) |
|
|
|
|
|
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
|
model = CnnModel() |
|
model.load_state_dict(torch.load('svhn_cnn.pth', map_location=device)) |
|
model = model.to(device) |
|
model.eval() |
|
|
|
|
|
def predict_and_display(image_path, model): |
|
|
|
transform = transforms.Compose([ |
|
transforms.Resize((28, 28)), |
|
transforms.ToTensor(), |
|
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5]) |
|
]) |
|
image = Image.open(image_path).convert('RGB') |
|
image_tensor = transform(image).unsqueeze(0).to(device) |
|
|
|
|
|
with torch.no_grad(): |
|
outputs = model(image_tensor) |
|
_, predicted = torch.max(outputs, 1) |
|
|
|
|
|
st.image(image, caption='Uploaded Image', use_container_width=True) |
|
st.write(f"Predicted Digit: {predicted.item()}") |
|
|
|
|
|
st.title("Digit Prediction from Image") |
|
st.write("Upload an image of a digit for prediction:") |
|
|
|
uploaded_image = st.file_uploader("Choose an image...", type="png") |
|
|
|
if uploaded_image is not None: |
|
|
|
image_path = "uploaded_image.png" |
|
with open(image_path, "wb") as f: |
|
f.write(uploaded_image.getbuffer()) |
|
|
|
|
|
predict_and_display(image_path, model) |
|
|