话不多说,上视频先看效果!

这里我使用cv_bridge + face_racognition在ROS系统下进行人脸识别项目的开发。关于face_recognition的内容大家可以自行百度,在CSDN可以直接下载整个项目。https://github.com/ageitgey/face_recognition

我这里也提供了我自己整理好的代码文件,github的太全面,不适合小白来看。想玩这个的话,需要熟练Python,熟悉Python OpenCV即可。

关于此次的项目设计,总共有三个功能包,人脸数据采集、人脸模型训练、人脸实时识别,原理的话就是通过usb_cam节点在ROS系统喜爱发布图像话题,通过Python编程订阅后使用cv_bridge转成Python OpenCV的数据格式,进行处理即可。

FaceData是数据集,里面有人脸样本数据、样本数据计算好的模型和haarcascade_frontalface_default分类器,最后这个是OpenCV自带的,我们区网上下载即可。

face_data_collect是人脸数据采集功能包,负责采集人脸图片,并自动进行处理,做成适合训练的样本数据。

face_training是人脸模型训练功能包,执行这个功能包之后会将人脸样本数据进行计算,做出来一个以yml为后缀名的模型文件。

face_recognition是人脸实时识别功能包,这里会调用我们训练好的模型进行实时人脸识别。

face_recognition_Demo是我整理github的face_recognition之后的代码,github的代码过于全面,这里我只留下来了最基础的Python例程。

接下来我们依次来解读这三个功能包。

face_data_collect人脸数据采集功能包

先贴代码,如下。

#!/usr/bin/env python3
#coding:utf-8

import rospy
import cv2
import os
import sys
import easygui
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image

face_detector = cv2.CascadeClassifier('/home/ubuntu/catkin_ws/src/face_recognition/FaceData/haarcascade_frontalface_default.xml')
face_id = int(easygui.enterbox(msg="请输入当前人脸ID.",title="Warning"))
print('\n 正在拍摄人脸数据,请等待 ...')
count = 0

class image_converter:
	def __init__(self):    
		# 创建cv_bridge,声明图像的发布者和订阅者
		self.image_pub = rospy.Publisher("cv_bridge_image", Image, queue_size=1)
		self.bridge = CvBridge()
		self.image_sub = rospy.Subscriber("/usb_cam/image_raw", Image, self.callback)

	def callback(self,data):
		# 使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式
		try:
			cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
		except CvBridgeError as e:
			print(str(e))
		
		global count
		
		gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)		
		faces = face_detector.detectMultiScale(gray, 1.3, 5)
		
		for (x, y, w, h) in faces:
			cv2.rectangle(cv_image, (x, y), (x+w, y+w), (255, 0, 0))
			count += 1
			cv2.imwrite('/home/ubuntu/catkin_ws/src/face_recognition/FaceData/face_data/User.' + str(face_id) + '.' + str(count) + '.jpg', gray[y: y + h, x: x + w])
		cv2.imshow('FaceDataCollect', cv_image)
			
		cv2.waitKey(1)
		
		if count >= 100:
			cv2.destroyAllWindows()

		# 再将opencv格式额数据转换成ros image格式的数据发布
		try:
			self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))
		except CvBridgeError as e:
			print(str(e))

if __name__ == '__main__':
	try:
		# 初始化ros节点
		rospy.init_node("face_data_collect")
		rospy.loginfo("Starting face_data_collect node")
		image_converter()
		rospy.spin()
	except KeyboardInterrupt:
		print("Shutting down face_data_collect node.")
		cv2.destroyAllWindows()

这段代码的作用是订阅/usb_cam/image_raw话题,并通过cv_bridge转换成Python OpenCV的数据格式进行处理。我们这里先将图像转成灰度图,然后使用人脸分类器进行人脸检测,检测到人脸的话会将当前人脸区域保存然后显示实时画面,否则直接显示实时画面。有个count变量是对采集到的人脸数据进行计数,这里我选择采集100章图片作为样本,样本数量越多、质量越好,训练的模型精准度就高,识别效果就越好。

face_training是人脸模型训练功能包

先贴代码,如下。

#!/usr/bin/env python3
#coding:utf-8

import numpy as np
from PIL import Image
import os
import cv2

path = '/home/ubuntu/catkin_ws/src/face_recognition/FaceData/face_data'

recognizer = cv2.face.LBPHFaceRecognizer_create()
detector = cv2.CascadeClassifier("/home/ubuntu/catkin_ws/src/face_recognition/FaceData/haarcascade_frontalface_default.xml")

def getImagesAndLabels(path):
	imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
	faceSamples = []
	ids = []
	for imagePath in imagePaths:
		PIL_img = Image.open(imagePath).convert('L')   # convert it to grayscale
		img_numpy = np.array(PIL_img, 'uint8')
		id = int(os.path.split(imagePath)[-1].split(".")[1])
		faces = detector.detectMultiScale(img_numpy)
		for (x, y, w, h) in faces:
			faceSamples.append(img_numpy[y:y + h, x: x + w])
			ids.append(id)
	return faceSamples, ids

