Canmv K210开发板案例——人脸识别

学习目标:

  • 在Canmv IDE环境下使用K210学习人脸识别功能,将摄像头检测到的人脸通过按键进行保存、系统进行编号,若下次识别到保存编号过的人脸,则通过变色框框出识别到的人脸,并标出序号与置信度

学习内容:

1、导入相关库,并初始化摄像头和LCD显示屏

import sensor, image, time, lcd
import gc                          # 内存管理模块
from maix import KPU               # 加速AI计算
from maix import GPIO, utils       # 使用Pin脚的GPIO功能、基础功能
from fpioa_manager import fm       # 管理内部功能和引脚映射关系的功能模块
from board import board_info       # 板子board_info类


lcd.init()  # 初始化LCD显示屏
sensor.reset()  # 复位并初始化摄像头
sensor.set_pixformat(sensor.RGB565)  # 设置摄像头输出格式为 RGB565
sensor.set_framesize(sensor.QVGA) # 设置摄像头输出大小为 QVGA (320x240)
sensor.skip_frames(time = 1000) # 等待摄像头稳定
clock = time.clock() # 创建一个clock对象,用来计算帧率

2、新建人脸特性图像大小为64*64,建立标准人脸关键点坐标

feature_img = image.Image(size=(64,64), copy_to_fb=False) # 人脸特性图像为64*64图输入,这里初始化一个image
feature_img.pix_to_ai()  # 对rgb565的image生成ai运算需要的r8g8b8格式存储

# 标准人脸关键点坐标
FACE_PIC_SIZE = 64
dst_point =[(int(38.2946 * FACE_PIC_SIZE / 112), int(51.6963 * FACE_PIC_SIZE / 112)),
            (int(73.5318 * FACE_PIC_SIZE / 112), int(51.5014 * FACE_PIC_SIZE / 112)),
            (int(56.0252 * FACE_PIC_SIZE / 112), int(71.7366 * FACE_PIC_SIZE / 112)),
            (int(41.5493 * FACE_PIC_SIZE / 112), int(92.3655 * FACE_PIC_SIZE / 112)),
            (int(70.7299 * FACE_PIC_SIZE / 112), int(92.2041 * FACE_PIC_SIZE / 112)) ]

3、初始化KPU相关的参数,加载人脸检测模型以及使用yolo2网络算法,kpu需要加载kmodel文件

# 人脸检测锚点;锚点参数与模型参数一致,同一个模型这个参数是固定的(即训练模型时确定了)
anchor = (0.1075, 0.126875, 0.126875, 0.175, 0.1465625, 0.2246875, 0.1953125, 0.25375, 0.2440625, 0.351875, 0.341875, 0.4721875, 0.5078125, 0.6696875, 0.8984375, 1.099687, 2.129062, 2.425937)
kpu = KPU()# 创建一个kpu对象,用于人脸检测
kpu.load_kmodel("/sd/KPU/yolo_face_detect/face_detect_320x240.kmodel") # 导入人脸检测模型
# yolo2初始化
kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320 , net_h=240 ,layer_w=10 ,layer_h=8, threshold=0.7, nms_value=0.2, classes=1)

4、初始化KPU相关的参数,加载ld5模型,kpu需要加载kmodel文件

ld5_kpu = KPU()# 创建一个kpu对象,用于人脸5关键点检测
print("ready load model")
ld5_kpu.load_kmodel("/sd/KPU/face_recognization/ld5.kmodel") # 导入人脸5关键点检测模型

5、初始化KPU相关的参数,加载特征模型,kpu需要加载kmodel文件

fea_kpu = KPU()# 创建一个kpu对象,用于人脸属性检测
print("ready load model")
fea_kpu.load_kmodel("/sd/KPU/face_recognization/feature_extraction.kmodel") # 导入人脸属性检测模型

6、新建按键功能,上升沿触发按键中断,用来记录需要识别的人脸

