danielle2035's picture
Add file
d32e728
Raw
History Blame Contribute Delete
5.71 kB
import sys
from pathlib import Path
import numpy as np
import requests
from io import BytesIO
from PIL import Image
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from notifications.serializers import ImageClassifierSerializer, ClassificationResultSerializer
import torch
import torch.nn as nn
from torchvision import transforms
import tensorflow as tf
# PATHS
BASE_DIR = Path(__file__).resolve().parent.parent
PYTORCH_PATH = BASE_DIR / "models" / "pytorch_model.pth"
TENSORFLOW_PATH = BASE_DIR / "models" / "model_best.keras"
CLASSES = ["buildings", "forest", "glacier", "mountain", "sea", "street"]
CONFIDENCE_THRESHOLD = 0.6
# PYTORCH MODEL
class CNN(nn.Module):
def __init__(self, num_classes=6):
super().__init__()
self.block1 = self._block(3, 32)
self.block2 = self._block(32, 64)
self.block3 = self._block(64, 128)
self.block4 = self._block(128, 256)
self.gap = nn.AdaptiveAvgPool2d(1)
self.fc1 = nn.Linear(256, 128)
self.fc2 = nn.Linear(128, num_classes)
self.dropout = nn.Dropout(0.5)
def _block(self, in_c, out_c):
return nn.Sequential(
nn.Conv2d(in_c, out_c, 3, padding=1),
nn.BatchNorm2d(out_c),
nn.ReLU(),
nn.Conv2d(out_c, out_c, 3, padding=1),
nn.BatchNorm2d(out_c),
nn.ReLU(),
nn.MaxPool2d(2)
)
def forward(self, x):
x = self.block1(x)
x = self.block2(x)
x = self.block3(x)
x = self.block4(x)
x = self.gap(x)
x = x.view(x.size(0), -1)
x = self.dropout(torch.relu(self.fc1(x)))
x = self.fc2(x)
return x
# TRANSFORM
pytorch_transform = transforms.Compose([
transforms.Resize((150, 150)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406],
[0.229, 0.224, 0.225])
])
_pytorch_model = None
_tensorflow_model = None
def get_pytorch_model():
global _pytorch_model
if _pytorch_model is None:
model = CNN(num_classes=len(CLASSES))
model.load_state_dict(torch.load(str(PYTORCH_PATH), map_location="cpu"))
model.eval()
_pytorch_model = model
return _pytorch_model
def get_tensorflow_model():
global _tensorflow_model
if _tensorflow_model is None:
_tensorflow_model = tf.keras.models.load_model(str(TENSORFLOW_PATH), compile=False)
return _tensorflow_model
class ImageClassifierAPIView(APIView):
def post(self, request):
serializer = ImageClassifierSerializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=400)
try:
if "image" in serializer.validated_data:
image = Image.open(serializer.validated_data["image"]).convert("RGB")
else:
image_url = serializer.validated_data["image_url"]
if not image_url.startswith("http"):
return Response({"error": "Invalid URL"}, status=400)
resp = requests.get(image_url, timeout=10)
resp.raise_for_status()
image = Image.open(BytesIO(resp.content)).convert("RGB")
model_name = serializer.validated_data.get("model", "pytorch")
if model_name == "pytorch":
result = self._predict_pytorch(image)
else:
result = self._predict_tensorflow(image)
result["model_used"] = model_name
return Response(result, status=200)
except Exception as e:
print("ERROR:", e)
return Response({"error": str(e)}, status=400)
def _predict_pytorch(self, image):
model = get_pytorch_model()
tensor = pytorch_transform(image).unsqueeze(0)
with torch.no_grad():
outputs = model(tensor)
probs = torch.nn.functional.softmax(outputs, dim=1)
values, indices = torch.topk(probs, k=len(CLASSES), dim=1)
values = values.squeeze().cpu().numpy()
indices = indices.squeeze().cpu().numpy()
confidence = float(values[0])
all_probs = [
{"class": CLASSES[indices[i]], "probability": float(values[i])}
for i in range(len(CLASSES))
]
if confidence < CONFIDENCE_THRESHOLD:
return {
"predicted_class": "unknown",
"confidence": confidence,
"all_probabilities": all_probs
}
return {
"predicted_class": CLASSES[indices[0]],
"confidence": confidence,
"all_probabilities": all_probs
}
# TENSORFLOW
def _predict_tensorflow(self, image):
model = get_tensorflow_model()
img = image.resize((130, 130))
arr = np.array(img, dtype=np.float32)
arr = arr[:, :, ::-1]
arr = np.expand_dims(arr, 0)
preds = model.predict(arr, verbose=0)[0]
sorted_idx = np.argsort(preds)[::-1]
confidence = float(preds[sorted_idx[0]])
all_probs = [
{"class": CLASSES[i], "probability": float(preds[i])}
for i in sorted_idx
]
if confidence < CONFIDENCE_THRESHOLD:
return {
"predicted_class": "unknown",
"confidence": confidence,
"all_probabilities": all_probs
}
return {
"predicted_class": CLASSES[sorted_idx[0]],
"confidence": confidence,
"all_probabilities": all_probs
}