1. 程式人生 > >樹莓派執行ROS實現face++人臉識別

樹莓派執行ROS實現face++人臉識別

摘要

  Face++是北京曠視科技有限公司旗下的新型視覺服務平臺,Face++平臺通過提供雲端API、離線SDK、以及面向使用者的自主研發產品形式,將人臉識別技術廣泛應用到網際網路及移動應用場景中,非常重要的是它對於非商業使用者提供免費試用帳號註冊。對於開發者來說自己開發高效準確的演算法並不是一件容易的事情,所以我們利用face++線上人臉識別平臺,結合ros系統,搭建一套價效比比較高的門禁系統。通過這一過程能夠更加深刻的理解嵌入式、ros以及雲服務的廣泛應用,為使用其他的雲服務API介面打下良好的基礎。   將樹莓派作為硬體載體,編寫ros節點實現攝像頭自動拍照,拍完後呼叫opencv進行人臉識別,如果存在人臉,便同時與本地許可權人照片(擁有開門許可權的人)一起上傳至face++雲平臺,利用face++的API對兩張照片進行識別和比較,從而返回訊息提示是否為擁有開門許可權的人,繼電器做出相應動作。

準備工作

1、 在樹莓派編譯好ros系統。 2、 登入Face++的官方網站https://www.faceplusplus.com.cn/完成註冊。 Face++ 註冊介面   建立API Key 建立API Key

  在應用管理-API Key介面就可以看到兩個非常關鍵的引數,API Key和API Secret,在所有的POST呼叫當中均需要這兩個引數。API Key 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 API介面

實踐流程

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實現所需功能。 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所需要的檔案。 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

face_node包目錄     ①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()

  (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"

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引腳為低電平

執行結果

	$cd ~/catkin_ws/
	$catkin_make

編譯通過之後,插上USB攝像頭,執行

	$roslaunch face_node face_node.launch

螢幕輸出列印的資訊,但識別到有人,它會將結果釋出到topic_face的主題,沒人的時候列印No one,我們開啟另外一個命令視窗執行

	$rostopic echo /topic_face

檢視/topic_face釋出的訊息,如果是1,說明拍照的人和本地許可權者照片相同,2的話,說明不是本地許可權者。

參考文獻