# Instructions to build a quick skeleton Flask app for Cat/Dog Classifier This guide will help you to create a simple skeleton of flask app which allow you to upload a image file and call tensorflow to predict the image for you. Then result will be displayed on classify HTML page. Remember, there are a lot of potential improvement ideas for your flask apps: - Change POST request to Ajax so the page will be never reloaded - Style up the website (specially the upload form) - Add more pages about yourself and model - Add options to use multiple different models - Go further, make one-page web application (everything handled by Ajax) ## Project structure * simple_flask * static * images * script.js * style.css * templates * classify.html * home.html * uploads * app.py ### Make the main flask directory called simple_flask Ubuntu/Mac ```bash mkdir simple_flask ``` Windows ```bash mkdir "simple_flask" ``` ### Change directory ```bash cd simple_flask ``` ### Create the following folders and files Ubuntu/Mac ```bash mkdir static templates uploads static/images touch app.py static/script.js static/style.css templates/classify.html templates/home.html ``` Windows ```bash mkdir "static" "templates" "uploads" "static/images" copy nul "app.py" copy nul "static/script.js" copy nul "static/style.css" copy nul "templates/classify.html" copy nul "templates/home.html" ``` ### Add this routing and prediction code in **flask_app/app.py** ```python import os from flask import Flask, render_template, request from flask import send_from_directory import numpy as np import tensorflow as tf app = Flask(__name__) dir_path = os.path.dirname(os.path.realpath(__file__)) UPLOAD_FOLDER = 'uploads' STATIC_FOLDER = 'static' # Load model cnn_model = tf.keras.models.load_model(STATIC_FOLDER + '/' + 'catdog_classifier_Xception.h5') IMAGE_SIZE = 224 # Preprocess an image def preprocess_image(image): image = tf.image.decode_jpeg(image, channels=3) image = tf.image.resize(image, [IMAGE_SIZE, IMAGE_SIZE]) image /= 255.0 # normalize to [0,1] range return image # Read the image from path and preprocess def load_and_preprocess_image(path): image = tf.io.read_file(path) return preprocess_image(image) # Predict & classify image def classify(model, image_path): preprocessed_imgage = load_and_preprocess_image(image_path) preprocessed_imgage = tf.reshape(preprocessed_imgage, (1,IMAGE_SIZE ,IMAGE_SIZE ,3)) prob = cnn_model.predict(preprocessed_imgage) label = "Cat" if prob >= 0.5 else "Dog" classified_prob = prob if prob >= 0.5 else 1 - prob return label, classified_prob # home page @app.route('/') def home(): return render_template('home.html') @app.route('/classify', methods=['POST','GET']) def upload_file(): if request.method == 'GET': return render_template('home.html') else: file = request.files["image"] upload_image_path = os.path.join(UPLOAD_FOLDER, file.filename) print(upload_image_path) file.save(upload_image_path) label, prob = classify(cnn_model, upload_image_path) prob = round((prob[0][0] * 100), 2) return render_template('classify.html', image_file_name = file.filename, label = label, prob = prob) @app.route('/classify/<filename>') def send_file(filename): return send_from_directory(UPLOAD_FOLDER, filename) if __name__ == '__main__': app.debug = True app.run(debug=True) app.debug = True ``` ### Add some css styles in **flask_app/static/style.css** ```css h2 { letter-spacing: 2px; font-size: 1.5rem } form { padding-top:20px; font-size: 24px; } .classify{ text-align:center; margin:auto; margin-top:200px; } .image-prediction, .text-prediction{ margin-top: 100px; text-align:center; } .pred_img { width: 400px; height: 400px; } #pred { font-size: 60px; text-transform: uppercase; } #label { color: red; font-size: 4rem; text-transform: uppercase; } #classify_button { font-size: 2rem; font-weight: bold; } ``` ## HTML Template ### Create template for our home page at **flask_app/templates/home.html** > *Hints: most of this code can be automaticaly generated with a VSCode Extension called flask-snippets by only writting "fapp"* > ![](https://i.imgur.com/npmdsha.png =500x) ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catto & Doggo Classifier</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <link rel="stylesheet" href="/static/style.css" type="text/css"> </head> <body> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a class="navbar-brand" href="#">Simple Catto Doggo</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarText"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a> </li> </ul> <span class="navbar-text"> Simple Flask With Upload Functionality </span> </div> </nav> <div class="container-fluid"> <div class="classify"> <div> <h2> Hi, Please upload your dog or cat image here! </h2> </div> <div> <form action="/classify" method="post" enctype="multipart/form-data"> <div class="file has-name is-centered"> <label class="file-label"> <input class="file-input" type="file" id="image" name="image"> </label> </div> <br> <input type="submit" value="Classify" class="btn btn-danger" name="image" id="classify_button"> </form> </div> </div> </div> <script src="/static/script.js"></script> </body> </html> ``` > *Hints: most of this code can be automaticaly generated with VSCode Extensions called Bootstrap 4, Font awesome 4, Font Awesome 5 Free & Pro snippets by only writting "b4-$"* ### Create template for our classify/prediction page at **flask_app/templates/classify.html** ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Catto & Doggo Classifier</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> <link rel="stylesheet" href="/static/style.css" type="text/css"> </head> <body> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a class="navbar-brand" href="#">Simple Catto Doggo</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarText"> <ul class="navbar-nav mr-auto"> <li class="nav-item active"> <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a> </li> </ul> <span class="navbar-text"> Simple Flask With Upload Functionality </span> </div> </nav> <div class="container-fluid"> <div class="row"> <div class="col image-prediction"> <img class="pred_img" src="{{ url_for('send_file', filename=image_file_name)}}" /> </div> <div class="col text-prediction"> <h1 id="pred"> This is a </h1> <h1 class="box" id="label"> {{ label }} </h1> <h2> Confidence: <span>{{prob}}%</span> </h2> <br> <a href="/" class="btn btn-primary">Back to Home</a> </div> </div> </div> <script src="/static/script.js"></script> </body> </html> ``` ### Add trained Xception model of Mina have built before: Download the saved model in the link: https://drive.google.com/file/d/1W4uEBbCLFTsdYk6dWeEkzte1jk0X95XP/view?usp=sharing And copy that catdog_classifier_Xception.h5 into our **simple_flask/static/** folder **For fun, you should train your own model and replace this model with your model.** ### Run the flask app Change directory ```bash flask run ``` On Docker, you need to expose that IP address to public so run: ```bash flask run --host=0.0.0.0 ``` :confetti_ball: Congratulation, you have just created your Flask app. Here is the link of your Flask app: http://127.0.0.1:5000 Or http://localhost:5000