第三十九章 YOLO2人脸检测实验

1)实验平台:正点原子DNK210开发板

2)章节摘自【正点原子】DNK210使用指南 - CanMV版 V1.0

3)购买链接:https://detail.tmall.com/item.htm?&id=782801398750

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/k210/ATK-DNK210.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子K210技术交流企鹅群:605557868

从本章开始,将通过几个实例介绍Kendryte K210上的KPU,以及CanMV下KPU的使用方法,本章将先介绍YOLO2网络的人脸检测应用在CanMV上的实现。通过本章的学习,读者将学习到YOLO2网络的人脸检测应用在CanMV上的实现。
本章分为如下几个小节:
39.1 maix.KPU模块介绍
39.2 硬件设计
39.3 程序设计
39.4 运行验证

39.1 maix.KPU模块介绍

Kendryte K210片上拥有一个KPU,KPU是通用的神经网络处理器,它可以在低功耗的情况下实现卷据神经网络的计算,实时获取被检测目标的大小、坐标和种类,对人脸或者物体进行检测和分类。
Kendryte K210片上KPU的主要特点如下所示:

  1. 支持计算多层卷积神经网络,每层卷积神经网络的控制参数可单独配置
  2. 支持中断模式,可配置加速器在每层卷积结束后是否产生中断信号
  3. 支持输入图像片上存储,存储容量大小为2M字节,卷积结果可由DMA读出
  4. 支持输入输出通道数目、输入输出图像行高列宽可配置,其中通道数目范围在1~1024之间,输出行高列宽与输入相同,或者是输入处于2或者4且向下取整
  5. 支持两种卷积内核,分别为1×1和3×3,卷积步长为1,十种池化方式,包括bypass、步长为1且大小为22的均值、步长为1且大小为2×2的最大值、步长为2且大小为2×2的均值、步长为2且大小为2×2的最大值、步长为2且大小为2×2的左上值、步长为2且大小为2×2的右上值、步长为4且大小为4×4的均值、步长为4且大小为4×4的最大值、步长为4且大小为4×4的左上值
  6. 支持两种padding方式,分别为任意填充和取最近值
  7. 支持在输入图像行高超过256时,自动对卷积结果进行抽样,仅保留奇数行奇数列结果
  8. 支持卷积参数、批归一化参数、激活参数配置,AI加速器主动读取,读取地址可配置
  9. 支持卷据参数片上存储,存储容量为72K字节,可以边卷积边读取卷据参数,每层网络最多可以读取64次
  10. 支持mobilenet-V1
  11. 实时工作时最大支持神经网络参数大小为5.5MiB到5.9MiB
  12. 非实时工作最大支持网络参数大小为Flash大小扣除软件占用大小

在CanMV中可以使用CanMV提供的maix.KPU模块操作Kendryte K210上的KPU。maix.KPU模块对图像进行卷积运算,并提供了一些网络的支持,使得开发者能够很方便地在Kendryte K210上使用CanMV实现一些需要卷积神经网络计算的应用。
maix.KPU模块提供了KPU构造函数,用于创建一个KPU对象,KPU构造函数如下所示:

class KPU()

通过KPU构造函数可以创建并初始化一个KPU对象。
KPU构造函数的使用示例如下所示:

from maix import KPU
kpu = KPU()

maix.KPU为KPU对象提供了load_kmodel()方法,用于从文件系统或Flash中加载网络模型,load_kmodel()方法如下所示:

load_kmodel(path, size)

load_kmodel()方法用于为KPU对象从文件系统或Flash中加载网路模型,网络模型加载后会被加载到内存中等待使用。
path指的是网络模型在文件系统中的路径或网络模型在Flash中的起始地址。
size指的是网络模型在Flash中的大小,当使用文件系统的方式加载网络模型时,该参数无效。
load_kmodel()方法的使用示例如下所示:

from maix import KPU
kpu = KPU()
kpu.load_kmode("/sd/KPU/face_detect_320x240.kmodel")

maix.KPU模块为KPU对象提供了init_yolo2()方法,用于初始化yolo2网络模型,init_yolo2()方法如下所示:

