一、项目基本介绍
刷脸支付、刷脸乘车等以人脸识别为基础的应用在我们生活中使用的越来越多。基于此设计并制作一个人脸识别系统,可以实现以下功能:处理器通过摄像头采集图像,之后进行图像处理,识别图像中特定的人脸信息。
可实现这一功能的方式有很多,如树莓派,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时,又出现问题了,并且该方法失效,我也就没在继续研究,感兴趣的小伙伴可以去研究一下。
首先是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的使用是看的哔站课程,在学习使用pyserial的时候也遇到一个问题,就是import serial后调用函数仍会报错,网上查资料后,根据教程,卸载serial,安装pyserial,问题解决。
电脑与stm32的通信时我遇到的最大问题,其主要原因是对C语言和python的一些数据类型不太熟悉。
下面是我查资料是看到的一些觉得比较好的内容:
实验工程相关文件:链接:https://pan.baidu.com/s/1awn1ZfBshcGR4HQQnZqyHw
提取码:4r73
文章出处登录后可见!