top of page

Skin Disease Classification Using Deep Learning: Building a Web Application – Part 3

In the previous two parts of this blog series, we explored the construction of a skin disease classification model. In Part 1, we built a Convolutional Neural Network (CNN) from scratch, and in Part 2, we implemented MobileNet, a pre-trained model, to enhance our skin disease classification task using the HAM10000 dataset.


Now, in Part 3, we will take the next step and build a web application that utilizes the MobileNet model to classify skin diseases in real time. This web app allows users to upload images or provide URLs of skin lesion images and receive predictions based on our trained deep learning model.


Overview of the Web Application

The primary goal of this part is to showcase how to deploy a deep learning model using Flask, a lightweight Python web framework. By the end of this blog, you will understand how to:

  1. Load a trained deep learning model (MobileNet in our case).

  2. Preprocess the uploaded images to ensure compatibility with the model.

  3. Use Flask to build a user-friendly interface for skin disease classification.

  4. Display the classification results, including the top 3 predicted classes and their probabilities.


Step-by-Step Implementation

1. Setting Up the Environment


We begin by setting up our Flask environment and necessary dependencies. First, we load the required libraries, including TensorFlow/Keras for model inference and Flask for web app development.


import os
import uuid
import flask
import urllib
from PIL import Image
from tensorflow.keras.models import load_model
from flask import Flask, render_template, request
from tensorflow.keras.preprocessing.image import load_img, img_to_array
from keras.applications.mobilenet import preprocess_input
from keras.models import model_from_json

2. Loading the Pre-Trained MobileNet Model

In this step, we load the pre-trained MobileNet model that we trained in Part 2. The model is saved in two parts: the architecture in JSON format and the weights in an HDF5 file.

app = Flask(__name__)

j_file = open('model.json', 'r')
loaded_json_model = j_file.read()
j_file.close()
model = model_from_json(loaded_json_model)
model.load_weights('model.h5')

Here, we are loading the model architecture (model.json) and the weights (model.h5). This allows us to reuse the MobileNet model for inference.


3. Defining Allowed Image Formats

We restrict the types of image files that users can upload to ensure the model processes only compatible formats such as jpg, jpeg, and png.

ALLOWED_EXT = set(['jpg', 'jpeg', 'png', 'jfif'])
def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXT

4. Defining Prediction Logic

The predict() function handles image preprocessing and prediction. It loads the image, resizes it to 224x224 (the input size required by MobileNet), and normalizes the pixel values.

def predict(filename, model):
    img = load_img(filename, target_size=(224, 224))
    img = img_to_array(img)
    img = img.reshape(1, 224, 224, 3)
    img = img.astype('float32')
    img = img / 255.0
    result = model.predict(img)
    
    classes = ['Actinic Keratoses', 'Basal Cell Carcinoma', 'Benign Keratosis', 'Dermatofibroma',
               'Melanoma', 'Melanocytic Nevi', 'Vascular naevus']
    dict_result = {result[0][i]: classes[i] for i in range(7)}

    prob = sorted(result[0], reverse=True)[:3]
    class_result = [dict_result[prob[i]] for i in range(3)]
    prob_result = [(prob[i] * 100).round(2) for i in range(3)]

    return class_result, prob_result

The function returns the top 3 predicted classes along with their respective probabilities, allowing users to see the most likely diagnoses.


5. Building the Flask Routes

We define several routes to handle different parts of the web app, including the homepage, an about page, and the success page where the prediction results are displayed.

@app.route('/')
def home():
    return render_template("index.html")

@app.route('/success', methods=['GET', 'POST'])
def success():
    error = ''
    target_img = os.path.join(os.getcwd(), 'static/images')
    
    if request.method == 'POST':
        if request.form:
            link = request.form.get('link')
            try:
                resource = urllib.request.urlopen(link)
                unique_filename = str(uuid.uuid4()) + ".jpg"
                img_path = os.path.join(target_img, unique_filename)
                with open(img_path, 'wb') as output:
                    output.write(resource.read())
                
                class_result, prob_result = predict(img_path, model)
                predictions = {"class1": class_result[0], "class2": class_result[1], "class3": class_result[2],
                               "prob1": prob_result[0], "prob2": prob_result[1], "prob3": prob_result[2]}

            except Exception as e:
                error = 'Error accessing the image or inappropriate input'

            if not error:
                return render_template('success.html', img=unique_filename, predictions=predictions)
            else:
                return render_template('index.html', error=error)
                
        elif request.files:
            file = request.files['file']
            if file and allowed_file(file.filename):
                file.save(os.path.join(target_img, file.filename))
                img_path = os.path.join(target_img, file.filename)

                class_result, prob_result = predict(img_path, model)
                predictions = {"class1": class_result[0], "class2": class_result[1], "class3": class_result[2],
                               "prob1": prob_result[0], "prob2": prob_result[1], "prob3": prob_result[2]}

            else:
                error = "Please upload images of jpg, jpeg, or png format only"

            if not error:
                return render_template('success.html', img=file.filename, predictions=predictions)
            else:
                return render_template('index.html', error=error)
    else:
        return render_template('index.html')