start_processing = False 
BOUNCE_PROTECTION = 50 # 定义外部中断时间为50ms

fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 将IO16配置为GPIOHS0
key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 配置GPIOHS0为输入
def set_key_state(*_): # 配置外部中断及中断回调函数,配置GPIOHS0为输入
    global start_processing # 发送数据
    start_processing = True
    time.sleep_ms(BOUNCE_PROTECTION)
# 配置GPIOHS0上升沿触发中断
key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)

7、新建人脸识别变量。变量record_ftrs用于存储按键记录下的人脸特征的数组;THRESHOLD为人脸识别相似度分数阈值,超过此值则认为是识别到的人脸,需要精准识别可以上调;recog_flag表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase

record_ftrs = [] #空列表 用于存储按键记录下的人脸特征的数组
THRESHOLD = 80.5   # 人脸识别相似度分数阈值为80.5,需要精准识别可以上调
recog_flag = False  # 表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase

8、提取检测到的人脸的信息,目标检测中的边界框(x,y,w,h形式转换与绘制)

def extend_box(x, y, w, h, scale):  # 目标检测中的边界框(x,y,w,h形式转换与绘制)
    x1_t = x - scale*w
    x2_t = x + w + scale*w
    y1_t = y - scale*h
    y2_t = y + h + scale*h
    x1 = int(x1_t) if x1_t>1 else 1
    x2 = int(x2_t) if x2_t<320 else 319
    y1 = int(y1_t) if y1_t>1 else 1
    y2 = int(y2_t) if y2_t<240 else 239
    cut_img_w = x2-x1+1
    cut_img_h = y2-y1+1
    return x1, y1, cut_img_w, cut_img_h

9、使用while循环将图像传入KPU循环进行计算,使用yolo2神经网络算法解算

  • (1)将检测到的人脸用白色框标记出来,kpu运算并获取结果,根据结果算出人脸5关键点,由关键点算出变换矩阵,相似变换修正人脸图, 检测人脸属性获取结果。当识别到BOOT按键按下时,记录人脸信息,每次运算结束后删除对象,管理内存
  • (2)人脸识别时,将检测到的人脸与储存的特征进行比对,比对两个特征数据并给出相似度分数,取最大相似度分数,当检测到的人脸信息相似度分数大于识别阈值,表示人脸识别成功,显示出persion:ID+置信度;未识别到是保存过的人脸,显示出unregistered+置信度每次运算结束后删除对象,管理内存
  • (3)检测到按键BOOT按下,上升沿按键中断,记录下当前检测到人脸特征,打印人脸记录次数。判断是否为保存过的人脸,并且用方框的颜色区分。显示屏显示FPS、显示提示用户操作的字符串。最后将创建的kpu对象去初始化,释放模型内存。每次运算结束后删除对象,管理内存
