系列文章目录
一、关于K210 sd卡的学习教程
二、Maxi bit K210人脸识别新手教程
三、Maix bit K210 人脸识别(掉电存储)附详细代码解析
四、Maix bit K210 模型训练教程
本篇文章目录
一、怎么进行掉电存储
二、怎么运用代码实现
三、代码及功能展示
前言:
如果做一个人脸识别开门的功能,如果每次掉电重启都要重新录入人脸,那也太麻烦了,也说不过去,不现实,因此具有掉电不丢失的人脸识别功能才算是真正的人脸识别,下面是一篇关于人脸识别(掉电存储)的新手教学
一、怎么进行掉电存储
我就列举两种常用的方法:
1.使用内部 Flash: K210 芯片内部拥有 16MiB 的 Flash 存储空间,可以用来存储程序和数据。但是,Flash 的写入次数是有限的,频繁写入可能会降低其使用寿命
2.使用外部 SD 卡: K210 支持 micro SDXC 拓展存储,最大支持 128GB。可以通过编程将数据写入 SD 卡来实现掉电存储。例如,可以将人脸特征值以文本文件的形式保存到 SD 卡中,这 样即使断电,数据也不会丢失
K210 Flash内存不是很大,我不是至此把文本信息存储到内部Flash,所以我用的是第二种方式,不过俩种方法都一样,就改一下存储路径就行了
二、怎么运用代码实现
之前我已经写了一篇新手怎么使用官方提供的人脸识别模型和代码的文章,如果还有兄弟没实现过的可以去学习一下,后面的内容也是建立再它上面
人脸识别的新手学习教程https://blog.csdn.net/m0_73823991/article/details/142690595?spm=1001.2014.3001.5502所以现在打开官方提供的源代码(不知道在哪里的,上面文章有讲)
官方的代码是不具备掉电存储的,因此我们要在上面添加具有存储功能的代码,我使用的是存入sd卡的方式,那么肯定要必备sd卡 和 读卡器(可以不用)
读卡器可以方便我们删除文件和创建文件(需要入手)
既然我都是使用sd卡来存储内容了,那我也把3个人脸识别的模型要放进sd里面了,节省一下Maix bit的Flash,如果不想存入sd卡也可以,就要通过kflash_gui软件下载3个人脸识别模型到Flash,上面文章有讲怎么下载
那么怎么运用代码写入信息到sd卡呢,如果还有兄弟没接触过sd卡的可以学习下面文章
Maix bit sd的新手学习教程https://blog.csdn.net/m0_73823991/article/details/142679446?spm=1001.2014.3001.5502下面是保存人脸学习到sd卡的代码
# 将特征保存到SD卡的函数
def save_feature_to_sd(feat):
with open('/sd/features.txt','a') as f:
record =ubinascii.b2a_base64(feat) #将特征编码为Base64
f.write(record)
关于函数的解释:将人脸特征信息转化为二进制编码,存储到sd卡的features.txt文本文件,网上好像还有将人脸特征转化为字符串的形式存储,好像也可以,不过我觉得还是直接转化为二进制方式存储方便
导入模块: 确保已经导入了ubinascii模块,它是MicroPython中用于二进制和ASCII之间转换的模 块。
文件路径: 确保/sd/features.txt是正确的文件路径,并且SD卡已经正确挂载在K210上。
编码: 使用ubinascii.b2a_base64将特征数据编码为Base64格式,这样可以确保数据在写入 文件时不会因为包含特殊字符而出错。
写入模式: 使用'a'模式打开文件,这意味着数据将被追加到文件的末尾,而不是覆盖现有内容,如果sd卡没有features.txt,它会自己创建。
feature_file_exists = 0 # 文本文件存在标志
# 检查SD卡中是否存在features.txt文件
for v in os.ilistdir('/sd'): #进入sd卡查找
if v[0] == 'features.txt' and v[1] == 0x8000: #0x8000表示文件
feature_file_exists = 1 #找到标志位变1 说明文件存在
if(feature_file_exists):
print("start")
with open('/sd/features.txt','rb') as f:
s = f.readlines()#将这个文本以每一行的形式赋值为给s (因为一行代表一个人脸信息)
print(len(s))
for line in s: #迭代 依次将每一行(个)人脸特征添加到record_ftrs数组
#print(ubinascii.a2b_base64(line))
record_ftrs.append(bytearray(ubinascii.a2b_base64(line)))# 解码Base64并存储特征(二进制)
这部分代码的功能是先判断是否存在features.txt文件,如果存在,则说明先前已经运行过程序并且已经保存人脸信息到sd卡,这是我们就要把sd储存的信息复制record_ftrs数组
record_ftrs是官方代码里面已经定义的数组变量(用来存储第一次上电的人脸信息特征),因为它是在程序里面定义的,那么断电程序后程序又开始重新执行,变量清除,之前记录到数组的人脸特征就被清除掉了,这时我们就要用我们前一次上电时侯保存到sd的信息复制到record_ftrs数组,之后就能跟第一次记录的人脸比较了。
这就是掉电存储的原理,也是我们要在官方代码添加的代码
下面我自己修改添加的代码
# 加载SD卡中的模型
task_fd = kpu.load("/sd/FaceDetection.smodel")
task_ld = kpu.load("/sd/FaceLandmarkDetection.smodel")
task_fe = kpu.load("/sd/FeatureExtraction.smodel")
因为我把3个人脸识别模型放到sd卡里面了,所以调用上面这几句
如果不想放入sd卡里面,就继续用官方的代码(前提是你已经将模型下载到Flash)
task_fd = kpu.load(0x300000) # 从flash 0x200000 加载人脸检测模型
task_ld = kpu.load(0x400000) # 从flash 0x300000 加载人脸五点关键点检测模型
task_fe = kpu.load(0x500000) # 从flash 0x400000 加载人脸196维特征值模型
这里会出现一个问题,你不断运行停止会报错
因为程序一次只能加载5个模型,一开始已经加载了3个模型,你停止程序再运行,相当又重新开始执行一次代码,又要加载3个模型,就加载了6个模型,会出现报错,除非你每次重新编译一次,不过要等很久,我每次调试要等很久非常烦躁
因此我添加了自己的一些代码
#不管是否已经运行已经加载过,先释放掉模型文件,因为程序一次只能加载5个模型,加上这次就有6个了
kpu.deinit(kpu.load("/sd/FeatureExtraction.smodel"))
kpu.deinit(kpu.load("/sd/FaceLandmarkDetection.smodel"))
kpu.deinit(kpu.load("/sd/FaceDetection.smodel"))
在加载模型的前面释放掉之前加载的模型
三、代码及功能展示
下面是源码(附有详细解释)
import sensor,image,lcd # 导入相关库
from board import board_info # 导入开发板信息
import KPU as kpu # 导入KPU神经网络处理模块
import time
import os
from Maix import FPIOA,GPIO # 导入FPIOA和GPIO库
from fpioa_manager import fm # 导入fpioa_manager库
import ubinascii
import time
import utime
# 按键配置
fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 注册按键GPIO
key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 初始化按键GPIO为输入
start_processing = False # 按键按下标志
BOUNCE_PROTECTION = 50 # 按键抖动保护时间(毫秒)
# 按键中断服务程序
def set_key_state(*_):
global start_processing #告诉函数start_processing是全局变量
start_processing = True # 设置按键按下标志为True
utime.sleep_ms(BOUNCE_PROTECTION)# 等待以消除按键抖动
key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)# 设置按键中断
record_ftr=[] #空列表 用于存储当前196维特征
record_ftrs=[] #空列表 用于存储按键记录下人脸特征, 可以将特征以txt等文件形式保存到sd卡后,读取到此列表,即可实现人脸断电存储。
names = ['MR.1', 'MR.2', 'MR.3', 'Mr.4', 'Mr.5', 'Mr.6', 'Mr.7', 'Mr.8', 'Mr.9' , 'Mr.10'] # 人名标签,与上面列表特征值一一对应。
reco = ''
record = []
# 将特征保存到SD卡的函数
def save_feature_to_sd(feat):
with open('/sd/features.txt','a') as f:
record =ubinascii.b2a_base64(feat) #将特征编码为Base64
f.write(record)
st = ''
feature_file_exists = 0 # 文本文件存在标志
# 检查SD卡中是否存在features.txt文件
for v in os.ilistdir('/sd'): #进入sd卡查找
if v[0] == 'features.txt' and v[1] == 0x8000: #0x8000表示文件
feature_file_exists = 1 #找到标志位变1 说明文件存在
if(feature_file_exists):
print("start")
with open('/sd/features.txt','rb') as f:
s = f.readlines()#将这个文本以每一行的形式赋值为给s (因为一行代表一个人脸信息)
print(len(s))
for line in s: #迭代 依次将每一行(个)人脸特征添加到record_ftrs数组
#print(ubinascii.a2b_base64(line))
record_ftrs.append(bytearray(ubinascii.a2b_base64(line)))# 解码Base64并存储特征(二进制)
#下面做调试用
print(record_ftrs,names)#串行终端每次重启卡查看时候存入上次保存的人脸数据
print(len(record_ftrs))#几个人脸数据
print("end")
#不管是否已经运行已经加载过,先释放掉模型文件,因为程序一次只能加载5个模型,加上这次就有6个了
kpu.deinit(kpu.load("/sd/FeatureExtraction.smodel"))
kpu.deinit(kpu.load("/sd/FaceLandmarkDetection.smodel"))
kpu.deinit(kpu.load("/sd/FaceDetection.smodel"))
# 加载SD卡中的模型
task_fd = kpu.load("/sd/FaceDetection.smodel")
task_ld = kpu.load("/sd/FaceLandmarkDetection.smodel")
task_fe = kpu.load("/sd/FeatureExtraction.smodel")
#task_fd = kpu.load(0x300000) # 从flash 0x200000 加载人脸检测模型
#task_ld = kpu.load(0x400000) # 从flash 0x300000 加载人脸五点关键点检测模型
#task_fe = kpu.load(0x500000) # 从flash 0x400000 加载人脸196维特征值模型
clock = time.clock() # 初始化系统时钟,计算帧率
lcd.init() # 初始化lcd
sensor.reset() #初始化sensor 摄像头
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_hmirror(1) #设置摄像头镜像
sensor.set_vflip(1) #设置摄像头翻转
lcd.rotation()
sensor.run(1) #使能摄像头
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437, 6.92275, 6.718375, 9.01025) #anchor for face detect 用于人脸检测的Anchor
dst_point = [(44,59),(84,59),(64,82),(47,105),(81,105)] #standard face key point position 标准正脸的5关键点坐标 分别为 左眼 右眼 鼻子 左嘴角 右嘴角
a = kpu.init_yolo2(task_fd, 0.5, 0.3, 5, anchor) #初始化人脸检测模型
img_lcd=image.Image() # 设置显示buf
img_face=image.Image(size=(128,128)) #设置 128 * 128 人脸图片buf
a=img_face.pix_to_ai() # 将图片转为kpu接受的格式
while(1): # 主循环
img = sensor.snapshot()#从摄像头获取一张图片
clock.tick() #记录时刻,用于计算帧率
code = kpu.run_yolo2(task_fd, img)# 运行人脸检测模型
if code:# 如果检测到人脸
for i in code:# 迭代坐标框
# 绘制人脸框并裁剪人脸区域
a = img.draw_rectangle(i.rect())# 运行人脸检测模型,获取人脸坐标位置
face_cut = img.cut(i.x(), i.y(), i.w(), i.h())# 裁剪人脸部分图片到 face_cut
face_cut_128 = face_cut.resize(128, 128)# 将裁出的人脸图片 缩放到128 * 128像素
a = face_cut_128.pix_to_ai()# 将猜出图片转换为kpu接受的格式
# 人脸五点关键点检测
fmap = kpu.forward(task_ld, face_cut_128)
plist = fmap[:]# 获取关键点预测结果
le = (i.x() + int(plist[0] * i.w() - 10), i.y() + int(plist[1] * i.h()))# 计算左眼位置, 这里在w方向-10 用来补偿模型转换带来的精度损失
re = (i.x() + int(plist[2] * i.w()), i.y() + int(plist[3] * i.h()))# 计算右眼位置
nose = (i.x() + int(plist[4] * i.w()), i.y() + int(plist[5] * i.h()))#计算鼻子位置
lm = (i.x() + int(plist[6] * i.w()), i.y() + int(plist[7] * i.h()))#计算左嘴角位置
rm = (i.x() + int(plist[8] * i.w()), i.y() + int(plist[9] * i.h()))#右嘴角位置
a = img.draw_circle(le[0], le[1], 4)
a = img.draw_circle(re[0], re[1], 4)
a = img.draw_circle(nose[0], nose[1], 4)
a = img.draw_circle(lm[0], lm[1], 4)
a = img.draw_circle(rm[0], rm[1], 4)# 在相应位置处画小圆圈
# 人脸对齐到标准位置
src_point = [le, re, nose, lm, rm]# 图片中 5 坐标的位置
T = image.get_affine_transform(src_point, dst_point)# 根据获得的5点坐标与标准正脸坐标获取仿射变换矩阵
a = image.warp_affine_ai(img, img_face, T)#对原始图片人脸图片进行仿射变换,变换为正脸图像
a = img_face.ai_to_pix() # 将正脸图像转为kpu格式
# a = img.draw_image(img_face, (128,0))
del (face_cut_128) # 释放裁剪人脸部分图片
# 计算人脸特征向量
fmap = kpu.forward(task_fe, img_face) # 计算正脸图片的196维特征值
feature = kpu.face_encode(fmap[:]) #获取计算结果
reg_flag = False
scores = [] # 存储特征比对分数
for j in range(len(record_ftrs)): #迭代已存特征值
score = kpu.face_compare(record_ftrs[j], feature) #计算当前人脸特征值与已存特征值的分数
scores.append(score) #添加分数总表
max_score = 0
index = 0
for k in range(len(scores)): #迭代所有比对分数,找到最大分数和索引值
if max_score < scores[k]:
max_score = scores[k]
index = k
if max_score > 85:# 如果最大分数大于85, 可以被认定为同一个人
a = img.draw_string(i.x(), i.y(), ("%s :%2.1f" % (
names[index], max_score)), color=(0, 255, 0), scale=2)
else:
a = img.draw_string(i.x(), i.y(), ("X :%2.1f" % (
max_score)), color=(255, 0, 0), scale=2)
if start_processing:#识别到按键按下
record_ftr = feature
record_ftrs.append(record_ftr)#将当前特征添加到已知特征列表
save_feature_to_sd(record_ftr) #存到SD卡
start_processing = False
break
a = img.draw_string(0,220,"face", color=(255,0,0),scale=2) #显示未知 与 分数
a = lcd.display(img) #刷屏显示
代码功能:
可以先将代码固化到板子上,这样就可以脱机运行(上电就行)
然后进行人脸识别,摄像头对准人脸,按下板子的BOOT按键,就把人脸信息存储到sd卡了,大家可以多录入几个人脸,然后断电,再上电看看是否还能识别录入的人脸
以上就是我的学习过程记录,兄弟们可以关注点赞支持,如果对代码不理解的欢迎评论区讨论
后面还会继续更新K210 的其他内容