摘要
Face++是北京旷视科技有限公司旗下的新型视觉服务平台,Face++平台通过提供云端API、离线SDK、以及面向用户的自主研发产品形式,将人脸识别技术广泛应用到互联网及移动应用场景中,非常重要的是它对于非商业用户提供免费试用帐号注册。对于开发者来说自己开发高效准确的算法并不是一件容易的事情,所以我们利用face++在线人脸识别平台,结合ros系统,搭建一套性价比比较高的门禁系统。通过这一过程能够更加深刻的理解嵌入式、ros以及云服务的广泛应用,为使用其他的云服务API接口打下良好的基础。
将树莓派作为硬件载体,编写ros节点实现摄像头自动拍照,拍完后调用opencv进行人脸识别,如果存在人脸,便同时与本地权限人照片(拥有开门权限的人)一起上传至face++云平台,利用face++的API对两张照片进行识别和比较,从而返回消息提示是否为拥有开门权限的人,继电器做出相应动作。
准备工作
1、 在树莓派编译好ros系统。
2、 登入Face++的官方网站https://www.faceplusplus.com.cn/完成注册。
创建API Key
在应用管理-API Key界面就可以看到两个非常关键的参数,API Key和API Secret,在所有的POST调用当中均需要这两个参数。
3、 调试摄像头
(1) 插入USB摄像头,运行lsusb命令,查看是否被树莓派识别,一般插入第一个USB摄像头,会被加载为/dev/video0
(2)安装拍照软件fswebcam
sudo apt-get install fswebcam
(3)试拍一张看看
sudo fswebcam -d /dev/video0 -r 320x240 home/pi/0.jpg
4、 用命令行安装opencv相关库,在此之前请务必更新一下系统。
sudo apt-get update #更新源
sudo apt-get upgrade #更新系统
sudo apt-get install libopencv-dev
sudo apt-get install python-opencv
(1)检查opencv是否安装成功,请尝试以下命令
$ python
>>> from cv2 import cv
>>> quit()
没有收到导入错误(ImportError: No module named cv2),安装成功
收到导入错误 ①opencv没有正确安装
②Python路径没有设置正确
(2)OpenCV Python库存储在cv2.so文件中,运行命令验证是否已安装
$ locate cv2.so | grep python
得到cv2.so的路径:/usr/lib/python2.7/dist-packages/cv2.so
5、 下载Face++的官网API的python的接口SDK,作为参考api调用。
https://github.com/FacePlusPlus/python-sdk
实践流程
1、 摄像头拍照并调用opencv进行人脸识别
(1)在主循环内间隔2秒调用摄像头拍照
import os #导入python的os模块
import time #导入python的time模块
command="fswebcam -d /dev/video0 -r 600*600 /home/pi/Desktop/photo.jpeg"
os.system(command) #执行command所代表的指令,即拍照
time.sleep(2) #等待2s
但不能每次拍完照都与本地权限者照片比较一下,那样调用face++的API过于频繁,一来造成累积的网络延时过大,二来正式API应用以调用次数收费(试用API也不能无限次调用,且网络不稳定,容易出问题),因此每次拍完照都要识别一下照片中有没有人脸,如果有人脸再调用face++进行人脸比较。但同样的道理也不能用face++进行人脸识别,综上考虑,用opencv进行识别。
(2)拍好照片,并调用opencv进行人脸识别
import numpy as np #导入python的numpy模块并更名为np
import cv2 #导入python的opencv模块
#用opencv进行人脸识别,识别出有人脸才进行下一步人脸比较,否则一直拍照
face_cascade = cv2.CascadeClassifier(haarcascade_frontalface_alt2.xml) #read image读取图像
img = cv2.imread(photo)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #Convert to grayscale转换为灰度图
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
若faces返回不为空,则识别出了人脸,接下来调用Face++人脸对比API,进行比对。
2、Face++人脸对比
通过参考Face++的官方SDK函数,结合官网的API说明,选定Detect API与Compare API实现所需功能。
#利用face++的detectAPI识别opencv识别到的人脸照片
detectresult=api.detect(api_key=API_KEY,api_secret=API_SECRET,image_file=File(photo))
print_result('Detect result:', detectresult) #将结果输出到打印到命令行界面上
#调用face++的compareAPI将拍到的照片与两张本地权限者照片做比较,confidence表示拍到的照片与本地权限者照片是同一人的置信度
comparation=api.compare(api_key=API_KEY,api_secret=API_SECRET,image_file1=File(compare_photo),image_file2=File(photo))
confidence=comparation['confidence']
(1)detectAPI主要是对识别的照片进行处理,并将结果通过调用print_result函数输出到命令行界面
(2)compareAPI将拍到的照片与本地权限者照片做比较,将结果赋给confidence
(3)confidence是对比结果置信度”,经过测试,本地权限者去拍照进行比对confidence值一般在90以上,而非本地权限者拍照比对结果在60以下,所以我们将判断的阀值定为70。
3、将Face++官网的SDK修改为ros的节点。
(1)下面是修改为ros节点,SDK所需要的文件。
(2)创建face_node功能包。
$ cd ~/catkin_ws/src
$ catkin_create_pkg face_node std_msgs rospy roscpp
$ cd face_node
$ mkdir -p nodes launch photo
①haarcascade_frontalface_alt2.xml下载
人脸的Haar特征分类器就是一个XML文件,该文件中会描述人脸的Haar特征值。当然Haar特征的用途可不止可以用来描述人脸这一种,用来描述眼睛,嘴唇或是其它物体也是可以的,OpenCV有已经自带了人脸的Haar特征分类器。
下载地址:
在GitHub上找到opencv软件包,并在其中找到haarcascade_frontalface_alt2.xml
https://github.com/opencv/opencv/tree/master/data/haarcascades
②在photo的目录下放入本地权限者的照片
③ ros下python函数调用
call.py需要调用到facepp.py文件,ros不能像python一样,直接用from facepp import API, File包含,并把facepp.py和call.py放在同一个目录,.py并不是可编译的脚本文件,为了适应ros统一的编译体系,使ros识别出你写的python node,所以也加入了Python的编译流程,主要目的就是让ros和系统找到你的python程序。
1. 在face_node/src目录下建立face_node,将facepp.py复制到该目录下,并且创建_init_.py
$ cd ~/catkin_ws/src/face_node/src
$ mkdir face_node
$ touch _init_.py
2. 在~/catkin_ws/src/face_node下建立setup.py文件, 另外还需要在CMakesLists.txt中加一行catkin_python_setup()来处理setup.py,这一行应该在find_package()之后。
$vim setup.py
#!/usr/bin/env python
from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup
d = generate_distutils_setup(
packages=['face_node'],
package_dir={'': 'src'},
)
setup(**d)
$vim CMakesLists.txt
cmake_minimum_required(VERSION 2.8.3)
project(face_node)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
)
catkin_package(DEPENDS)
catkin_python_setup()
可以参考以下两个博客修改:
https://blog.csdn.net/Cyril__Li/article/details/78979253
https://blog.csdn.net/light_jiang2016/article/details/55505627
(3)将call.py修改为ros节点,下面将两个代码贴出来,可以参照修改。
call.py:
#-*- coding: utf-8 -*-
# 您需要先注册一个App,并将得到的API key和API secret写在这里。
# You need to register your App first, and enter you API key/secret.
API_KEY = "<YOUR_API_KEY>"
API_SECRET = "<YOUR_API_SECRETַ>"
# 网络图片的URL地址,调用demo前请填上内容
# The url of network picture, please fill in the contents before calling demo
face_one = 'http://bj-mc-prod-asset.oss-cn-beijing.aliyuncs.com/mc-official/images/face/demo-pic11.jpg'
# 本地图片的地址,调用demo前请填上内容
# Local picture location, please fill in the contents before calling demo
face_two = './demo.jpeg'
# 本地图片的地址,调用demo前请填上内容
# Local picture location, please fill in the contents before calling demo
face_search = './demo.jpeg'
#国际版的服务器地址
#the server of international version
api_server_international = 'https://api-us.faceplusplus.com/facepp/v3/'
# Import system libraries and define helper functions
# 导入系统库并定义辅助函数
from pprint import pformat
def print_result(hit, result):
def encode(obj):
if type(obj) is unicode:
return obj.encode('utf-8')
if type(obj) is dict:
return {encode(v): encode(k) for (v, k) in obj.iteritems()}
if type(obj) is list:
return [encode(i) for i in obj]
return obj
print hit
result = encode(result)
print '\n'.join(" " + i for i in pformat(result, width=75).split('\n'))
# First import the API class from the SDK
# 首先,导入SDK中的API类
from facepp import API, File
#创建一个API对象,如果你是国际版用户,代码为:api = API(API_KEY, API_SECRET, srv=api_server_international)
#Create a API object, if you are an international user,code: api = API(API_KEY, API_SECRET, srv=api_server_international)
api = API(API_KEY, API_SECRET)
# 创建一个Faceset用来存储FaceToken
# create a Faceset to save FaceToken
ret = api.faceset.create(outer_id='test')
print_result("faceset create", ret)
# 对图片进行检测
# detect image
Face = {}
res = api.detect(image_url=face_one)
print_result("person_one", res)
Face['person_one'] = res["faces"][0]["face_token"]
res = api.detect(image_file=File(face_two))
print_result("person_two", res)
Face['person_two'] = res["faces"][0]["face_token"]
# 将得到的FaceToken存进Faceset里面
# save FaceToken in Faceset
api.faceset.addface(outer_id='test', face_tokens=Face.itervalues())
# 对待比对的图片进行检测,再搜索相似脸
# detect image and search same face
ret = api.detect(image_file=File(face_search))
print_result("detect", ret)
search_result = api.search(face_token=ret["faces"][0]["face_token"], outer_id='test')
# 输出结果
# print result
print_result('search', search_result)
print '=' * 60
for k, v in Face.iteritems():
if v == search_result['results'][0]['face_token']:
print 'The person with highest confidence:', k
break
# 删除无用的人脸库
# delect faceset because it is no longer needed
api.faceset.delete(outer_id='test', check_empty=0)
# 恭喜!您已经完成了本教程,可以继续阅读我们的API文档并利用Face++ API开始写您自
# 己的App了!
# 旅途愉快 :)
# Congratulations! You have finished this tutorial, and you can continue
# reading our API document and start writing your own App using Face++ API!
# Enjoy :)
ros_faceselect.py:
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import rospy
from std_msgs.msg import Int32
import time #导入python的time模块
import numpy as np #导入python的numpy模块并更名为np
import cv2 #导入python的opencv模块
import os #导入python的os模块
command="fswebcam -d /dev/video0 -r 600*600 /home/pi/Desktop/photo.jpeg"
photo = "/home/pi/Desktop/photo.jpeg"
compare_photo = "/home/pi/catkin_ws/src/face_node/photo/photo_compare.jpeg"
hf_alt2_xml = "/home/pi/catkin_ws/src/face_node/haarcascade_frontalface_alt2.xml"
# 您需要先注册一个App,并将得到的API key和API secret写在这里。
# You need to register your App first, and enter you API key/secret.
API_KEY = "yJL5Mw02ZohjfElqG9BUXVDftC2AS8lw"
API_SECRET = "whXnDdVyiu0V6hL3lgWJfw0nFDiADLRX"
# Import system libraries and define helper functions
# 导入系统库并定义辅助函数
from pprint import pformat
def print_result(hit, result):
def encode(obj):
if type(obj) is unicode:
return obj.encode('utf-8')
if type(obj) is dict:
return {encode(v): encode(k) for (v, k) in obj.iteritems()}
if type(obj) is list:
return [encode(i) for i in obj]
return obj
print hit
result = encode(result)
print '\n'.join(" " + i for i in pformat(result, width=75).split('\n'))
# First import the API class from the SDK
# 首先,导入SDK中的API类
from face_node.facepp import API, File
#创建一个API对象,如果你是国际版用户,代码为:api = API(API_KEY, API_SECRET, srv=api_server_international)
#Create a API object, if you are an international user,code: api = API(API_KEY, API_SECRET, srv=api_server_international)
api = API(API_KEY, API_SECRET)
rospy.init_node('opencv_face_node')
pub = rospy.Publisher('topic_face', Int32, queue_size=3)
#下面进入拍照循环
while not rospy.is_shutdown():
os.system(command)#执行command所代表的指令,即拍照
time.sleep(2)#等待2s
#下面是用opencv进行人脸识别,识别出有人脸才进行下一步人脸比较,否则一直拍照
face_cascade = cv2.CascadeClassifier(hf_alt2_xml)
#read image读取图像
img = cv2.imread(photo)
#Convert to grayscale转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#detect face Multiscale 检测面尺度
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
if faces!=():
#利用face++的detectAPI识别刚刚拍摄的照片中的人脸,其实这句调试时能看到不少信息,方便些,可以省略
detectresult=api.detect(api_key=API_KEY,api_secret=API_SECRET,image_file=File(photo))
print_result('Detect result:', detectresult) #将结果输出到打印到命令行界面上
#调用face++的compareAPI将拍到的照片与本地权限者照片做比较,confidence表示拍到的照片与本地权限者照片是同一人的置信度
comparation=api.compare(api_key=API_KEY,api_secret=API_SECRET,image_file1=File(compare_photo),image_file2=File(photo))
confidence=comparation['confidence']
if confidence >= 70:
pub.publish(1)
else:
pub.publish(2)
else:
print "No one"
代码下载地址:https://github.com/qmros/ros_facenode
4、将Face++官网的SDK修改为ros的节点。
(1)将继电器的正、负、in端分别接树莓派GPIO的5V引脚、地引脚与11引脚,在程序中导入python的GPIO库,写入简单的GPIO引脚高低电平控制程序,观察继电器的状态。
import RPi.GPIO as GPIO#导入库
GPIO.setmode(GPIO.BOARD) # BOARD编号方式,基于插座引脚编号
GPIO.setup(11, GPIO.OUT)#将GPIO的11引脚设为输出
GPIO.output(11, 1)# 让GPIO的11引脚为高电平
GPIO.output(11, 0) # 让GPIO的11引脚为低电平
(2)通过连接arduino来控制继电器,参考网址创客智造
https://www.ncnynl.com/archives/201610/917.html
运行结果
$cd ~/catkin_ws/
$catkin_make
编译通过之后,插上USB摄像头,运行
$roslaunch face_node face_node.launch
屏幕输出打印的信息,但识别到有人,它会将结果发布到topic_face的主题,没人的时候打印No one,我们打开另外一个命令窗口运行
$rostopic echo /topic_face
查看/topic_face发布的消息,如果是1,说明拍照的人和本地权限者照片相同,2的话,说明不是本地权限者。
参考文献
[1] 在树莓派上实现face++人脸识别 - oBuZuoPiQi的博客
https://blog.csdn.net/obuzuopiqi/article/details/54428433
[2] 基于opencv与face++人脸识别+树莓派的门禁管理系统设计报告
http://www.51hei.com/bbs/dpj-103117-1.html
[3] ROS Package中的Python使用规范 - Cyril__Li的博客
https://blog.csdn.net/Cyril__Li/article/details/78979253
[4] ros 编译 Python 文件 - 蒋亮亮的博客
https://blog.csdn.net/light_jiang2016/article/details/55505627