print('正在训练人脸模型数据,请等待...')
faces, ids = getImagesAndLabels(path)
recognizer.train(faces, np.array(ids))

recognizer.write(r'/home/ubuntu/catkin_ws/src/face_recognition/FaceData/face_trainer/trainer.yml')
print("{0}组人脸模型数据训练完成,程序退出".format(len(np.unique(ids))))

这部分的代码没有涉及到ROS,只是读取上一步采集好的数据,通过recognition来进行训练,输出一个train.yml的模型文件即可。我们在识别的程序里面会调用这个文件进行人脸识别。

face_recognition人脸实时识别功能包

先贴代码,如下。

#!/usr/bin/env python3
#coding:utf-8

import rospy
import cv2
from cv_bridge import CvBridge, CvBridgeError
from sensor_msgs.msg import Image

recognizer = cv2.face.LBPHFaceRecognizer_create()
recognizer.read('/home/ubuntu/catkin_ws/src/face_recognition/FaceData/face_trainer/trainer.yml')
faceCascade = cv2.CascadeClassifier("/home/ubuntu/catkin_ws/src/face_recognition/FaceData/haarcascade_frontalface_default.xml")
font = cv2.FONT_HERSHEY_SIMPLEX

idnum = 0
names = ['LiHua', 'Alex','Walt']
minW = 0.1*640
minH = 0.1*480

class image_converter:
	def __init__(self):    
		# 创建cv_bridge,声明图像的发布者和订阅者
		self.image_pub = rospy.Publisher("cv_bridge_image", Image, queue_size=1)
		self.bridge = CvBridge()
		self.image_sub = rospy.Subscriber("/usb_cam/image_raw", Image, self.callback)

	def callback(self,data):
		# 使用cv_bridge将ROS的图像数据转换成OpenCV的图像格式
		try:
			cv_image = self.bridge.imgmsg_to_cv2(data, "bgr8")
		except CvBridgeError as e:
			print(str(e))

		gray = cv2.cvtColor(cv_image, cv2.COLOR_BGR2GRAY)
		faces = faceCascade.detectMultiScale(
			gray,
			scaleFactor=1.2,
			minNeighbors=5,
			minSize=(int(minW), int(minH))
		)
		
		for (x, y, w, h) in faces:
			cv2.rectangle(cv_image, (x, y), (x+w, y+h), (0, 255, 0), 2)
			idnum, confidence = recognizer.predict(gray[y:y+h, x:x+w])

			if confidence < 100:
				idnum = names[idnum]
				confidence = "{0}%".format(round(100 - confidence))
			else:
				idnum = "unknown"
				confidence = "{0}%".format(round(100 - confidence))

			cv2.putText(cv_image, str(idnum), (x+5, y-5), font, 1, (0, 0, 255), 1)
			cv2.putText(cv_image, str(confidence), (x+5, y+h-5), font, 1, (0, 0, 0), 1)

		cv2.imshow('camera', cv_image)
        
		cv2.waitKey(1)

		# 再将opencv格式额数据转换成ros image格式的数据发布
		try:
			self.image_pub.publish(self.bridge.cv2_to_imgmsg(cv_image, "bgr8"))
		except CvBridgeError as e:
			print(str(e))

if __name__ == '__main__':
	try:
		# 初始化ros节点
		rospy.init_node("face_rocognition")
		rospy.loginfo("Starting face_rocognition node")
		image_converter()
		rospy.spin()
	except KeyboardInterrupt:
		print("Shutting down face_rocognition node.")
		cv2.destroyAllWindows()

这里的代码实现的就是大家最后看到的效果啦!原理也很简单,订阅/usb_cam/image_raw话题,并通过cv_bridge转换成Python OpenCV的数据格式进行处理。主要是将图像数据传入到我们声明的模型当中即可,通过x、y、w、h四个数值来确定人脸并绘制矩形将人脸框出。我们以confidence准确值来判断当前的人脸信息,并进行标注。


大家也可以自己跑一下,算是一个很简单的Demo吧~

编程的话,主要还是Python + OpenCV,然后使用了ROS的通讯机制——话题通讯、ROS的功能包——cv_bridge和usb_cam、ROS的工具——image_view,当然这个过程肯定是没有离开ROS社区的帮助,毕竟调试不出bug的代码是很罕见的。

最后吧,我还是说一句话~

ROS = 通讯机制+工具箱+功能包+社区

ROS只是一个工具,它不是核心。你的思想、你的代码才是项目的核心,一定一定一定不要本末倒置。

初入门者,切记!