Build an Image Classification API Using Django Rest Framework.
paulsaul621
Posted on November 23, 2021
Overview:
Machine Learning (ML) and data science applications are in high demand. When ML algorithms offer information before it is known, the benefits for business are significant. Integrating machine learning algorithms for inference into production systems is a technological barrier hence the need for deploying ML models as API'S.
Introduction
In this short article, we implement AI based models to detect COVID-19 in Chest X-rays and CT Scans using four Deep Learning Algorithms: VGG16, ResNet50, InceptionV3 and Xception. Note, we shall focus mainly on implementing the api and not the model creation.
To test my live endpoint, send a POST request to the following URL with an x-ray image appended to the body of the request. You will get the following sample output if request was successful:
https://alienx.tech/api/v1/xray # for the x-ray images
https://alienx.tech/api/v1/ct # for CT scans
{
"status": "success",
"data": {
"asset_id": "3e978ba830fb266978af20f2bf816f5e",
"public_id": "vacpxfywfohgfprwhrso",
"version": 1637699139,
"version_id": "c9017c7d3b28ce797edefec0b0d72796",
"signature": "e9c632b832e773cbbcb8906f93aba1d9e859d4bf",
"width": 1205,
"height": 1395,
"format": "png",
"resource_type": "image",
"created_at": "2021-11-23T20:25:39Z",
"tags": [],
"bytes": 1325222,
"type": "upload",
"etag": "86005d3c34202b10949db5569570cd16",
"placeholder": false,
"url": "http://res.cloudinary.com/prometheusapi/image/upload/v1637699139/vacpxfywfohgfprwhrso.png",
"secure_url": "https://res.cloudinary.com/prometheusapi/image/upload/v1637699139/vacpxfywfohgfprwhrso.png",
"original_filename": "covid-19-pneumonia-22",
"api_key": "138196782467569"
},
"url": "http://res.cloudinary.com/prometheusapi/image/upload/v1637699139/vacpxfywfohgfprwhrso.png",
"xception_chest_pred": "100.00% COVID",
"inception_chest_pred": "100.00% COVID",
"vgg_chest_pred": "100.00% COVID",
"resnet_chest_pred": "100.00% COVID"
}
ML Model Building
The dataset for the project was gathered from two open source Github repositories:
- Chest X-ray images (1000 images) were obtained from: https://github.com/ieee8023/covid-chestxray-dataset
- CT Scan images (750 images) were obtained from: https://github.com/UCSD-AI4H/COVID-CT/tree/master/Data-split
Four algorithms: VGG16, ResNet50, InceptionV3 and Xception were trained separately on Chest X-rays and CT Scans, giving us a total of 8 deep learning models. 80% of the images were used for training the models and the remaining 20% for testing the accuracy of the models.
The code for training the 8 models is available on my github repository. The model for the project can be found on the following google drive.
Turning the Model into an RESTFUL API
Following Python best practices, we will create a virtual environment for our project, and install the required packages.
First, create the project directory.
$ mkdir djangoapp
$ cd djangoapp
Now, create a virtual environment and install the required packages.
For macOS and Unix systems:
$ python3 -m venv myenv
$ source myenv/bin/activate
(myenv) $ pip install django requests djangorestframework tensorflow cloudinary opencv-python
For Windows:
$ python3 -m venv myenv
$ myenv\Scripts\activate
(myenv) $ pip install django requests djangorestframework tensorflow cloudinary opencv-python
Setting Up Your Django Application
First, navigate to the directory djangoapp we created and establish a Django project.
(myenv) $ django-admin startproject mainapp
This will auto-generate some files for your project skeleton:
mainapp/
manage.py
mainapp/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
Now, navigate to the directory you just created (make sure you are in the same directory as manage.py) and create your app directory.
(myenv) $ python manage.py startapp monitor
This will create the following:
monitor/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
On the mainapp/settings.py file, look for the following line and add the app we just created above.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',#new line
'monitor', #new line
]
Ensure you are in the monitor directory then create a new directory called templates and a new file called urls.py. Your directory structure of monitor application should look like this
monitor/
__init__.py
admin.py
apps.py
migrations/
templates/
__init__.py
models.py
tests.py
urls.py
views.py
Ensure your mainapp/urls.py file, add our monitor app URL to include the URLs we shall create next on the monitor app:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
#path('admin/', admin.site.urls),
path('', include('monitor.urls')),#monitor app url
]
Now, on the monitor/urls.py file, add our website there:
from django.urls import path
from .views import *
urlpatterns = [
path('api/upload/xray', UploadView.as_view(), name = 'prediction'),
path('api/upload/ct', CTUploadView.as_view(), name = 'ct_prediction'),
]
Let’s create another directory to store our machine learning model. I’ll also add the dataset to the project for those who want to achieve the whole dataset. (It is not compulsory to create a data folder.)
(venv)$ mkdir ml
(venv)$ mkdir ml/models
(venv)$ mkdir ml/data
We also need to tell Django where our machine learning model is located and also add our cloudinary configuration there. Add these lines to settings.py file:
import os
import cloudinary
cloudinary.config(
cloud_name = "prometheusapi",
api_key = "GETYOURAPIKEY",
api_secret = "GETYOURAPIKEY"
)
MODELS = os.path.join(BASE_DIR, 'ml/models')
Load Keras Model through apps.py
Load your machine learning models in apps.py so that when the application starts, the trained model is loaded only once. Otherwise, the trained model is loaded each time an endpoint is called, and then the response time will be slower.
Let’s update apps.py
import os
from django.apps import AppConfig
from django.conf import settings
from tensorflow.keras.models import load_model
from tensorflow import keras
class ResNetModelConfig(AppConfig):
name = 'resnetAPI'
MODEL_FILE = os.path.join(settings.MODELS, "resnet_chest.h5")
model = keras.models.load_model(MODEL_FILE)
class ResNetCTModelConfig(AppConfig):
name = 'resnetCTAPI'
MODEL_FILE = os.path.join(settings.MODELS, "resnet_ct.h5")
model = keras.models.load_model(MODEL_FILE)
class VGGModelConfig(AppConfig):
name = 'vggAPI'
MODEL_FILE = os.path.join(settings.MODELS, "vgg_chest.h5")
model = keras.models.load_model(MODEL_FILE)
class VGGCTModelConfig(AppConfig):
name = 'vggCTAPI'
MODEL_FILE = os.path.join(settings.MODELS, "vgg_ct.h5")
model = keras.models.load_model(MODEL_FILE)
class InceptionModelConfig(AppConfig):
name = 'inceptionv3_chestAPI'
MODEL_FILE = os.path.join(settings.MODELS, "inceptionv3_chest.h5")
model = keras.models.load_model(MODEL_FILE)
class InceptionCTModelConfig(AppConfig):
name = 'inceptionv3_chestCTAPI'
MODEL_FILE = os.path.join(settings.MODELS, "inception_ct.h5")
model = keras.models.load_model(MODEL_FILE)
class ExceptionModelConfig(AppConfig):
name = 'xception_chestAPI'
MODEL_FILE = os.path.join(settings.MODELS, "xception_chest.h5")
model = keras.models.load_model(MODEL_FILE)
class ExceptionCTModelConfig(AppConfig):
name = 'xception_chestCTAPI'
MODEL_FILE = os.path.join(settings.MODELS, "xception_ct.h5")
model = keras.models.load_model(MODEL_FILE)
Edit views.py
The last step is to update views.py. The views will be mainly responsible for two tasks:
- Process incoming POST requests.
- Make a prediction with the incoming data and give the result as a Response.
import urllib
from django.shortcuts import render
import numpy as np
from .apps import *
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, JSONParser
import cloudinary.uploader
import matplotlib.pyplot as plt
import cv2
# Create your views here.
class UploadView(APIView):
parser_classes = (
MultiPartParser,
JSONParser,
)
@staticmethod
def post(request):
file = request.data.get('picture')
upload_data = cloudinary.uploader.upload(file)
#print(upload_data)
img = upload_data['url']
#load models
resnet_chest = ResNetModelConfig.model
vgg_chest = VGGModelConfig.model
inception_chest = InceptionModelConfig.model
xception_chest = ExceptionModelConfig.model
req = urllib.request.urlopen(img)
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
image = cv2.imdecode(arr, -1) # 'Load it as it is'
#image = cv2.imread('upload_chest.jpg') # read file
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # arrange format as per keras
image = cv2.resize(image,(224,224))
image = np.array(image) / 255
image = np.expand_dims(image, axis=0)
resnet_pred = resnet_chest.predict(image)
probability = resnet_pred[0]
#print("Resnet Predictions:")
if probability[0] > 0.5:
resnet_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
resnet_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(resnet_chest_pred)
vgg_pred = vgg_chest.predict(image)
probability = vgg_pred[0]
#print("VGG Predictions:")
if probability[0] > 0.5:
vgg_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
vgg_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(vgg_chest_pred)
inception_pred = inception_chest.predict(image)
probability = inception_pred[0]
#print("Inception Predictions:")
if probability[0] > 0.5:
inception_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
inception_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(inception_chest_pred)
xception_pred = xception_chest.predict(image)
probability = xception_pred[0]
#print("Xception Predictions:")
if probability[0] > 0.5:
xception_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
xception_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(xception_chest_pred)
return Response({
'status': 'success',
'data': upload_data,
'url':img,
'xception_chest_pred':xception_chest_pred,
'inception_chest_pred':inception_chest_pred,
'vgg_chest_pred':vgg_chest_pred,
'resnet_chest_pred':resnet_chest_pred,
}, status=201)
class CTUploadView(APIView):
parser_classes = (
MultiPartParser,
JSONParser,
)
@staticmethod
def post(request):
file = request.data.get('picture')
upload_data = cloudinary.uploader.upload(file)
#print(upload_data)
img = upload_data['url']
#load models
resnet_chest = ResNetCTModelConfig.model
vgg_chest = VGGCTModelConfig.model
inception_chest = InceptionCTModelConfig.model
xception_chest = ExceptionCTModelConfig.model
req = urllib.request.urlopen(img)
arr = np.asarray(bytearray(req.read()), dtype=np.uint8)
image = cv2.imdecode(arr, -1) # 'Load it as it is'
#image = cv2.imread('upload_chest.jpg') # read file
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # arrange format as per keras
image = cv2.resize(image,(224,224))
image = np.array(image) / 255
image = np.expand_dims(image, axis=0)
resnet_pred = resnet_chest.predict(image)
probability = resnet_pred[0]
#print("Resnet Predictions:")
if probability[0] > 0.5:
resnet_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
resnet_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(resnet_chest_pred)
vgg_pred = vgg_chest.predict(image)
probability = vgg_pred[0]
#print("VGG Predictions:")
if probability[0] > 0.5:
vgg_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
vgg_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(vgg_chest_pred)
inception_pred = inception_chest.predict(image)
probability = inception_pred[0]
#print("Inception Predictions:")
if probability[0] > 0.5:
inception_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
inception_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(inception_chest_pred)
xception_pred = xception_chest.predict(image)
probability = xception_pred[0]
#print("Xception Predictions:")
if probability[0] > 0.5:
xception_chest_pred = str('%.2f' % (probability[0]*100) + '% COVID')
else:
xception_chest_pred = str('%.2f' % ((1-probability[0])*100) + '% NonCOVID')
#print(xception_chest_pred)
return Response({
'status': 'success',
'data': upload_data,
'url':img,
'xceptionCT_chest_pred':xception_chest_pred,
'inceptionCT_chest_pred':inception_chest_pred,
'vggCT_chest_pred':vgg_chest_pred,
'resnetCT_chest_pred':resnet_chest_pred,
}, status=201)
Testing our API
Create the necessary migrations then run the server:
(myenv) $ python manage.py makemigrations
(myenv) $ python manage.py migrate
(myenv) $ python manage.py runserver
Fire up Postman and make a POST request with an image appended to the body.
Thanks for staying tuned!
Posted on November 23, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.