Create a custom Appsody stack with support for Python Flask and OpenCV

This tutorial shows you how to create a custom Appsody stack with Python Flask and OpenCV support. In order to achieve this, you create a copy of an existing Python Flask stack and add support for OpenCV. We will then build and test the stack with a sample code.

Appsody is an open source project that helps you develop containerized applications for the cloud. OpenCV is an open source computer vision and machine learning software library.

Learning objectives

In this tutorial, you will learn to:

  • Create a custom Appsody stack with support for Python Flask and OpenCV.
  • Build and test the stack with sample code.
  • Deploy an image to an OpenShift cluster on IBM Cloud.

Prerequisites

You’ll need Appsody installed and an IBM Cloud account.

Estimated time

Completing this tutorial should take about 30 minutes.

Steps

  1. Create copy of python flask stack.
  2. Modify the Python flask stack to add support for OpenCV.
  3. Build the stack.
  4. Create an appsody project using the new stack.
  5. Test.
  6. Deploy to OpenShift cluster on IBM Cloud.
1

Create copy of an Appsody Python Flask stack

Run the command to make a copy:

appsody stack create python-flask-opencv --copy incubator/python-flask

You will see a python-flask-opencv folder created.

2

Modify the Python Flask stack to add support for OpenCV

Now, let’s modify the stack.

$ cd python-flask-opencv

Open the file `Dockerfile-stack under the image folder. Add the following lines after the first line FROM python:3.7 as seen below. Save the file.

FROM python:3.7

RUN apt-get update
RUN apt-get clean
RUN pip install --upgrade pip; \
    pip install opencv-python

Note: opencv-python is a wrapper package for OpenCV Python bindings. Please refer to the Python documentation for more details on usage and licensing.

Next, open the file Dockerfile under folder image/project. Add the following lines after the first line FROM python:3.7as seen below.

FROM python:3.7

RUN apt-get update
RUN apt-get clean
RUN pip install --upgrade pip; \
    pip install opencv-python

That’s it! You’ve added support for OpenCV. In the next steps, I’ll show you how to package the stack.

3

Build the stack

Go to the python-flask-opencv folder and run the following command:

appsody stack package

This builds the stack into a local Appsody repository (called dev.local) where you can create Appsody projects based on the newly created stack.

4

Create an Appsody project using the new stack

  1. Create a new empty folder and name it. I named the one here example. Create an Appsody project inside the newly created folder by running the following command:

      $ cd example
      $ appsody init dev.local/python-flask-opencv
    
  2. Create a folder templates.

      $ mkdir templates
      $ cd templates
    
  3. Add a file index.html to templates folder with the following content:

     <!doctype html>
     <html lang="en">
       <h2>Convert image to grayscale using Python, OpenCV.</h2>
        <h5>Upload an image.</h5>
        </p>
        </p>
        <div class="upload-form">
          <form action = "/uploader" method = "POST"
            enctype = "multipart/form-data">
            <input type = "file" name = "file" />
            <input value="Convert" style="background-color:green;color:white" type = "submit"/>
          </form>
        </div>
     </html>
    
  4. Add a file display.html" totemplates` folder with the below content:

     <!doctype html>
     <html lang="en">
         <div>
             <h2>Converted Image!</h2>
             <img src="getimage">
         </div>
     </html>
    
  5. Modify the __init__.py file

    1. Change the import statements and add required import statements

      Make changes to existing import statements and add other required import statements. The import statements section should look like the one below:

      from flask import Flask, redirect, render_template, request
      from werkzeug import secure_filename
      import os
      import sys
      from flasgger import Swagger
      from server import app
      from server.routes.prometheus import track_requests
      from os import path
      from userapp import improcess
      
    2. Create and initialize variables

      Add the below statements below the import section:

      app=Flask(__name__,template_folder='templates')
      
    3. Create a function and add a route to index.html

      @app.route("/home")
      def home():
      return render_template("index.html")
      
    4. Create a function and add a route for uploader

      @app.route('/uploader', methods = ['POST'])
      @track_requests
      def upload_file():
      if request.method == 'POST':
         f = request.files['file']
         # create a secure filename
         filename = f.filename
         # save file
         filepath = os.path.join("./userapp/",filename)
         f.save(filepath)
         # convert image to grayscale
         filepath_gray = improcess.convert_grayscale(filepath);
         return render_template("display.html")
      
    5. Create a function and add a route for getimage

      @app.route('/getimage', methods = ['GET'])
      @track_requests
      def getimage():
      filename_gray = "gray.png"
      filepath_gray = os.path.join("./userapp/",filename_gray)
      if path.exists(filepath_gray):
         f = open(filepath_gray, "rb");
         print(f)
         print("image found!")
         contents = f.read()
         return contents;
      else:
         print("no image found!")
         return "";
      

      Note: The function getimage is used for displaying the converted grayscale image.

  6. Create a new Python module improcess.py and add the following lines. Save the file as improcess.py.

     import cv2
     import os
     from os import path
    
     def convert_grayscale(filepath):
         ''' Convert image to grayscale'''
         image = cv2.imread(filepath,0)
         filename_gray = "gray.png"
         filepath_gray = os.path.join("./userapp/",filename_gray)
         print(filepath_gray)
         cv2.imwrite(filepath_gray,image)
         return filepath_gray
    
    5

    Test

