Spaces:
Runtime error
Runtime error
Add application file
Browse files
app.py
ADDED
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Importing libraries for gradio app
|
2 |
+
import gradio as gr
|
3 |
+
import numpy as np
|
4 |
+
import torch
|
5 |
+
import torch.nn as nn
|
6 |
+
import torch.nn.functional as F
|
7 |
+
from torchvision import models
|
8 |
+
import torchvision.transforms as tt
|
9 |
+
from PIL import Image
|
10 |
+
|
11 |
+
|
12 |
+
|
13 |
+
|
14 |
+
|
15 |
+
# Moving both Data and Model into GPU
|
16 |
+
|
17 |
+
def get_default_device():
|
18 |
+
"""Pick GPU if available, else CPU"""
|
19 |
+
if torch.cuda.is_available():
|
20 |
+
return torch.device('cuda')
|
21 |
+
else:
|
22 |
+
return torch.device('cpu')
|
23 |
+
|
24 |
+
def to_device(data, device):
|
25 |
+
"""Move tensor(s) to chosen device"""
|
26 |
+
if isinstance(data, (list,tuple)):
|
27 |
+
return [to_device(x, device) for x in data]
|
28 |
+
return data.to(device, non_blocking=True)
|
29 |
+
|
30 |
+
class DeviceDataLoader():
|
31 |
+
"""Wrap a dataloader to move data to a device"""
|
32 |
+
def __init__(self, dl, device):
|
33 |
+
self.dl = dl
|
34 |
+
self.device = device
|
35 |
+
|
36 |
+
def __iter__(self):
|
37 |
+
"""Yield a batch of data after moving it to device"""
|
38 |
+
for b in self.dl:
|
39 |
+
yield to_device(b, self.device)
|
40 |
+
|
41 |
+
def __len__(self):
|
42 |
+
"""Number of batches"""
|
43 |
+
return len(self.dl)
|
44 |
+
|
45 |
+
|
46 |
+
|
47 |
+
# Defining our Class for just prediction
|
48 |
+
|
49 |
+
def accuracy(outputs, labels):
|
50 |
+
_, preds = torch.max(outputs, dim=1)
|
51 |
+
return torch.tensor(torch.sum(preds == labels).item() / len(preds))
|
52 |
+
|
53 |
+
class ImageClassificationBase(nn.Module):
|
54 |
+
|
55 |
+
def validation_step(self, batch):
|
56 |
+
images, labels = batch
|
57 |
+
out = self(images) # Generate predictions
|
58 |
+
loss = F.cross_entropy(out, labels) # Calculate loss
|
59 |
+
acc = accuracy(out, labels) # Calculate accuracy
|
60 |
+
return {'val_loss': loss.detach(), 'val_acc': acc}
|
61 |
+
|
62 |
+
def validation_epoch_end(self, outputs):
|
63 |
+
batch_losses = [x['val_loss'] for x in outputs]
|
64 |
+
epoch_loss = torch.stack(batch_losses).mean() # Combine losses
|
65 |
+
batch_accs = [x['val_acc'] for x in outputs]
|
66 |
+
epoch_acc = torch.stack(batch_accs).mean() # Combine accuracies
|
67 |
+
return {'val_loss': epoch_loss.item(), 'val_acc': epoch_acc.item()}
|
68 |
+
|
69 |
+
|
70 |
+
|
71 |
+
# Defining our finetuned Resnet50 Architecture with our Classification layer
|
72 |
+
|
73 |
+
class IndianFoodModelResnet50(ImageClassificationBase):
|
74 |
+
def __init__(self, num_classes, pretrained=True):
|
75 |
+
super().__init__()
|
76 |
+
# Use a pretrained model
|
77 |
+
self.network = models.resnet50(pretrained=pretrained)
|
78 |
+
# Replace last layer
|
79 |
+
self.network.fc = nn.Linear(self.network.fc.in_features, num_classes)
|
80 |
+
|
81 |
+
def forward(self, xb):
|
82 |
+
return self.network(xb)
|
83 |
+
|
84 |
+
|
85 |
+
|
86 |
+
# for prediction
|
87 |
+
@torch.no_grad()
|
88 |
+
def evaluate(model, val_loader):
|
89 |
+
model.eval()
|
90 |
+
outputs = [model.validation_step(batch) for batch in val_loader]
|
91 |
+
return model.validation_epoch_end(outputs)
|
92 |
+
|
93 |
+
|
94 |
+
|
95 |
+
# initialising our model and moving it to GPU
|
96 |
+
classes = ['burger', 'butter_naan', 'chai', 'chapati', 'chole_bhature',
|
97 |
+
'dal_makhani', 'dhokla', 'fried_rice', 'idli', 'jalebi',
|
98 |
+
'kaathi_rolls', 'kadai_paneer', 'kulfi', 'masala_dosa', 'momos',
|
99 |
+
'paani_puri', 'pakode', 'pav_bhaji', 'pizza', 'samosa']
|
100 |
+
model = IndianFoodModelResnet50(len(classes), pretrained=True)
|
101 |
+
to_device(model, device);
|
102 |
+
|
103 |
+
|
104 |
+
|
105 |
+
# loading the model
|
106 |
+
ckp_path = './indianFood-resnet50.pth'
|
107 |
+
model.load_state_dict(torch.load(ckp_path))
|
108 |
+
model.eval()
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
# image preprocessing before prediction
|
113 |
+
stats = ((0.485, 0.456, 0.406), (0.229, 0.224, 0.225))
|
114 |
+
img_tfms = tt.Compose([tt.Resize((224, 224)),
|
115 |
+
tt.ToTensor(),
|
116 |
+
tt.Normalize(*stats, inplace = True)])
|
117 |
+
|
118 |
+
def predict_image(image, model):
|
119 |
+
# Convert to a batch of 1
|
120 |
+
xb = to_device(image.unsqueeze(0), device)
|
121 |
+
# Get predictions from model
|
122 |
+
yb = model(xb)
|
123 |
+
# Pick index with highest probability
|
124 |
+
_, preds = torch.max(yb, dim=1)
|
125 |
+
# Retrieve the class label
|
126 |
+
return classes[preds[0].item()]
|
127 |
+
|
128 |
+
|
129 |
+
|
130 |
+
def classify_image(path):
|
131 |
+
img = Image.open(path)
|
132 |
+
img = img_tfms(img)
|
133 |
+
#img = img.permute(2, 0, 1)
|
134 |
+
label = predict_image(img, model)
|
135 |
+
|
136 |
+
return label
|
137 |
+
|
138 |
+
|
139 |
+
image = gr.inputs.Image(shape=(224, 224), type="filepath")
|
140 |
+
label = gr.outputs.Label(num_top_classes=1)
|
141 |
+
|
142 |
+
|
143 |
+
gr.Interface(
|
144 |
+
fn=classify_image,
|
145 |
+
inputs=image,
|
146 |
+
outputs=label,
|
147 |
+
#examples = [["images/1"], ["images/1"]],
|
148 |
+
theme = "huggingface",
|
149 |
+
interpretation="default"
|
150 |
+
).launch()
|