while True:
    gc.collect() # 内存管理:手动启动垃圾收集
    print("mem free:",gc.mem_free()) #查看剩余内存信息(栈)
    print("heap free:",utils.heap_free())  #查看剩余内存信息(堆)
    clock.tick() # 更新计算帧率的clock
    img = sensor.snapshot() # 拍照,获取一张图像
    kpu.run_with_output(img) # 对输入图像进行kpu运算
    dect = kpu.regionlayer_yolo2() # yolo2后处理
    fps = clock.fps() # 获取帧率
    if len(dect) > 0:
        for l in dect :
            # 对检测到的人脸框裁剪
            x1, y1, cut_img_w, cut_img_h= extend_box(l[0], l[1], l[2], l[3], scale=0)
            face_cut = img.cut(x1, y1, cut_img_w, cut_img_h) # 从img中裁剪出人脸图
            face_cut_128 = face_cut.resize(128, 128) # 对人脸图调整大小到128*128
            face_cut_128.pix_to_ai() # 对rgb565格式的128人脸图生成ai运算需要的rgb888格式存储
            out = ld5_kpu.run_with_output(face_cut_128, getlist=True) # kpu运算并获取结果
            face_key_point = []
            for j in range(5): # 根据结果算出人脸5关键点,并在图中标出
                x = int(KPU.sigmoid(out[2 * j])*cut_img_w + x1)
                y = int(KPU.sigmoid(out[2 * j + 1])*cut_img_h + y1)
                face_key_point.append((x,y))
            T = image.get_affine_transform(face_key_point, dst_point) #由关键点算出变换矩阵
            image.warp_affine_ai(img, feature_img, T) # 相似变换修正人脸图
            feature = fea_kpu.run_with_output(feature_img, get_feature = True)  # 检测人脸属性获取结果
            del face_key_point # 删除对象

            scores = []
            for j in range(len(record_ftrs)):
                score = kpu.feature_compare(record_ftrs[j], feature) #  特征比对,比对两个特征数据并给出相似度分数
                scores.append(score) # 将新元素score附加到数组的末尾,使其增长
            if len(scores):
                max_score = max(scores) # 取最大相似度分数
                index = scores.index(max_score)
                if max_score > THRESHOLD:
                    # 检测到是保存过的人脸,显示出persion:ID 置信度
                    img.draw_string(0, 195, "persion:%d,score:%2.1f" %(index, max_score), color=(0, 255, 0), scale=2)
                    recog_flag = True
                else:
                    # 未识别到是保存过的人脸,显示出unregistered 置信度
                    img.draw_string(0, 195, "unregistered,score:%2.1f" %(max_score), color=(255, 0, 0), scale=2)
            del scores # 删除对象

            if start_processing: # 检测到按键BOOT按下
                record_ftrs.append(feature) # 记录特征
                print("record_ftrs:%d" % len(record_ftrs)) # 打印人脸记录次数
                start_processing = False # 按键BOOT关

            if recog_flag:
                # 识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 绿色框
                img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
                recog_flag = False
            else:
                # 未识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 白色框
                img.draw_rectangle(l[0],l[1],l[2],l[3], color=(255, 255, 255))
            del (face_cut_128) # 删除对象
            del (face_cut) # 删除对象

    img.draw_string(0, 0, "%2.1ffps" %(fps), color=(0, 60, 255), scale=2.0) # 显示屏显示FPS
    #显示字符串press boot key to regist face(0, 215)
    img.draw_string(0, 215, "press boot key to regist face", color=(255, 100, 0), scale=2.0)
    lcd.display(img)

# 创建的kpu对象去初始化,释放模型内存, 立即释放
kpu.deinit()
ld5_kpu.deinit()
fea_kpu.deinit()


实验分析:

BOOT保存人脸信息后进行人脸检测:可以看出绿色框标记出了人脸,屏幕显示出了人员标签号、相似度分数信息

在这里插入图片描述

在这里插入图片描述


为什么两个不同的人会识别为同一个人???: 为什么两个明显不同的人会识别成功为一个人呢?,我们对比结果分析得出结论,都显示为标签信息2,但是相似度分数却相差较大,前者相似度分数97.5,后者相似度分数82.4。所以设定合适的相似度分数阈值是非常重要的

在这里插入图片描述

在这里插入图片描述


未保存前与保存后识别对比: 未保存人脸信息时,我们识别人脸会检测到相似度的分数较低(69.5),低于设定的阈值(80.5),显示为白色图框,显示不能辨别字样;按键BOOT保存人脸信息后,我们识别人脸会检测到相似度的分数较高(99.5),低于设定的阈值(80.5),显示为绿色图框,显示识别到的标签信息;

在这里插入图片描述

在这里插入图片描述


可以同时检测多个人脸: 可以将各自的标签与相似度分数显示出来,由于未开发此功能,所以显示重叠,大家可以修改开发:若有多个标签,将显示的标签分别打印在不同的位置

在这里插入图片描述


