opencv进行人脸识别并发送到stm32进行显示

一、项目基本介绍

        刷脸支付、刷脸乘车等以人脸识别为基础的应用在我们生活中使用的越来越多。基于此设计并制作一个人脸识别系统,可以实现以下功能:处理器通过摄像头采集图像,之后进行图像处理,识别图像中特定的人脸信息。

        可实现这一功能的方式有很多,如树莓派,openmv,k210,再有就是我所采用的方案:pc端使用python调用opencv进行人脸识别,然后将人脸识别结果发送到stm32的开发板上进行显示。

所用到的软件有:

        pycharm(用于编写python代码,在这里有一点问题,目前尚未完全解决,稍后说明)

        keil5(用于编写stm32工程,我采用的库函数开发,基于正点原子的代码进行编写)

        PCtoLCD(用于汉字取模)

所用到的硬件有:

        正点原子精英版(带屏幕)

        SG90舵机

二、功能介绍

        用python调用opencv识别人脸,,将识别到的人脸与提前训练好的特征文件进行对比,根据对比结果向stm32开发板发送相应命令及数据。

        stm32的开发板上有三个LED灯(蓝灯为电源灯,红灯持续闪烁,表示开发板正在正常运行,黄灯当PC端识别到符合要求的人脸后亮起,反之熄灭),当当PC端识别到符合要求的人脸后stm32会控制舵机顺时针旋转180°,3秒后回正,同时驱动lcd屏显示识别结果及对应ID。

三、代码实现

        在放代码之前,我先说一下我遇到的pycharm的问题:

        在pycharm里面调用opencv时,无法自动补全代码,同时会标黄警告,但是运行的时候没有问题,关于这个问题,我查了很多博客,找了一些解决方法,如使用其他版本的opencv-python,修改pycharm配置等。前者没能解决我的问题,后者就是在settings中添加一下cv2的路径,配置好之后,一切可以正常使用,但是两天后,当我再次打开pycharm时,又出现问题了,并且该方法失效,我也就没在继续研究,感兴趣的小伙伴可以去研究一下。

opencv进行人脸识别并发送到stm32进行显示

opencv进行人脸识别并发送到stm32进行显示 

 

        首先是python部分的代码:

1、使用opencv训练人脸特征文件:

def train():
    # 图片路径
    path = 'jm/'
    facesSamplds = []
    idss = []
    imagePaths = [os.path.join(path, f) for f in os.listdir(path)]
    # 检测人脸
    face_detector = cv2.CascadeClassifier("opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")
    # 遍历列表中的图片
    for imagePath in imagePaths:
        # 打开图片
        PIL_img = Image.open(imagePath).convert('L')
        # 将图像转化成数组
        img_numpy = np.array(PIL_img, 'uint8')
        faces = face_detector.detectMultiScale(img_numpy)
        # 获取每张图片id
        id = int(str(os.path.split(imagePath)[1].split('.')[0])[:4])
        for x, y, w, h in faces:
            facesSamplds.append(img_numpy[y:y + h, x:x + w])
            idss.append(id)
    faces = facesSamplds
    recognizer = cv2.face.LBPHFaceRecognizer_create()
    recognizer.train(faces, np.array(idss))
    recognizer.write('trainer/trainer.yml')  # 保存文件
    print('训练成功')

直接调用已经封装好的函数进行训练。

2、使用opencv进行人脸识别,将识别到的人脸与特征文件进行对比得到confidences值,confidences值越低证明相似度越高,以此判断是否为一个人。

def perform(tx, photo=''):
    confidences = []
    ids = []
    data1 = b'A\r\n'
    recogizer = cv2.face.LBPHFaceRecognizer_create()  # 加载训练数据集文件
    recogizer.read('trainer/trainer.yml')
    if tx == 1:
        img = cv2.imread(photo)  # 准备识别图片
        flag = 1
    else:
        cap = cv2.VideoCapture(0)
        flag = cap.isOpened()
        if flag:
            ret, photo = cap.read()
            img = cv2.flip(photo, 1)
            cap.release()
        else:
            print('摄像头打开失败')
    if flag:
        g_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 转灰度
        face_detector = cv2.CascadeClassifier("opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml")
        faces = face_detector.detectMultiScale(g_img)
        img_yfs = cv2.imread("cw.jpg")
        for x, y, w, h in faces:
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)  # 人脸识别
            img_yfs = img[y-int(h*0.5):y+int(h*1.5), x-int(w*0.5):x+int(w*1.5)].copy()
            id, confidence = recogizer.predict(g_img[y:y + h, x:x + w])
            ids.append(id)
            confidences.append(confidence)

这里我设置了两种获取要识别图像的方法,一种是调用电脑摄像头进行拍摄识别,另一种的直接调用电脑上已有的图片进行识别。