KPU.init_yolo2(anchor=None, anchor_num=5, img_w=320, img_h=240, net_w=320, net_h=240, layer_w=10,
 layer_h=8, threshold=0.7, nms_value=0.4, classes=1)

init_yolo2()方法用于初始化yolo2网络模型,同时为yolo2网络传入一些必要的参数,只有在使用YOLO2网络模型时,才需要用到该方法。
anchor指的是锚点参数,锚点参数是网络在训练前就确定下来的一组数据,同一个网络模型的锚点参数是固定的,锚点参数与网络模型绑定。
anchor_num指的是锚点的数量,固定为len(anchor)//2,即锚点参数数据大小的一半。
img_w和img_h指的是输入图像的宽度和高度,这决定了画框的边界,如果输入图像是由一张小尺寸的图像扩充来的,可以将这两个参数设置为原图的宽度和高度。
net_w和net_h指的是模型需要的图像的宽度和高度,这是在网络模型训练前就确定好的,与网络模型绑定。
layer_w和layer_h指的是网络模型的层宽和层高,这是在网络模型训练前就确定号的,与网路模型绑定。
threshold指的是概率阈值,在网络识别到物体后,之后当被识别物体的可信度大于概率阈值,才会输出该结果,取值范围为[0, 1],默认为0.7。
nums_value指的是box_iou门限,为了防止同一个物体被框出多个框,当在同一个物体上框出两个框时,这两个框的交叉区域占两个框总占用面积的比例如果小于这个参数值,就取其中概率最大的一个框,默认为0.4。
classes指的是网络要分辨目标的类数,这是在网络模型训练前就确定好的,与网络模型绑定,默认为1。
init_yolo2()方法的使用示例如下所示:

from maix import KPU
import image
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)
names = ['face']
kpu = KPU()
kpu.load_kmode("/sd/KPU/face_detect_320x240.kmodel")
kpu.init_yolo2(anchor, anchor_num=len(anchor) // 2, img_w=320, img_h=240, net_w=320, net_h=240, 
layer_w=10, layer_h=8, threshold=0.5, nms_value=0.2, classes=len(names))

maix.KPU模块为KPU对象提供了run_with_output()方法,用于将图像送入KPU进行运算,并获取运算结果,run_with_output()方法如下所示:

KPU.run_with_output(input, getlist, get_feature)

run_with_output()方法用于将图像送入KPU进行运算,并获取运算结果,且能够指定返回结果的数据类型。
input指的是输入的图像,需要是Image对象,因此可以是摄像头的输出图像,或是文件系统中的图像文件。
getlist指的是是否返回浮点数列表,当为True时,返回浮点数列表,默认为False。
get_feature指的是是否返回L2归一化后的浮点特征值(最大允许256),当为True时,返回L2归一化后的浮点特征值,默认为False。
run_with_output()方法的使用示例如下所示:

from maix import KPU
import image
img = image.Image(size=(320, 240))
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)
names = ['face']
kpu = KPU()
kpu.load_kmode("/sd/KPU/face_detect_320x240.kmodel")
kpu.init_yolo2(anchor, anchor_num=len(anchor) // 2, img_w=320, img_h=240, net_w=320, net_h=240, 
layer_w=10, layer_h=8, threshold=0.5, nms_value=0.2, classes=len(names))
kpu.run_with_output(input=img, getlist=False, get_feature=False)

maix.KPU模块为KPU对象提供了regionlayer_yolo2()方法,用于进行YOLO2运算,并获取其运算结果,regionlayer_yolo2()方法如下所示:

KPU.regionlayer_yolo2()

regionlayer_yolo2()方法用于进行YOLO2运算,并返回一个二维列表,每个子列表表示识别到的一个物体的信息,包含了物体在输入图像上左上角的X坐标、Y坐标、宽度、高度,以及class的类别序号,以及置信度。
regionlayer_yolo2()方法的使用示例如下所示:

