#AzureIoT – How to create an Azure IoT module from an Azure #CustomVision project 👀 4/N

Buy Me A Coffee

Hi !

Today’s post will be mostly focused on code. And on the main tasks

  • Merge and create a new docker definition file.
  • Merge Azure IoT project files with the exported Azure Custom Vision project
  • Update the main Python app to run as Azure IoT Module and also perform Computer Vision analysis.

Azure IoT Module Docker

Let’s start with the docker file. The following docker file

  • Uses an ARM32V7 with Python 3.7 installed
  • Install requirements from the requirements.txt file. This includes the Azure IoT req: azure-iot-device~=2.0.0
  • The install the required libraries to perform Custom Vision tasks. I know this maybe part of the requirements.txt file. I’ll upgrade this in next iteration.
  • Opens the 80 port
  • Run the app from the python file [main.py]
FROM arm32v7/python:3.7-slim-buster


COPY requirements.txt ./
RUN pip install -r requirements.txt

# Code added to support Custom Vision app in the Azure IoT Module
RUN apt update && apt install -y libjpeg62-turbo libopenjp2-7 libtiff5 libatlas-base-dev libgl1-mesa-glx
RUN pip install absl-py six protobuf wrapt gast astor termcolor keras_applications keras_preprocessing --no-deps
RUN pip install numpy==1.16 tensorflow==1.13.1 --extra-index-url 'https://www.piwheels.org/simple' --no-deps
RUN pip install flask pillow --index-url 'https://www.piwheels.org/simple'

# Expose the port

COPY . .

# use default launch for the python app
# Run the flask server for the endpoints
CMD python -u main.py

Good start, now let’s merge files.

Merging files

I copied all the Custom Vision ARM32v7 export files to the root of the Azure IoT module. We don’t need the app.py file, so the final list this one.

  • cvexport.manifest
  • Dockerfile.amd64
  • Dockerfile.arm32v7
  • Dockerfile.arm64v8
  • Dockerfile.amd64.debug
  • Dockerfile.arm32v7.debug
  • Dockerfile.arm64v8.debug
  • labels.txt
  • main.py
  • metadata_properties.json
  • model.pb
  • module.json
  • predict.py
  • requirements.txt

Now let’s work to the Python app.

Python app

The final code is running with 2 main threads:

  • Register and interact with the Azure IoT environment
  • Run an WebApp for Computer Vision

# Bruno Capuano – 2021
# Azure IoT Module running an Azure Custom Vision webapp
import time
import os
import sys
import asyncio
from six.moves import input
import threading
from azure.iot.device.aio import IoTHubModuleClient
# Custom Vision for ARM imports
import json
import io
# Imports for the REST API
from flask import Flask, request, jsonify
# Imports for image procesing
from PIL import Image
# Imports for prediction
from predict import initialize, predict_image, predict_url
print ( "init flask app" )
app = Flask(__name__)
# 4MB Max image size limit
app.config['MAX_CONTENT_LENGTH'] = 4 * 1024 * 1024
print ( "Load and intialize the model" )
# Load and intialize the model
async def initAzureIoTModule():
if not sys.version >= "3.5.3":
raise Exception( "The sample requires python 3.5.3+. Current version of Python: %s" % sys.version )
print ( "IoT Hub Client for Python" )
# The client object is used to interact with your Azure IoT hub.
module_client = IoTHubModuleClient.create_from_edge_environment()
# connect the client.
await module_client.connect()
# define behavior for receiving an input message on input1
async def input1_listener(module_client):
while True:
input_message = await module_client.receive_message_on_input("input1") # blocking call
print("the data in the message received on input1 was ")
print("custom properties are")
print("forwarding mesage to output1")
await module_client.send_message_to_output(input_message, "output1")
# Schedule task for C2D Listener
listeners = asyncio.gather(input1_listener(module_client))
print ( "Azure IoT Module registerd and waiting for messages.")
except Exception as e:
print('EXCEPTION:', str(e))
# =============================================
# webapp for custom vision
# =============================================
async def webAppCustomVision():
print ( "Start CV webapp" )
# Run the server
app.run(host='', port=8089)
# Default route just shows simple text
def index():
return 'CustomVision.ai simple drawing server.'
# Like the CustomVision.ai Prediction service /image route handles either
# – octet-stream image file
# – a multipart/form-data with files in the imageData parameter
@app.route('/image', methods=['POST'])
@app.route('/<project>/image', methods=['POST'])
@app.route('/<project>/image/nostore', methods=['POST'])
@app.route('/<project>/classify/iterations/<publishedName>/image', methods=['POST'])
@app.route('/<project>/classify/iterations/<publishedName>/image/nostore', methods=['POST'])
@app.route('/<project>/detect/iterations/<publishedName>/image', methods=['POST'])
@app.route('/<project>/detect/iterations/<publishedName>/image/nostore', methods=['POST'])
def predict_image_handler(project=None, publishedName=None):
imageData = None
if ('imageData' in request.files):
imageData = request.files['imageData']
elif ('imageData' in request.form):
imageData = request.form['imageData']
imageData = io.BytesIO(request.get_data())
img = Image.open(imageData)
results = predict_image(img)
return jsonify(results)
except Exception as e:
print('EXCEPTION:', str(e))
return 'Error processing image', 500
# Like the CustomVision.ai Prediction service /url route handles url's
# in the body of hte request of the form:
# { 'Url': '<http url>'}
@app.route('/url', methods=['POST'])
@app.route('/<project>/url', methods=['POST'])
@app.route('/<project>/url/nostore', methods=['POST'])
@app.route('/<project>/classify/iterations/<publishedName>/url', methods=['POST'])
@app.route('/<project>/classify/iterations/<publishedName>/url/nostore', methods=['POST'])
@app.route('/<project>/detect/iterations/<publishedName>/url', methods=['POST'])
@app.route('/<project>/detect/iterations/<publishedName>/url/nostore', methods=['POST'])
def predict_url_handler(project=None, publishedName=None):
image_url = json.loads(request.get_data().decode('utf-8'))['url']
results = predict_url(image_url)
return jsonify(results)
except Exception as e:
print('EXCEPTION:', str(e))
return 'Error processing image'
if __name__ == "__main__":
loop = asyncio.get_event_loop()

So far, so good. In the next post, I’ll deploy and test the module on an Azure IoT Device and start to think on adding some configuration options, and refactor and improve!


Happy coding!


El Bruno

More posts in my blog ElBruno.com.

More info in https://beacons.ai/elbruno

Azure ☁ IoT

Create an Azure IoT Module using Raspberry Pi and Grove Sensors

  1. Raspberry Pi + Grove Sensors, read temperature and humidity values
  2. Raspberry Pi + Grove Sensors, send temperature and humidity values as telemetry to Azure IoT Hub
  3. Raspberry Pi + Grove Sensors, create a Azure IoT Module to send temperature and humidity values as telemetry to Azure IoT Hub
  4. Raspberry Pi + Grove Sensors, publish and use the Azure IoT Module
  5. Raspberry Pi + Grove Sensors, notes on build and configuration
  6. Raspberry Pi + Grove Sensors, details on how to send a telemetry message and sample messages

Create an Azure IoT Module from Azure Custom Vision project

¿Con ganas de ponerte al día?

En Lemoncode te ofrecemos formación online impartida por profesionales que se baten el cobre en consultoría:

1 comment

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: