Intro

Welcome back to my blog. Last time we built a mobile application that takes photo of handwritten math formula and sends it to server. Today, we’ll receive that photo and prepare it for image processing step.

We’ll do all of this with Django. Django is a powerful Python Web framework that let’s you easily build web applications.

Setup

# Create the project directory
mkdir server
cd server

# Install virtualenv python package
pip install virtualenv

# Create a virtualenv to isolate our package dependencies locally
virtualenv env
source env/bin/activate  # On Windows use `env\Scripts\activate`

# Install Django into the virtualenv
pip install django numpy opencv-python

# Set up a new project with a single application
django-admin.py startproject server .  # Note the trailing '.' character
cd server
django-admin.py startapp app
cd ..

Add ‘server.app’ to your INSTALLED_APPS setting and allow all hosts.

# settings.py

ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
    ...
    'server.app',
]

The project layout should look like this:

  • env
  • server
    • app
      • migrations
      • __init__.py
      • admin.py
      • apps.py
      • models.py
      • tests.py
      • views.py
    • __init__.py
    • settings.py
    • urls.py
    • wsgi.py
  • manage.py

Code

When building a Django project you are supposed to:

  • Define Django models.

    Generally, each model represents a single database table (e.g. users table). Models consist of fields which represent table columns (e.g. username or password).

    We won’t define any Django models, because we don’t want to save uploaded images in the database.

  • Specify URLs.

    Before we send any photos to server, we need to specify a URL address.

    # urls.py
    
    from django.contrib import admin
    from django.urls import path
    from server.app import views
    urlpatterns = [
        # This is where photos will be uploaded
        path('api/photos/', views.photos),
        path('admin/', admin.site.urls),
    ]
    

    In Django, URL addresses are mapped to Python functions (views). So, the next thing we’re going to do is define photos function.

  • Define Django views.

    Django views are regular Python functions (can be a class too). Our view will receive uploaded photo, process it and respond with a prediction.

    # app/views.py
    
    from django.http import HttpResponse
    from django.views.decorators.csrf import csrf_exempt
    # Encode/decode json
    import json
    
    @csrf_exempt
    def photos(request):
    
        if request.method == "POST":
            # Decode bytes to string
            body = request.body.decode("utf-8")
            # Convert body to Json
            body = json.loads(body)
            # Get base64 string
            base64_img = body.get('photo')
    
            # Convert base64 string to opencv image
            opencv_img = base64_to_opencv_img(base64_img)
    
        return HttpResponse()
    
    

    Because request.body is a stream of bytes, we need to convert it to JSON object to easily access it’s properties.

    After we get photo property from request.body object, we want to convert it to opencv image, so that we can display or process it.

    This is a sample method that converts base64 string to an opencv image:

    # app/views.py
    
    ...
    # Encode/decode base64
    import base64
    import cv2
    import numpy as np
    
    def base64_to_opencv_img(base64_img):
        # Remove redundant prefix from base64 string
        data = base64_img.split(',')[1]
        # Convert base64 string to stream of bytes
        bin_data = base64.b64decode(data)
        # Create a numpy array that represents our image
        img_array = np.fromstring(bin_data, np.uint8)
        # Convert numpy array to opencv image
        opencv_img = cv2.imdecode(img_array, 0) # 2nd argument specifies image mode (0=grayscale)
        return opencv_img
    

    Now, we can display our grayscale image by doing the following:

    cv2.imshow('image', opencv_img)
    cv2.waitKey(0)
    

How to run?

Navigate to the folder that contains manage.py file. The following command starts a Web server on the local machine on port 8000.

python manage.py runserver 0.0.0.0:8000

Make sure that images_endpoint variable inside photoService service (client application) points to the IP address of your local machine.

// js/services.js

app.service('photoService', ['$http', '$q', function($http, $q) {
    // Address where images are to be sent
    this.images_endpoint = 'http://IP_address:8000/api/photos/';

    ...
}])

Results

video-results