文件夹文件:由于此实验需要BOOT按键来保存人脸信息,所以不能在Canmv IDE运行人脸识别代码,我们需要将写的Python文件重命名为main.py下载到我们的开发板中,参考上一节Canmv K210开发板文件下载——将程序烧录到Canmv Cam点击下载修改过的主程序main.py

在这里插入图片描述


串口打印数据: 分析串口打印的数据,我们未保存人脸信息之前,剩余内存信息(栈):665472、剩余内存信息(堆):430080;保存一张人脸信息之后:剩余内存信息(栈):657216、剩余内存信息(堆):430080;对比可以得到结论:我们按键BOOT保存的图片信息会保存在栈内存,一张人脸的数据用掉了堆内存8256字节
在这里插入图片描述


实验过程与总结:

实验过程:

  • 在Canmv IDE环境下实现人脸识别功能,首先需要导入必要的库,并初始化K210开发板上的摄像头和LCD显示屏。接着编写程序以启动摄像头显示图像,并利用人脸识别算法检测图像中的人脸。检测到人脸之后,若按键BOOT触发中断检测人脸特征信息,对人脸进行编号并存储其相关信息。

  • 在后续的识别过程中,系统会将当前检测到的人脸与已保存的人脸数据库进行比对。若检测的相似度大于设定的相似度分数阈值,即识别出之前编号的人脸,系统会用不同颜色的框标记该人脸,还会在框旁显示其序号和识别的置信度

  • 点击下载需要用到的模型

总结:

  • 在Canmv IDE环境下,通过导入关键库并初始化K210开发板的摄像头和LCD显示屏,实现了人脸识别系统。该系统能检测图像中的人脸,通过按键保存人脸数据并进行编号。再次识别时,系统将匹配并标记已知人脸,展示其序号和置信度,增强了人脸检测的交互性和个性化。此处检测人脸的阈值:THRESHOLD=80.5,如果需要识别人脸更加准确,可以适当调整阈值。

完整代码展示:

# Face_recog - By: Jack - 周三 4月 24 2024

import sensor, image, time, lcd
import gc                          # 内存管理模块
from maix import KPU               # 加速AI计算
from maix import GPIO, utils       # 使用Pin脚的GPIO功能、基础功能
from fpioa_manager import fm       # 管理内部功能和引脚映射关系的功能模块
from board import board_info       # 板子board_info类


lcd.init()  # 初始化LCD显示屏
sensor.reset()  # 复位并初始化摄像头
sensor.set_pixformat(sensor.RGB565)  # 设置摄像头输出格式为 RGB565
sensor.set_framesize(sensor.QVGA) # 设置摄像头输出大小为 QVGA (320x240)
sensor.skip_frames(time = 1000) # 等待摄像头稳定
clock = time.clock() # 创建一个clock对象,用来计算帧率

feature_img = image.Image(size=(64,64), copy_to_fb=False) # 人脸特性图像为64*64图输入,这里初始化一个image
feature_img.pix_to_ai()  # 对rgb565的image生成ai运算需要的r8g8b8格式存储

# 标准人脸关键点坐标
FACE_PIC_SIZE = 64
dst_point =[(int(38.2946 * FACE_PIC_SIZE / 112), int(51.6963 * FACE_PIC_SIZE / 112)),
            (int(73.5318 * FACE_PIC_SIZE / 112), int(51.5014 * FACE_PIC_SIZE / 112)),
            (int(56.0252 * FACE_PIC_SIZE / 112), int(71.7366 * FACE_PIC_SIZE / 112)),
            (int(41.5493 * FACE_PIC_SIZE / 112), int(92.3655 * FACE_PIC_SIZE / 112)),
            (int(70.7299 * FACE_PIC_SIZE / 112), int(92.2041 * FACE_PIC_SIZE / 112)) ]