Here, the app processes two kinds of input: image upload and URL input. The predictions are displayed on the success.html page with the top 3 probable classes and their probabilities.


6. Running the Flask Application

Finally, we run the application in debug mode, making it accessible for local testing and further development.

if __name__ == "__main__":
    app.run(debug=True)


Complete Code

import os
import uuid
import flask
import urllib
from PIL import Image
from numpy import number
from tensorflow.keras.models import load_model
from flask import Flask , render_template  , request , send_file
from tensorflow.keras.preprocessing.image import load_img , img_to_array
from keras.applications.mobilenet import preprocess_input, decode_predictions
from keras.models import model_from_json


app = Flask(__name__)
j_file = open('model.json', 'r')
loaded_json_model = j_file.read()
j_file.close()
model = model_from_json(loaded_json_model)
model.load_weights('model.h5')


ALLOWED_EXT = set(['jpg' , 'jpeg' , 'png' , 'jfif'])
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1] in ALLOWED_EXT

classes = [
   'Actinic Keratoses',
   'Basal Cell Carcinoma',
   'Benign Keratosis',
   'Dermatofibroma',
   'Melanoma',
   'Melanocytic Nevi',
   'Vascular naevus'

]


def predict(filename , model):
    img = load_img(filename , target_size = (224, 224))
    img = img_to_array(img)
    img = img.reshape(1,224,224,3)

    img = img.astype('float32')
    img = img/255.0
    result = model.predict(img)

    dict_result = {}
    for i in range(7):
        dict_result[result[0][i]] = classes[i]

    res = result[0]
    res.sort()
    res = res[::-1]
    prob = res[:3]
    
    prob_result = []
    class_result = []
    for i in range(3):
        prob_result.append((prob[i]*100).round(2))
        class_result.append(dict_result[prob[i]])

    return class_result , prob_result


@app.route('/')
def home():
        return render_template("index.html")


@app.route('/about/')
def about():
    return render_template("about.html")

@app.route('/contact/')
def contact():
    return render_template("contact.html")
# more changes to be made in contact

# @app.route('/about1/')
# def about():
#     return "this is about page"


@app.route('/login/')
def login():
    return render_template("login.html")
    

@app.route('/success' , methods = ['GET' , 'POST'])
def success():
    error = ''
    target_img = os.path.join(os.getcwd() , 'static/images')
    if request.method == 'POST':
        if(request.form):
            link = request.form.get('link')
            try :
                resource = urllib.request.urlopen(link)
                unique_filename = str(uuid.uuid4())
                filename = unique_filename+".jpg"
                img_path = os.path.join(target_img , filename)
                output = open(img_path , "wb")
                output.write(resource.read())
                output.close()
                img = filename

                class_result , prob_result = predict(img_path , model)

                predictions = {
                      "class1":class_result[0],
                        "class2":class_result[1],
                        "class3":class_result[2],
                        "prob1": prob_result[0],
                        "prob2": prob_result[1],
                        "prob3": prob_result[2],
                }

            except Exception as e : 
                print(str(e))
                error = 'This image from this site is not accesible or inappropriate input'

            if(len(error) == 0):
                return  render_template('success.html' , img  = img , predictions = predictions)
            else:
                return render_template('index.html' , error = error) 

            
        elif (request.files):
            file = request.files['file']
            if file and allowed_file(file.filename):
                file.save(os.path.join(target_img , file.filename))
                img_path = os.path.join(target_img , file.filename)
                img = file.filename

                class_result , prob_result = predict(img_path , model)

                predictions = {
                      "class1":class_result[0],
                        "class2":class_result[1],
                        "class3":class_result[2],
                        "prob1": prob_result[0],
                        "prob2": prob_result[1],
                        "prob3": prob_result[2],
                }

            else:
                error = "Please upload images of jpg , jpeg and png extension only"

            if(len(error) == 0):
                return  render_template('success.html' , img  = img , predictions = predictions)
            else:
                return render_template('index.html' , error = error)

    else:
        return render_template('index.html')

if __name__ == "__main__":
    app.run(debug = True)   

In this third part of the blog series, we successfully created a web application to deploy our MobileNet skin disease classification model. The Flask app enables users to upload skin lesion images or provide a URL, and in return, they receive predictions for possible skin diseases, with probabilities for the top 3 classes. This deployment illustrates how deep learning models can be integrated into real-world applications for user interaction.


Project Demo Video




By combining model building, training, and deployment, we have completed a comprehensive workflow for skin disease classification using deep learning. This solution is efficient, scalable, and ready for further enhancements, such as deployment on cloud platforms or integration with mobile applications.

If you require any assistance with this project or Machine Learning projects, please do not hesitate to contact us. We have a team of experienced developers who specialize in Machine Learning and can provide you with the necessary support and expertise to ensure the success of your project. You can reach us through our website or by contacting us directly via email or phone.


Comments


bottom of page