Now I’ll show you how to build and run the project.

  1. Go to the example folder and run the following commands:

    $ appsody build
    $ appsody run
    
  2. Open the URL: http://localhost:8080/home.

    home

    We will test the service for the image:

    test

  3. Click on Browse and upload the image.

  4. Click on Convert after selecting the image from a local folder.

    The converted image displays as shown below:

    result

  5. You can get the health of the container at: http://localhost:8080/health

     {"status":"UP"}
    
  6. You can view the metrics for the application at http://localhost:8080/metrics

     ...
     # TYPE requests_for_routes_total counter
     requests_for_routes_total{endpoint="/uploader",method="POST"} 1.0
     requests_for_routes_total{endpoint="/getimage",method="GET"} 1.0
     # TYPE requests_for_routes_created gauge
     requests_for_routes_created{endpoint="/uploader",method="POST"} 1.5744054798223705e+09
     requests_for_routes_created{endpoint="/getimage",method="GET"} 1.5744054799005709e+09
    
6

Deploy to an OpenShift cluster on IBM Cloud

The appsody build command will locally build a Docker image of your Appsody project.

  1. Run the command below to view the Docker image.

     $ docker images example
     REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
     example             latest              e04e2c3f263f        12 seconds ago      1.09GB
    
  2. Log in to OpenShift.

     oc login https://xxxx.containers.cloud.ibm.com:xxxxx --token=xxxxxxxxxxx
    
  3. Create a route for Docker registry if not created already.

      $ oc project default
      $ oc get svc
    

    The output appears as shown below:

      NAME               TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)                      AGE
      docker-registry    ClusterIP      172.21.xxx.xx    <none>           5000/TCP                     18h
      kubernetes         ClusterIP      172.21.x.x       <none>           443/TCP,53/UDP,53/TCP        18h
      myfirstosdeploy    ClusterIP      172.21.xx.xxx    <none>           5000/TCP                     17h
      registry-console   ClusterIP      172.21.xxx.xxx   <none>           9000/TCP                     18h
      router             LoadBalancer   172.21.xx.x      169.47.xxx.xxx   80:31297/TCP,443:30385/TCP   18h
    
  4. Run the following command to create a route to the Docker registry.

      $ oc create route reencrypt --service=docker-registry
    
  5. Check the create route details.

      $ oc get route docker-registry
    

    The output appears as shown below:

      NAME              HOST/PORT                                                                                                            PATH      SERVICES          PORT       TERMINATION   WILDCARD
      docker-registry   docker-registry-default.clustersiteam-5290cxxxxxxxxxxd1b85xxx-0001.us-east.containers.appdomain.cloud                 docker-registry   5000-tcp   reencrypt     None
    

    Note the Docker registry URL that is displayed with the pattern: docker-registry-default.<cluster_name>-<ID_string>.<region>.containers.appdomain.cloud.

  6. Set it as a variable.

     export IMAGE_REGISTRY=docker-registry-default.<cluster_name>-<ID_string>.<region>.containers.appdomain.cloud
    
  7. Log into the Docker registry.

      docker login -u $(oc whoami) -p $(oc whoami -t) $IMAGE_REGISTRY
    
  8. Create a new project.

     oc new-project example
    

    Give root permissions to the default service account.

     oc adm policy add-scc-to-user anyuid -z default
    
  9. Deploy the image to the registry on OpenShift.

     appsody deploy --tag example/example:latest --push-url $IMAGE_REGISTRY --push --pull-url docker-registry.default.svc:5000
    
  10. Create a new OpenShift app.

     oc new-app --image-stream=example --name=example
    
  11. Expose the route.

     oc expose svc/example
    

    You can see the application deployed under the example project on the OpenShift web console.

    OS

Conclusion

Hopefully the steps in this tutorial helped you undersatnd how to create a custom Appsody stack with Python Flask and OpenCV support. Extending Appsody stacks to support other open projects is one of its benefits, and adding OpenCV support to the Python Flask stack enables you to use computer vision and machine learning software within containerized platforms.