# 人脸检测锚点;锚点参数与模型参数一致,同一个模型这个参数是固定的(即训练模型时确定了)
anchor = (0.1075, 0.126875, 0.126875, 0.175, 0.1465625, 0.2246875, 0.1953125, 0.25375, 0.2440625, 0.351875, 0.341875, 0.4721875, 0.5078125, 0.6696875, 0.8984375, 1.099687, 2.129062, 2.425937)
kpu = KPU()# 创建一个kpu对象,用于人脸检测
kpu.load_kmodel("/sd/KPU/yolo_face_detect/face_detect_320x240.kmodel") # 导入人脸检测模型
# yolo2初始化
kpu.init_yolo2(anchor, anchor_num=9, img_w=320, img_h=240, net_w=320 , net_h=240 ,layer_w=10 ,layer_h=8, threshold=0.7, nms_value=0.2, classes=1)


ld5_kpu = KPU()# 创建一个kpu对象,用于人脸5关键点检测
print("ready load model")
ld5_kpu.load_kmodel("/sd/KPU/face_recognization/ld5.kmodel") # 导入人脸5关键点检测模型

fea_kpu = KPU()# 创建一个kpu对象,用于人脸属性检测
print("ready load model")
fea_kpu.load_kmodel("/sd/KPU/face_recognization/feature_extraction.kmodel") # 导入人脸属性检测模型

start_processing = False
BOUNCE_PROTECTION = 50 # 定义外部中断时间为50ms

fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 将IO16配置为GPIOHS0
key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 配置GPIOHS0为输入
def set_key_state(*_): # 配置外部中断及中断回调函数,配置GPIOHS0为输入
    global start_processing # 发送数据
    start_processing = True
    time.sleep_ms(BOUNCE_PROTECTION)
# 配置GPIOHS0上升沿触发中断
key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)


record_ftrs = [] #空列表 用于存储按键记录下的人脸特征的数组
THRESHOLD = 80.5   # 人脸识别相似度分数阈值为80.5,需要精准识别可以上调
recog_flag = False  # 表示检测到的人脸是否已识别的状态 ,已识别为True,未识别为Flase

def extend_box(x, y, w, h, scale):  # 目标检测中的边界框(x,y,w,h形式转换与绘制)
    x1_t = x - scale*w
    x2_t = x + w + scale*w
    y1_t = y - scale*h
    y2_t = y + h + scale*h
    x1 = int(x1_t) if x1_t>1 else 1
    x2 = int(x2_t) if x2_t<320 else 319
    y1 = int(y1_t) if y1_t>1 else 1
    y2 = int(y2_t) if y2_t<240 else 239
    cut_img_w = x2-x1+1
    cut_img_h = y2-y1+1
    return x1, y1, cut_img_w, cut_img_h


