ros-noetic+face-recognition完成相应人脸识别(检测人脸并对应姓名)

一、概述

        因为课程要求,需要在ros上完成人脸识别,并将识别出来的人脸对应相应的姓名。我们采用python中第三方库face-recognition进行实现。

二、具体实现过程

(一)版本

        所使用的系统是ubuntu20.04,ros版本是noetic,在ros上面运行的python解释器是python3.8。程序先经过window系统下进行测试,在window下测试成功后,将其迁移至ubuntu系统下。

        face-recognition是别人已经训练好的一个模型,只需要调用其接口就行。

(二)第三方库安装

        程序主要使用两个库dlib和face-recognition。在终端中输入如下命令,安装即可。

pip install dlib
pip install face_recognition

        需要注意的是,需要首先安装dlib,因为face-recognition需要依赖于dlib,安装顺序出现问题可能会导致之后程序运行出现问题。如果dlib安装不上去,可以考虑使用源码安装,或者进行换源。

        可以参考这篇博客,上面也介绍face-recognition的安装和使用。Ros平台下基于face_recognition的人脸检测及识别_ros人脸识别gitee-CSDN博客

(三) 测试版本

        1.概述

        首先编写测试版本,在ros中进行测试,主要是测试是否可以使用face-recogniton。这部分我用于提取图片特征信息的图片数量比较少。少量图片可以达到检测出人脸并对应姓名的效果,效果不太理想,极其容易误检。但能达到测试效果。

        2.代码及分析

#!/usr/bin/env python

import rospy
import cv2 
import numpy as np
import face_recognition
import os
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError
from std_msgs.msg import Header

