Facial Recognition with Raspberry Pi and Alibaba Cloud IoT Platform

1. Overall Architecture

This practical scenario is based on the serverless architecture of Alibaba Cloud.

Image for post
Image for post

Effect:

Image for post
Image for post

2. Alibaba Cloud Products and Services

Note: At the time of writing, Alibaba Cloud’s Face Recognition product is only available for Mainland China users.

3. Hardware

Image for post
Image for post

4. Raspberry Pi Device-side Development

4.1 Enable the Camera

4.2 Create a Directory Structure

1. Create the IoT folder under the /home/pi directory.

2. Under /home/pi/iot, create the photos folder, the iot.cfg configuration file, and the iot.py file.

Image for post
Image for post

4.3 Prepare a Python 3 Program

4.3.1 Dependency Installation

pip3 install oss2
pip3 install picamera
pip3 install aliyun-python-sdk-iot-client

4.3.2 iot.cfg

Create a product (Basic), register the device and obtain the trituple.

[IoT]
productKey = xxx
deviceName = xxx
deviceSecret = xxx
[OSS]
ossAccessKey = xxx
ossAccessKeySecret = xxx
ossEndpoint = xxx
ossBucketId = xxx

4.3.3 iot . py

#! /usr/bin/python3
# -*- coding: utf-8 -*-
import oss2
from picamera import PiCamera
import time
import aliyunsdkiotclient.AliyunIotMqttClient as AliyunIot
import configparser
config = configparser.ConfigParser()
config.read('iot.cfg')
# IoT
PRODUCE_KEY = config['IOT']['productKey']
DEVICE_NAME = config['IOT']['deviceName']
DEVICE_SECRET = config['IOT']['deviceSecret']
HOST = PRODUCE_KEY + '.iot-as-mqtt.cn-shanghai.aliyuncs.com'
SUBSCRIBE_TOPIC = "/" + PRODUCE_KEY + "/" + DEVICE_NAME + "/control";
# oss
OSS_AK = config['OSS']['ossAccessKey']
OSS_AK_SECRET = config['OSS']['ossAccessKeySecret']
OSS_ENDPOINT = config['OSS']['ossEndpoint']
OSS_BUCKET_ID = config['OSS']['ossBucketId']
auth = oss2. Auth(OSS_AK, OSS_AK_SECRET)
bucket = oss2. Bucket(auth, OSS_ENDPOINT, OSS_BUCKET_ID)
camera = PiCamera()
camera.resolution = (720,480)
# Take a photo first, then upload photo to oss
def take_photo():
ticks = int(time.time())
fileName = 'raspi%s.jpg' % ticks
filePath = '/home/pi/iot/photos/%s' % fileName
# take a photo
camera.capture(filePath)
# upload to oss
bucket.put_object_from_file('piPhotos/'+fileName, filePath)
def on_connect(client, userdata, flags, rc):
print('subscribe '+SUBSCRIBE_TOPIC)
client.subscribe(topic=SUBSCRIBE_TOPIC)
def on_message(client, userdata, msg):
print('receive message topic :'+ msg.topic)
print(str(msg.payload))
take_photo()
if __name__ == '__main__':
client = AliyunIot.getAliyunIotMqttClient(PRODUCE_KEY,DEVICE_NAME, DEVICE_SECRET, secure_mode=3)
client.on_connect = on_connect
client.on_message = on_message
client.connect(host=HOST, port=1883, keepalive=60)
# loop
client.loop_forever()

5. Development Using Function Compute

5.1 index.js application