while True:
    gc.collect() # 内存管理:手动启动垃圾收集
    print("mem free:",gc.mem_free()) #查看剩余内存信息(栈)
    print("heap free:",utils.heap_free())  #查看剩余内存信息(堆)
    clock.tick() # 更新计算帧率的clock
    img = sensor.snapshot() # 拍照,获取一张图像
    kpu.run_with_output(img) # 对输入图像进行kpu运算
    dect = kpu.regionlayer_yolo2() # yolo2后处理
    fps = clock.fps() # 获取帧率
    if len(dect) > 0:
        for l in dect :
            # 对检测到的人脸框裁剪
            x1, y1, cut_img_w, cut_img_h= extend_box(l[0], l[1], l[2], l[3], scale=0)
            face_cut = img.cut(x1, y1, cut_img_w, cut_img_h) # 从img中裁剪出人脸图
            face_cut_128 = face_cut.resize(128, 128) # 对人脸图调整大小到128*128
            face_cut_128.pix_to_ai() # 对rgb565格式的128人脸图生成ai运算需要的rgb888格式存储
            out = ld5_kpu.run_with_output(face_cut_128, getlist=True) # kpu运算并获取结果
            face_key_point = []
            for j in range(5): # 根据结果算出人脸5关键点,并在图中标出
                x = int(KPU.sigmoid(out[2 * j])*cut_img_w + x1)
                y = int(KPU.sigmoid(out[2 * j + 1])*cut_img_h + y1)
                face_key_point.append((x,y))
            T = image.get_affine_transform(face_key_point, dst_point) #由关键点算出变换矩阵
            image.warp_affine_ai(img, feature_img, T) # 相似变换修正人脸图
            feature = fea_kpu.run_with_output(feature_img, get_feature = True)  # 检测人脸属性获取结果
            del face_key_point # 删除对象

            scores = []
            for j in range(len(record_ftrs)):
                score = kpu.feature_compare(record_ftrs[j], feature) #  特征比对,比对两个特征数据并给出相似度分数
                scores.append(score) # 将新元素score附加到数组的末尾,使其增长
            if len(scores):
                max_score = max(scores) # 取最大相似度分数
                index = scores.index(max_score)
                if max_score > THRESHOLD:
                    # 检测到是保存过的人脸,显示出persion:ID 置信度
                    img.draw_string(0, 195, "persion:%d,score:%2.1f" %(index, max_score), color=(0, 255, 0), scale=2)
                    recog_flag = True
                else:
                    # 未识别到是保存过的人脸,显示出unregistered 置信度
                    img.draw_string(0, 195, "unregistered,score:%2.1f" %(max_score), color=(255, 0, 0), scale=2)
            del scores # 删除对象

            if start_processing: # 检测到按键BOOT按下
                record_ftrs.append(feature) # 记录特征
                print("record_ftrs:%d" % len(record_ftrs)) # 打印人脸记录次数
                start_processing = False # 按键BOOT关

            if recog_flag:
                # 识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 绿色框
                img.draw_rectangle(l[0],l[1],l[2],l[3], color=(0, 255, 0))
                recog_flag = False
            else:
                # 未识别到是保存过的人脸,在图像上绘制一个矩形。 传递l[0],l[1],l[2],l[3] 白色框
                img.draw_rectangle(l[0],l[1],l[2],l[3], color=(255, 255, 255))
            del (face_cut_128) # 删除对象
            del (face_cut) # 删除对象

    img.draw_string(0, 0, "%2.1ffps" %(fps), color=(0, 60, 255), scale=2.0) # 显示屏显示FPS
    #显示字符串press boot key to regist face(0, 215)
    img.draw_string(0, 215, "press boot key to regist face", color=(255, 100, 0), scale=2.0)
    lcd.display(img)

# 创建的kpu对象去初始化,释放模型内存, 立即释放
kpu.deinit()
ld5_kpu.deinit()
fea_kpu.deinit()

相关推荐

  1. 人脸识别开发项目汇总

    2024-04-25 06:28:06       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-25 06:28:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-25 06:28:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-25 06:28:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-25 06:28:06       18 阅读

热门阅读

  1. 【go从入门到精通】常用标准库的用法

    2024-04-25 06:28:06       11 阅读
  2. stable diffusion QA

    2024-04-25 06:28:06       16 阅读
  3. 编写一款2D CAD/CAM软件(十七)绘制选择框

    2024-04-25 06:28:06       10 阅读
  4. jQuery 动画小练习

    2024-04-25 06:28:06       15 阅读
  5. Harmony专栏 TypeScript教程

    2024-04-25 06:28:06       16 阅读
  6. vue项目中定位组件来源的查找思路

    2024-04-25 06:28:06       14 阅读
  7. 1883. 准时抵达会议现场的最小跳过休息次数

    2024-04-25 06:28:06       15 阅读
  8. MAC 安装miniconda

    2024-04-25 06:28:06       11 阅读
  9. Axios

    2024-04-25 06:28:06       10 阅读
  10. 【OceanBase系列】—— 常用 SQL

    2024-04-25 06:28:06       13 阅读
  11. FPGA中乘除法运算实现途径

    2024-04-25 06:28:06       12 阅读