# 创建图像订阅者
class ImageConverter:
    def __init__(self):
        self.bridge = CvBridge()
        # 训练集文件夹目录
        self.train_dir = r"/home/gfc/study_BIT/ros/study/catkin_ws/src/testPublishPhoto/images"

        # 准备空的列表来保存人脸编码和对应的人名
        # 这个需要提前完成
        self.known_face_encodings = []
        self.known_face_names = []

        # 训练数据
        self.trainFunction()

        # 计数,用于输出图像
        self.num = 0

        self.image_raw_sub = rospy.Subscriber('/usb_cam/image_raw', Image, self.subscriberPhotoCallback)

    def convert_and_publish(self, img_path):
        # 使用OpenCV读取图像
        cv_image = cv2.imread(img_path)
        # 转换OpenCV图像到ROS图像消息
        ros_image = self.bridge.cv2_to_imgmsg(cv_image, "bgr8")
        # 创建频率
        rate = rospy.Rate(1)
        # 发布图像消息
        while not rospy.is_shutdown():
            self.image_pub.publish(ros_image)
            rospy.loginfo("Image converted and published.")
            rate.sleep()
            # rospy.sp

    def subscriberPhotoCallback(self, data):
        # 这个函数是用来接受摄像头的数据的
        bridge = CvBridge()
        image = bridge.imgmsg_to_cv2(data, "bgr8")
        face_locations = face_recognition.face_locations(image)
        face_encodings = face_recognition.face_encodings(image, face_locations)
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            self.num = self.num + 1
            matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding)
            name = ""
            face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
            best_match = np.argmin(face_distances)
            if matches[best_match]:
                name = self.known_face_names[best_match]
            cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
            cv2.rectangle(image, (left, bottom - 15), (right, bottom), (0, 0, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(image, name, (left + 6, bottom - 6), font, 3.0, (255, 255, 255), 5)
            cv2.imwrite(r"/home/gfc/study_BIT/ros/study/face_detect/src/testPublishPhoto/result/out" + str(self.num) + ".jpg", image)
            cv2.waitKey(500)

    def trainFunction(self):
        for person in os.listdir(self.train_dir):
            pix_dir = os.path.join(self.train_dir, person)
            # print(pix_dir)
        #     # 遍历每个人的文件夹中的每一张照片
            for item in os.listdir(pix_dir):
                if item.endswith("jpg") or item.endswith("png"):
                    image_path = os.path.join(pix_dir, item)
                    print(image_path)
#             # 加载图片
                    image = face_recognition.load_image_file(image_path)
#             # 尝试提取一张脸的特征编码
                    face_encoding = face_recognition.face_encodings(image)
                    if face_encoding:
                        self.known_face_encodings.append(face_encoding[0])
                        self.known_face_names.append(person)

 
def main():
    # ROS节点初始化
    rospy.init_node('test', anonymous=True)
    # 创建ImageConverter实例
    image_converter = ImageConverter()
    rospy.spin()

if __name__ == "__main__":
    main()

        这里我创建了一个类用于进行处理(也可以不使用类,更简单一些)。下面对类中函数进行解析。

def trainFunction(self):
        for person in os.listdir(self.train_dir):
            pix_dir = os.path.join(self.train_dir, person)
            # print(pix_dir)
        #     # 遍历每个人的文件夹中的每一张照片
            for item in os.listdir(pix_dir):
                if item.endswith("jpg") or item.endswith("png"):
                    image_path = os.path.join(pix_dir, item)
                    print(image_path)
#             # 加载图片
                    image = face_recognition.load_image_file(image_path)
#             # 尝试提取一张脸的特征编码
                    face_encoding = face_recognition.face_encodings(image)
                    if face_encoding:
                        self.known_face_encodings.append(face_encoding[0])
                        self.known_face_names.append(person)

        trainFunction函数用于使用face-recognition对我们图片进行特征提取,并将提取的特征保存。文件夹结构如下。每一个人创建一个文件夹,对应文件夹下放置这个人的图片。

         subscriberPhotoCallback函数用于,当摄像头接收到图片信息后,进入回调函数中进行处理。在处理中将ros中的图像信息先转换为OpenCV可以处理的信息。

        在测试中,我们将OpenCV和face-recognition所处理的数据保存到电脑上用于测试。

def subscriberPhotoCallback(self, data):
        # 这个函数是用来接受摄像头的数据的
        bridge = CvBridge()
        image = bridge.imgmsg_to_cv2(data, "bgr8")
        face_locations = face_recognition.face_locations(image)
        face_encodings = face_recognition.face_encodings(image, face_locations)
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            self.num = self.num + 1
            matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding)
            name = ""
            face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
            best_match = np.argmin(face_distances)
            if matches[best_match]:
                name = self.known_face_names[best_match]
            cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
            cv2.rectangle(image, (left, bottom - 15), (right, bottom), (0, 0, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(image, name, (left + 6, bottom - 6), font, 3.0, (255, 255, 255), 5)
            cv2.imwrite(r"/home/gfc/study_BIT/ros/study/face_detect/src/testPublishPhoto/result/out" + str(self.num) + ".jpg", image)
            cv2.waitKey(500)

(四)改进版本

        1.概述

        上面的测试版本存在一些值得完善的部分,测试时候每次都需要重新对每张图片进行特征提取,只能适用于小数据集的测试,如果增加数据数量则会导致单次运行时候过长。将程序分成特征提取和实时处理两部分。另外,测试版无法在rviz当中显示,需要一点改动。

        2.特征提取部分

        将特征提取单独分成一个文件,如下所示,将特征提取结果全部写入到一个txt文件中,用于实时处理作为参数进行读取。

        代码如下,最后结果写入到txt文件中。

import cv2
import face_recognition
import os
import numpy as np

# 训练集文件夹的目录
train_dir = r"D:\test1\pythonProject1\.venv\faceDetectorTest\images"

# 准备空的列表来保存人脸编码和对应的人名
known_face_encodings = []
known_face_names = []

# 遍历训练目录中的每一个人
for person in os.listdir(train_dir):
    pix_dir = os.path.join(train_dir, person)
    # print(pix_dir)
#     # 遍历每个人的文件夹中的每一张照片
    for item in os.listdir(pix_dir):
        if item.endswith("jpg") or item.endswith("png"):
            image_path = os.path.join(pix_dir, item)
            print(image_path)
#             # 加载图片
            image = face_recognition.load_image_file(image_path)
#             # 尝试提取一张脸的特征编码
            face_encoding = face_recognition.face_encodings(image)
            if face_encoding:
                known_face_encodings.append(face_encoding[0])
                known_face_names.append(person)

# # 输出测试
# for i in range(len(known_face_encodings)):
#     print(known_face_encodings[i])
#
# for i in range(len(known_face_names)):
#     print(known_face_names[i])

# 写入txt文件
file = open("example.txt", "w")
# file.write(known_face_encodings[0])
for i in range(len(known_face_encodings)):
    for j in range(len(known_face_encodings[i])):
        file.write(str(known_face_encodings[i][j]))
        file.write(" ")
    file.write(known_face_names[i])
    file.write("\n")

        3.实时监测部分

        这个部分基于上面测试版,添加几个功能。

#!/usr/bin/env python

import rospy
import cv2 
import numpy as np
import face_recognition
import os
from sensor_msgs.msg import Image
from cv_bridge import CvBridge, CvBridgeError
from std_msgs.msg import Header

# 创建图像订阅者
class ImageConverter:
    def __init__(self):
        self.bridge = CvBridge()
        # 特征信息文件名
        self.trainData = r"/home/gfc/study_BIT/ros/study/face_detect/src/testPublishPhoto/data/example.txt"
        # 准备空的列表来保存人脸编码和对应的人名
        # 这个需要提前完成
        self.known_face_encodings = []
        self.known_face_names = []

        # 训练数据
        # self.trainFunction()

        # 读取文件中的训练数据
        self.readInfo()

        # 创建一个图像发布者对象,用来发布处理之后的数据
        self.image_pub = rospy.Publisher("/imageDeal", Image, queue_size=5)

        self.image_raw_sub = rospy.Subscriber('/usb_cam/image_raw', Image, self.subscriberPhotoCallbackUpdate)

    def trainFunction(self):
        for person in os.listdir(self.train_dir):
            pix_dir = os.path.join(self.train_dir, person)
            # print(pix_dir)
        #     # 遍历每个人的文件夹中的每一张照片
            for item in os.listdir(pix_dir):
                if item.endswith("jpg") or item.endswith("png"):
                    image_path = os.path.join(pix_dir, item)
                    print(image_path)
#             # 加载图片
                    image = face_recognition.load_image_file(image_path)
#             # 尝试提取一张脸的特征编码
                    face_encoding = face_recognition.face_encodings(image)
                    if face_encoding:
                        self.known_face_encodings.append(face_encoding[0])
                        self.known_face_names.append(person)

    def readInfo(self):
        file = open(self.trainData, "r")
        data_raw = file.readlines()

        for i in range(len(data_raw)):
            tmp = data_raw[i].strip()
            tmp = tmp.split(" ")
            tmp2 = []
            for j in range(len(tmp) - 1):
                tmp2.append(float(tmp[j]))
                self.known_face_names.append(tmp[len(tmp) - 1])
                self.known_face_encodings.append(tmp2)
        self.known_face_encodings = np.array(self.known_face_encodings)
    
    def subscriberPhotoCallbackUpdate(self, data):
        # 这个函数是用来接受摄像头的数据的
        bridge = CvBridge()
        image = bridge.imgmsg_to_cv2(data, "bgr8")
        # cv2.imshow("hahaha", image_raw)
        # cv2.waitKey(100)
        face_locations = face_recognition.face_locations(image)
        face_encodings = face_recognition.face_encodings(image, face_locations)
        for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
            self.num = self.num + 1
            matches = face_recognition.compare_faces(self.known_face_encodings, face_encoding)
            name = ""
            face_distances = face_recognition.face_distance(self.known_face_encodings, face_encoding)
            best_match = np.argmin(face_distances)
            if matches[best_match] and face_distances[best_match] < 0.30:
                name = self.known_face_names[best_match]
            cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
            cv2.rectangle(image, (left, bottom - 15), (right, bottom), (0, 0, 255), cv2.FILLED)
            font = cv2.FONT_HERSHEY_DUPLEX
            cv2.putText(image, name, (left + 6, bottom - 6), font, 3.0, (255, 255, 255), 5)
            # cv2.imwrite(r"/home/gfc/study_BIT/ros/study/face_detect/src/testPublishPhoto/result/out" + str(self.num) + ".jpg", image)
            # cv2.waitKey(500)
        # 将OpenCV的图像信息转换为ROS消息类型
        image_message = bridge.cv2_to_imgmsg(image, "bgr8")
        self.image_pub.publish(image_message)
        cv2.waitKey(3)

def main():
    # ROS节点初始化
    rospy.init_node('testTran', anonymous=True)
    # 创建ImageConverter实例
    image_converter = ImageConverter()
    # rospy.loginfo("Image converted and published.")
    rospy.spin()

if __name__ == "__main__":
    main()

        其中readInfo用于从txt文件中将数据读取出来。subscriberPhotoCallbackUpdate函数首先将图像消息转化为OpenCV可以处理的图像信息,处理之后,再将其转换为ros发布的图像信息并进行发布。

        因为需要特征提取,实时可能稍微有一些延迟。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GFCGUO

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值