3、python调用电脑串口与stm32进行通信,需要用到pyserial。这里需要注意一个问题,opencv导入的照片颜色编码顺序是BGR,每种颜色占8位。而stm32驱动屏幕显示图像为RGB格式,红色和蓝色各占五位,绿色占六位。

def exchange(photo):
    photo_i = []
    gc = []
    for i in range(60):
        for j in range(60):
            x = photo[i][j][0] >> 3
            y = photo[i][j][0] >> 2
            z = photo[i][j][0] >> 3
            w = z << 11 | (y << 5 | x)
            o = int(w >> 8)
            p = int(w % 256)
            gc.append(o)
            gc.append(p)
        if i % 2 == 1:
            photo_i.append(gc)
            gc = []
    return photo_i

因为串口的速度有限,所以使用串口传输图像的速度会比较慢。

len1 = len(ids)
        tt = 0
        for i in range(len1):
            if ids[i] != ids[i-1] or i == 0:
                if confidences[i] <= 70:
                    print('id:', ids[i], confidences[i])
                    data1 = b'A' + str(ids[i]).encode('ascii') + b'AAAAA'
                    tt = 1
        if tt == 0:
            print('无符合人脸')
            data1 = b'B'
        img_yfs = cv2.resize(img_yfs, dsize=(60, 60))
        cv2.imwrite('1001.jpg', img_yfs)
        img_fs = exchange(img_yfs)
        cv2.imshow('result', img)
        cv2.waitKey(0)
        cv2.destroyAllWindows()

        # print(data1)
        if serial.isOpen():
            print('串口打开成功')
            serial.write(data1 + b'\r\n')  # 发送id
            for xx in range(30):
                data = b'BAAAABAAAA'
                for i in img_fs[xx]:
                    data = data + i.to_bytes(1, byteorder='little', signed=False)
                # print(data)
                serial.write(data + b'\r\n')
                while True:
                    data = serial.read(20)  # 串口读20位数据
                    if data != b'':
                        break
                # print('receive  data:', data)
        else:
            print('串口打开失败')
        # 关闭串口
        serial.close()
        return ids

使用串口传输数据,一次超过256位,就会莫名其妙的出错。所以我把图像拆分开,一次传输240位。

4、将每个ID被识别到的次数进行一个记录,并存入csv文件。

    list_id = []
    list_id1 = []
    serial = serial.Serial('COM7', 115200, timeout=2)
    # train()
    id_1 = perform(1, 'jm/1002.png')  # 0 摄像头   1 图片
    # print(id_1)
    with open('jl.csv', 'r') as f:
        reader = csv.reader(f)
        for row in reader:
            list_id.append(row)
        for i in range(len(list_id)):

            if int(list_id[i][0]) == id_1[0]:
                list_s = []
                list_s.append(list_id[1][0])
                list_s.append(int(list_id[i][1])+1)
            else:
                list_s = list_id[i]
            list_id1.append(list_s)

    with open('jl.csv', 'w', newline='') as f1:
        writer = csv.writer(f1)
        for i in list_id1:
            writer.writerow(i)

5、接着是stm32的代码程序,就是控制舵机转动,小灯亮灭,及屏幕的显示,这里就不全部展示。


		if (USART_RX_STA&0x8000)
		{
			len = USART_RX_STA&0x3FFF;
			for (t=0;t<10;t++)
				sf[t]=USART_RX_BUF[t];
			for(t=0;t<4;t++)
				id1[t]=sf[t+1];
			if(sf[5]=='A')
				
				LCD_ShowString(96,140,32,16,16,id1);
			if(sf[5]=='B')
			{
				for (t=10;t<len;t++)
					gImage_01[t-10]=USART_RX_BUF[t];
					
				LCD_Show_Photo(180,130+xt*2,60,2,(u8 *)gImage_01);
				xt++;
				if(xt==30)
					xt=0;
			}		
			USART_SendData(USART1,'K');
			while (USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			
			USART_RX_STA=0;
		}
		

opencv进行人脸识别并发送到stm32进行显示 

 

四、总结

        这是我第一次接触计算机视觉,学习使用的过程中也遇到了诸多困难,opencv的使用是看的哔站课程,在学习使用pyserial的时候也遇到一个问题,就是import serial后调用函数仍会报错,网上查资料后,根据教程,卸载serial,安装pyserial,问题解决。

        电脑与stm32的通信时我遇到的最大问题,其主要原因是对C语言和python的一些数据类型不太熟悉。

        下面是我查资料是看到的一些觉得比较好的内容:

python的字符串(str)和字节串(bytes)

python 字节串及10进制、16进制相关转换

正点原子STM32串口通讯实验详解

实验工程相关文件:链接:https://pan.baidu.com/s/1awn1ZfBshcGR4HQQnZqyHw 
提取码:4r73 

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
xiaoxingxing的头像xiaoxingxing管理团队
上一篇 2023年2月25日 下午7:03
下一篇 2023年2月25日 下午7:05

相关推荐