const request = require('request');
const url = require('url');
const crypto = require('crypto');
const TableStore = require('tablestore');
const co = require('co');
const RPCClient = require('@alicloud/pop-core'). RPCClient;
const config = require("./config");//IoT client
const iotClient = new RPCClient({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
endpoint: config.iotEndpoint,
apiVersion: config.iotApiVersion
});
//ots client
const otsClient = new TableStore.Client({
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
endpoint: config.otsEndpoint,
instancename: config.otsInstance,
maxRetries: 20
});
const options = {
url: config.dtplusUrl,
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-type': 'application/json'
}
};
module.exports.handler = function(event, context, callback) { var eventJson = JSON.parse(event.toString()); try {
var imgUrl = config.ossEndpoint + eventJson.events[0].oss.object.key;
options.body = JSON.stringify({ type: 0, image_url: imgUrl });
options.headers.Date = new Date().toUTCString();
options.headers.Authorization = makeDataplusSignature(options);
request.post(options, function(error, response, body) { console.log('face/attribute response body' + body)
const msg = parseBody(imgUrl, body)
//
saveToOTS(msg, callback);
});
} catch (err) {
callback(null, err);
}
};
parseBody = function(imgUrl, body) { body = JSON.parse(body);
//face_rect [left, top, width, height],
const idx = parseInt(10 * Math.random() % 4);
const age = (parseInt(body.age[0])) + "age";
const expression = (body.expression[0] == "1") ? config.happy[idx] : config.normal[idx];
const gender = (body.gender[0] == "1") ? "handsome man" : "beauty";
const glass = (body.glass[0] == "1") ? "wearing glasses" : "perfect vision";
return {
'imgUrl': imgUrl,
'gender': gender,
'faceRect': body.face_rect.join(','),
'glass': glass,
'age': age,
'expression': expression
};
}
//pub msg to WebApp by IoT
iotPubToWeb = function(payload, cb) {
co(function*() {
try {
// Create a device
var iotResponse = yield iotClient.request('Pub', {
ProductKey: config.productKey,
TopicFullName: config.topicFullName,
MessageContent: new Buffer(JSON.stringify(payload)).toString('base64'),
Qos: 0
});
} catch (err) {
console.log('iotPubToWeb err' + JSON.stringify(err))
}
cb(null, payload);
});
}
saveToOTS = function(msg, cb) { var ots_data = {
tableName: config.tableName,
condition: new TableStore.Condition(TableStore.RowExistenceExpectation.IGNORE, null),
primaryKey: [{ deviceId: "androidPhoto" }, { id: TableStore.PK_AUTO_INCR }], attributeColumns: [
{ 'imgUrl': msg.imgUrl },
{ 'gender': msg.gender },
{ 'faceRect': msg.faceRect },
{ 'glass': msg.glass },
{ 'age': msg.age },
{ 'expression': msg.expression }
],
returnContent: { returnType: TableStore.ReturnType.Primarykey }
}
otsClient.putRow(ots_data, function(err, data) { iotPubToWeb(msg, cb);
});
}
makeDataplusSignature = function(options) { const md5Body = crypto.createHash('md5').update(new Buffer(options.body)).digest('base64'); const stringToSign = "POST\napplication/json\n" + md5Body + "\napplication/json\n" + options.headers.Date + "\n/face/attribute"
// step2: encryption [Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )]
const signature = crypto.createHmac('sha1', config.secretAccessKey).update(stringToSign).digest('base64');
return "Dataplus " + config.accessKeyId + ":" + signature;
}

5.2 config.js Configuration File

module.exports = {
accessKeyId: 'account ak',
secretAccessKey: 'account ak secret',
iotEndpoint: 'https://iot.cn-shanghai.aliyuncs.com',
iotApiVersion: '2018-01-20',
productKey: 'web project pk',
topicFullName: 'web project subscription recognition result topic',
// Optional. OTS is not required if you do not want to save results.
otsEndpoint: 'OTS access point',
otsInstance: 'OTS instance',
tableName: 'OTS table for storing results',
}

6. Web App Development

<! DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Alibaba Cloud IoT</title>
<style type="text/css">
body {
line-height: 1.6;
font-family: Arial, Helvetica, sans-serif;
margin: 0;
padding: 0;
background: url(http://iot-face.oss-cn-shanghai.aliyuncs.com/iot-face-yq.png) no-repeat;
background-color: #202124;
}
.face-msg {
display: inline;
font-size: 32px;
color: #5FFFF8;
padding: 30px 160px 0px 60px;
}
</style>
</head>
<body>
<div style="padding: 190px 10px 0px 360px;">
<div class="face-msg" id='glass' style="color: #5FFFF8"></div>
<div class="face-msg" id='gender' style="color: #FF5FE5"></div>
<div class="face-msg" id='age' style="color: #FFDD5F"></div>
<div class="face-msg" id='expression' style="color: #FC4D4D"></div>
</div>
<! -- -->
<div style="position: relative;padding: 145px 10px 0px 165px;">
<div style="position: absolute;">
<canvas id="myCanvas" width="720px" height="480px"></canvas>
</div>
<img id='imageUrl' src="" width="720px" height="480px" />
</div>
<script type="text/javascript" src="http://iot-face.oss-cn-shanghai.aliyuncs.com/zepto.min.js"></script>
<script src="http://iot-face.oss-cn-shanghai.aliyuncs.com/mqttws31.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() { initMqtt();
});
var client; function initMqtt() {
// Simulate device parameters
var mqttClientId = Math.random().toString(36).substr(2);
client = new Paho.MQTT.Client("public.iot-as-mqtt.cn-shanghai.aliyuncs.com", 443, mqttClientId);
// set callback handlers
var options = {
useSSL: false,
userName: 'Replace this with iotId',
password: 'Replace this with the IoT token',
keepAliveInterval: 60,
onSuccess: onConnect,
onFailure: function(e) {
console.log(e);
}
};
client.onConnectionLost = onConnectionLost;
client.onMessageDelivered = onMessageDelivered;
client.onMessageArrived = onMessageArrived;
// connect the client
client.connect(options);
}
// called when the client connects
function onConnect() {
// Once a connection has been made, make a subscription
client.subscribe("Replace with the updated subscription data topic");
}
// called when the client loses its connection
function onConnectionLost(responseObject) {
if (responseObject.errorCode ! == 0) {
console.error("onConnectionLost:", responseObject);
}
}
function onMessageArrived(message) {
fillData(JSON.parse(message.payloadString))
}
function onMessageDelivered(message) {
console.log("onMessageDelivered: [" + message.destinationName + "] --- " + message.payloadString);
}
function fillData(data) { $("#age").html(data.age);
$("#expression").html(data.expression);
$("#gender").html(data.gender);
$("#glass").html(data.glass);
$("#imageUrl").attr("src", data.imgUrl); var rect = data.faceRect.split(","); //"270,22,202,287" var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#03A9F4';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.rect(rect[0], rect[1], rect[2], rect[3]);
ctx.stroke();
};
</script>
</body>
</html>

7. Photographing Directive Trigger

/**
* package.json Add the dependency to the file: "@alicloud/pop-core": "1.5.2"
*/
const co = require('co');
const RPCClient = require('@alicloud/pop-core'). RPCClient;
const options = {
accessKey: "Replace with your AK",
accessKeySecret: "replace with your AK secret",
};
//1. Initialize the client
const client = new RPCClient({
accessKeyId: options.accessKey,
secretAccessKey: options.accessKeySecret,
endpoint: 'https://iot.cn-shanghai.aliyuncs.com',
apiVersion: '2018-01-20'
});
const params = {
ProductKey: "a1p35XsaOS7",
TopicFullName: "the topic of the camera directive",
MessageContent: new Buffer('{"action":"takephoto"}').toString('base64'),
Qos: "0"
};
co(function*() {
try {
//3. Initiate an API call
const response = yield client.request('Pub', params);
console.log(JSON.stringify(response));
} catch (err) {
console.log(err);
}
});

Reference:https://www.alibabacloud.com/blog/facial-recognition-with-raspberry-pi-and-alibaba-cloud-iot-platform_594923?spm=a2c41.13056915.0.0

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store