from maix import KPU
import image
img = image.Image(size=(320, 240))
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)
names = ['face']
kpu = KPU()
kpu.load_kmode("/sd/KPU/face_detect_320x240.kmodel")
kpu.init_yolo2(anchor, anchor_num=len(anchor) // 2, img_w=320, img_h=240, net_w=320, net_h=240, 
layer_w=10, layer_h=8, threshold=0.5, nms_value=0.2, classes=len(names))
kpu.run_with_output(input=img, getlist=False, get_feature=False)
objs = kpu.regionlayer_yolo2()
for obj in objs:
    print("Rect: %d, %d, %d, %d" % (obj[0], obj[1], obj[2], obj[3]))
    print("Class number: %d" % (obj[4]))
print("Prob: %.2f" % (obj[5]))

maix.KPU模块为KPU对象提供了feature_compare()方法,用于进行特征比对,feature_compare()方法如下所示:

KPU.feature_compare(feature_0, feature_1)

feature_compare()方法用于对两组特征进行特征比对,比对两组特征数据并给出相似度得分,得分越高表示两组特征的相似度也就越高。
feature_0和feature_1指的是两组特征数据,浮点列表,最大值为256。
feature_compare()方法会返回两组比较特征的相似度得分值。
feature_compare()方法的使用示例如下所示:

feature = kpu.run_with_output(img, get_feature = True)
score = kpu.feature_compare(record_feature, feature)
print(score)

39.2 硬件设计

39.2.1 例程功能

  1. 获取摄像头输出的图像,并送入KPU进行YOLO2的人脸检测模型运算,后将运算结果和摄像头输出的图像一起显示在LCD上。

39.2.2 硬件资源
本章实验内容,主要讲解maix.KPU模块的使用,无需关注硬件资源。
39.2.3 原理图
本章实验内容,主要讲解maix.KPU模块的使用,无需关注原理图。

39.3 程序设计

39.3.1 maix.KPU模块介绍
有关maix.KPU模块的介绍,请见第39.1小节《maix.KPU模块介绍》。
39.3.2 程序流程图

图39.3.2.1 YOLO2人脸检测实验流程图

39.3.3 main.py代码
main.py中的脚本代码如下所示:

import lcd
import sensor
import gc
from maix import KPU
lcd.init()
sensor.reset()
sensor.set_framesize(sensor.QVGA)
sensor.set_pixformat(sensor.RGB565)
sensor.set_hmirror(False)
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)
names = ['face']
# 构造KPU对象
face_detecter = KPU()
# 加载模型文件
face_detecter.load_kmodel("/sd/KPU/face_detect_320x240.kmodel")
# 初始化YOLO2网络
face_detecter.init_yolo2(anchor, anchor_num=len(anchor) // 2, img_w=320, img_h=240, net_w=320, 
net_h=240, layer_w=10, layer_h=8, threshold=0.5, nms_value=0.2, classes=len(names))
while True:
    img= sensor.snapshot()
    # 进行KPU运算
   face_detecter.run_with_output(input=img, getlist=False, get_feature=False)
    # 进行YOLO2运算
   faces = face_detecter.regionlayer_yolo2()
    for face in faces:
       img.draw_rectangle(face[0], face[1], face[2], face[3], color=(0, 255, 0))
       img.draw_string(face[0] + 2, face[1] + 2, "%.2f" % (face[5]), color=(0, 255, 0))
       img.draw_string(face[0] + 2, face[1] + 10, names[face[4]], color=(0, 255, 0))
    lcd.display(img)
    gc.collect()

可以看到一开始是先初始化了LCD和摄像头。
接着是构造一个KPU对象,并从文件系统中加载YOLO2人脸检测网络需要用到的网络模型,并初始化YOLO2网络。
然后便是在一个循环中不断地获取摄像头输出的图像,并将其送入KPU中进行运算,然后再进行YOLO2网络运算,最后便得到网络识别出人脸在输入图像上的一些信息,将这些信息绘制到图像上后,在LCD上显示图像。

39.4 运行验证

将DNK210开发板连接CanMV IDE,点击CanMV IDE上的“开始(运行脚本)”按钮后,将摄像头对准人脸,让其采集到人脸图像,随后便能在LCD上看到摄像头输出的图像,同时图像中的人脸均被绿色的矩形框框出,并在矩形框内的左上角标出了人脸的置信度,如下图所示:

图39.4.1 LCD显示YOLO2人脸检测结果


正点原子
1 声望1 粉丝