K230 CanMV AI Demo示例说明#
版权所有©2023北京嘉楠捷思信息技术有限公司
免责声明#
您购买的产品、服务或特性等应受北京嘉楠捷思信息技术有限公司(“本公司”,下同)及其关联公司的商业合同和条款的约束,本文档中描述的全部或部分产品、服务或特性可能不在您的购买或使用范围之内。除非合同另有约定,本公司不对本文档的任何陈述、信息、内容的正确性、可靠性、完整性、适销性、符合特定目的和不侵权提供任何明示或默示的声明或保证。除非另有约定,本文档仅作为使用指导参考。
由于产品版本升级或其他原因,本文档内容将可能在未经任何通知的情况下,不定期进行更新或修改。
商标声明#
、“嘉楠”和其他嘉楠商标均为北京嘉楠捷思信息技术有限公司及其关联公司的商标。本文档可能提及的其他所有商标或注册商标,由各自的所有人拥有。
版权所有 © 2023北京嘉楠捷思信息技术有限公司。保留一切权利。 非经本公司书面许可,任何单位和个人不得擅自摘抄、复制本文档内容的部分或全部,并不得以任何形式传播。
目录#
[TOC]
前言#
概述#
本文档主要介绍如何写AI Demo。
读者对象#
本文档(本指南)主要适用于以下人员:
技术支持工程师
软件开发工程师
一、概述#
本文档包括19个AI Demo,这些示例程序都实现从摄像头采集数据、kpu推理到显示器展示的流程,应用到了K230 CanMV 平台的多个硬件模块:AI2D,KPU,Camera,Display等。
这些AI Demo分为两种类型:单模型、多模型,涵盖物体、人脸、人手、人体、车牌、OCR等方向;参考该文档,k230用户可以更快上手K230 AI应用的开发,实现预期效果。
更多AI Demo后续即将解锁。
单模型示例 |
多模型示例 |
---|---|
人脸检测 |
人脸关键点检测 |
COCO目标检测 |
人脸识别 |
yolov8-seg |
人脸姿态角 |
车牌检测 |
人脸解析 |
OCR检测 |
车牌识别 |
手掌检测 |
石头剪刀布 |
人体检测 |
OCR识别 |
人体姿态估计 |
手掌关键点检测 |
KWS |
静态手势识别 |
人脸mesh |
二、AI Demo单模型示例解析#
1.分模块解析#
1.1 模块引入#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行时模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
模块 |
说明 |
---|---|
image(必选) |
图像模块,主要用于读取、图像绘制元素(框、点等)等操作 |
media.camera(必选) |
|
media.display (必选) |
|
media.media(必选) |
|
nncase_runtime(必选) |
nncase运行时模块, 封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作 |
aidemo(可选) |
封装部分ai demo相关后处理、复杂画图操作 |
aicube(可选) |
封装基于ai cube训练的检测分割等任务的后处理 |
ulab.numpy (可选) |
|
time(可选) |
时间统计 |
gc(可选) |
垃圾回收模块,自动回收 |
1.2 参数配置#
不同模型(kmodel)根据自己需要设置参数信息,包括显示、AI原图、kmodel参数、文件配置、调试模式等。
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1024, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 576
# kmodel相关参数设置
# kmodel输入shape,NCHW,RGB
kmodel_input_shape = (1,3,320,320)
# ai原图padding
rgb_mean = [104,117,123]
# 其它kmodel参数
......
# 文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel' # kmodel路径
anchors_path = root_dir + 'utils/prior_data_320.bin' # kmodel anchor
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
1.3 时间统计工具#
ScopedTiming 类是一个用来测量代码块执行时间的上下文管理器。上下文管理器通过定义包含 __enter__
和 __exit__
方法的类来创建。当在 with 语句中使用该类的实例时,__enter__
在进入 with 块时被调用,__exit__
在离开时被调用。
#********************for scoped_timing.py********************
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
使用示例:
import ulab.numpy as np
debug_mode = 1
a = np.ones(10**6, dtype=np.float)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
a = a * 1.000001 + 0.000001
1.4 nncase使用:ai2d#
ai2d主要是用于对输入原图预处理进行硬件加速,然后把预处理结果喂给kmodel。
(1)ai2d基础用法示例#
import nncase_runtime as nn
# 注:此示例仅为基础用法,其它demo可根据实际情况调整
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def ai2d_demo(rgb888p_img):
# 初始化AI2D模块
# rgb888p_img为Image对象
with ScopedTiming("ai2d_init",debug_mode > 0):
# (1)创建ai2d实例
ai2d = nn.ai2d()
# (2)设置ai2d参数
# 设置ai2d输入、输出格式和数据类型
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
# 设置padding参数
ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
# 设置resize参数
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# (3)根据ai2d参数构建ai2d_builder
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], kmodel_input_shape)
# (4)创建ai2d输出,用于保存ai2d输出结果
global ai2d_output_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
# (5)创建ai2d输入对象,并将对象从numpy转换为tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (6)根据输入,ai2d参数,运行得到ai2d输出,将结果保存到ai2d_output_tensor中
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
# dump ai2d结果查看,查看结果是否正确(此时保存格式为nn.ai2d_format.NCHW_FMT,NCHW、RGB格式,需要转换为图片格式后再查看)
#ai2d_out_data = fp_ai2d_output_tensor.to_numpy() #
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
# (7)删除 ai2d、ai2d_input_tensor、ai2d_output_tensor、ai2d_builder 变量,释放对它所引用对象的内存引用
del ai2d
del ai2d_input_tensor
del ai2d_output_tensor
del ai2d_builder
(2)ai2d示例用法一:ai2d参数固定#
ai2d参数固定:针对视频流的不同帧,ai2d参数固定使用示例
eg:人脸检测模型,对于模型输入,需要将原图进行预处理(padding、resize)之后,然后再喂给kmodel;若是原图从sensor中取出,分辨率固定,则padding的上下左右位置是固定的,此时可以使用以下模板对原图进行预处理
import nncase_runtime as nn
#ai2d:ai2d实例
#ai2d_input_tensor:ai2d输入
#ai2d_output_tensor:ai2d输出
#ai2d_builder:根据ai2d参数,构建的ai2d_builder对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
.....
return [0, 0, 0, 0, top, bottom, left, right]
def ai2d_init():
# 初始化AI2D模块
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
# (1)创建ai2d实例
ai2d = nn.ai2d()
# (2)设置ai2d参数
# 设置ai2d输入、输出格式和数据类型
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
# 设置padding参数
ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
# 设置resize参数
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# (3)创建ai2d输出,用于保存ai2d输出结果
global ai2d_output_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
# (4)根据ai2d参数构建ai2d_builder,因为ai2d的参数不变,因此只需创建一次ai2d_builder
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], kmodel_input_shape)
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_output_tensor
# (1)创建ai2d输入对象,并将对象从numpy转换为tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)根据输入,ai2d参数,运行得到ai2d输出,将结果保存到ai2d_output_tensor中
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
# 删除 ai2d_input_tensor 变量,释放对它所引用对象的内存引用
del ai2d_input_tensor
使用示例:
ai2d_init() # ai2d初始化
while True:
rgb888p_img = camera_read(CAM_DEV_ID_0) # 从sensor拿到一帧图像
ai2d_run(rgb888p_img) # 对sensor原图像预处理
ai2d_release() # 释放ai2d_input_tensor,因为每帧原图不同,ai2d_input_tensor指向的对象都会改变,所以每次都需释放内存
......
global ai2d,ai2d_output_tensor,ai2d_builder #只需释放一次
del ai2d # 释放ai2d,因为ai2d指向的对象是固定的
del ai2d_output_tensor # 释放ai2d_output_tensor,因为ai2d_output_tensor指向的对象是固定的
del ai2d_builder # 释放ai2d_builder,因为ai2d的参数未变,ai2d_builder指向的对象是固定的
(3)ai2d示例用法二:ai2d参数不固定#
ai2d参数不固定:针对视频流的不同帧,ai2d参数实时变化
eg:人脸关键点检测模型,对于模型输入,需要将原图进行预处理(affine)之后,然后再喂给kmodel;即使原图从sensor中取出,分辨率固定,但是affine的参数是实时改变的,此时可以使用以下模板对原图进行预处理。
import nncase_runtime as nn
# fld_ai2d:人脸关键点ai2d实例
# fld_ai2d_input_tensor:人脸关键点ai2d输入
# fld_ai2d_output_tensor:人脸关键点ai2d输出
# fld_ai2d_builder:根据人脸关键点ai2d参数,构建的人脸关键点ai2d_builder对象
global fld_ai2d,fld_ai2d_input_tensor,fld_ai2d_output_tensor,fld_ai2d_builder
# affine参数
global matrix_dst
def get_affine_matrix(bbox):
# 根据人脸检测框获取仿射矩阵,用于将边界框映射到模型输入空间
with ScopedTiming("get_affine_matrix", debug_mode > 1):
......
return matrix_dst
def fld_ai2d_init():
with ScopedTiming("fld_ai2d_init",debug_mode > 0):
#for face landmark
global fld_ai2d
# (1)创建人脸关键点ai2d对象
fld_ai2d = nn.ai2d()
global fld_ai2d_output_tensor
# (2)创建人脸关键点ai2d_output_tensor对象
data = np.ones(fld_kmodel_input_shape, dtype=np.uint8)
fld_ai2d_output_tensor = nn.from_numpy(data)
def fld_ai2d_run(rgb888p_img,det):
# 人脸关键点ai2d运行,rgb888p_img是Image对象,det为人脸检测框
with ScopedTiming("fld_ai2d_run",debug_mode > 0):
global fld_ai2d,fld_ai2d_input_tensor,fld_ai2d_output_tensor
# (1)创建ai2d_input_tensor
# Image对象转换为numpy对象
ai2d_input = rgb888p_img.to_numpy_ref()
# 将numpy对象转换为ai2d_tensor
fld_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)设置ai2d参数
# 设置ai2d输入、输出格式、数据类型
fld_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global matrix_dst
# 根据检测框获取affine参数
matrix_dst = get_affine_matrix(det)
affine_matrix = [matrix_dst[0][0],matrix_dst[0][1],matrix_dst[0][2],
matrix_dst[1][0],matrix_dst[1][1],matrix_dst[1][2]]
# 设置affine参数
fld_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,affine_matrix)
global fld_ai2d_builder
# (3)根据新的ai2d affine参数,创建新的ai2d_builder对象
fld_ai2d_builder = fld_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fld_kmodel_input_shape)
# (4)ai2d_builder运行,将结果保存到fld_ai2d_output_tensor
fld_ai2d_builder.run(fld_ai2d_input_tensor, fld_ai2d_output_tensor)
def fld_ai2d_release():
with ScopedTiming("fld_ai2d_release",debug_mode > 0):
global fld_ai2d_input_tensor,fld_ai2d_builder
del fld_ai2d_input_tensor #删除fld_ai2d_input_tensor变量,释放对它所引用对象的内存引用
del fld_ai2d_builder #删除fld_ai2d_builder变量,释放对它所引用对象的内存引用
使用示例:
# ************main************
fld_ai2d_init() # ai2d初始化
while True:
rgb888p_img = camera_read(CAM_DEV_ID_0) # 从sensor拿到一帧图像
fld_ai2d_run(rgb888p_img,det) # 根据det,对sensor原图预处理
fld_ai2d_release() # 释放ai2d_input_tensor、ai2d_builder,因为每帧原图不同,ai2d_input_tensor指向的对象都会改变,所以每次都需释放内存;因为ai2d的参数实时改变,ai2d_builder对象需要每次创建,也需要每次都释放内存
......
global fld_ai2d,fld_ai2d_output_tensor #只需释放一次
del fld_ai2d # 释放fld_ai2d,因为ai2d指向的对象是固定的
del fld_ai2d_output_tensor # 释放fld_ai2d_output_tensor,因为fld_ai2d_output_tensor指向的对象是固定的
1.5 nncase使用:kpu#
(1)kpu基础用法#
import nncase_runtime as nn
# (1)初始化kpu对象
kpu_obj = nn.kpu()
# (2)加载kmodel
kpu_obj.load_kmodel(kmodel_file)
# (3)设置kpu输入
kmodel_input_shape = (1,3,320,320)
data = np.ones(kmodel_input_shape, dtype=np.uint8)
kmodel_input_tensor = nn.from_numpy(data)
kpu_obj.set_input_tensor(0, kmodel_input_tensor)
# (4)kpu运行
kpu_obj.run()
# (5)获取kpu模型输出
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data #tensor对象用完之后释放内存
results.append(result)
#(6)kpu后处理
......
# (7)释放kpu对象
del kpu_obj
(2)kpu示例用法:kpu配合ai2d使用#
import nncase_runtime as nn
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder #ai2d相关对象
global current_kmodel_obj #当前kpu对象
def kpu_init(kmodel_file):
# 初始化kpu对象,并加载kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
# (1)初始化kpu对象
kpu_obj = nn.kpu()
# (2)加载kmodel
kpu_obj.load_kmodel(kmodel_file)
# (3)ai2d初始化,模型输入需要预处理的情况下,kpu需要配合ai2d使用
ai2d_init()
return kpu_obj
def kpu_pre_process(rgb888p_img):
# kpu预处理,rgb888p_img是Image对象,原图
# (1)ai2d运行,对原图进行预处理
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor
# (2)将ai2d输出tensor设置为kpu模型输入
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor)
def kpu_get_output():
# 获取kpu输出
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取kpu输出,将输出转换为numpy格式,以便进行后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data #tensor对象用完之后释放内存
results.append(result)
return results
def kpu_run(kpu_obj,rgb888p_img):
# kpu推理运行
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)kpu预处理
kpu_pre_process(rgb888p_img)
with ScopedTiming("kpu_run",debug_mode > 0):
# (2)kpu运行
kpu_obj.run()
# (3)ai2d释放
ai2d_release()
# (4)获取模型输出
results = kpu_get_output()
# (5)kpu后处理,获取检测结果
with ScopedTiming("kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,kmodel_input_shape[2],prior_data,[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0]
# kpu释放
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_output_tensor
del kpu_obj #删除kpu_obj变量,释放对它所引用对象的内存引用
del ai2d #删除ai2d变量,释放对它所引用对象的内存引用
del ai2d_output_tensor #删除ai2d_output_tensor变量,释放对它所引用对象的内存引用
使用示例:
kmodel_file = "/sdcard/app/kmodel/face_detection_320.kmodel"
fd_kmodel = kpu_init(kmodel_file)
while True:
rgb888p_img = camera_read(CAM_DEV_ID_0) # 从sensor拿到一帧图像
dets = kpu_run(fd_kmodel,rgb888p_img) # kmodel推理
......
......
kpu_deinit(fd_kmodel) # 释放kmodel
1.6 媒体使用#
1.6.1 camera#
from media.camera import *
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
OUT_RGB888P_WIDTH = ALIGN_UP(1024, 16)
OUT_RGB888P_HEIGH = 576
def camera_init(dev_id):
# 根据设备id初始化camera
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# 设置camera的两路输出,一路输出用于显示,一路输出用于ai
# (1)设置显示输出
# 设置指定设备id的chn0的输出宽高
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
# 设置指定设备id的chn0的输出格式为yuv420sp
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# (2)设置AI输出
# 设置指定设备id的chn2的输出宽高
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
# 设置指定设备id的chn2的输出格式为rgb88planar
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# 启动sensor
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取指定设备chn2的一帧图像,即获取一帧AI原图
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放指定设备chn2一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 释放sensor
camera.stop_stream(dev_id)
使用示例:
from media.camera import *
# 初始化camera 0
camera_init(CAM_DEV_ID_0)
......(camera_start还要配合其它操作,稍后在media模块介绍)
# 启动sensor
camera_start(CAM_DEV_ID_0)
time.sleep(5)
rgb888p_img = None
while True:
# 读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
if rgb888p_img == -1:
# 若是未成功获取图像
print("face_detect_test, capture_image failed")
# 释放当前图像
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
.......
# 释放当前图像
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
1.6.2 display#
from media.display import *
# draw_img:用于画图
# osd_img:用于显示
global draw_img,osd_img
#for display
def display_init():
# 使用hdmi用于显示
display.init(LT9611_1920X1080_30FPS)
# 设置显示宽高、格式等
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放与显示相关的资源
display.deinit()
def display_draw(dets):
# 将检测框画到显示上
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
# 清空draw_img
draw_img.clear()
# 画检测框
for det in dets:
x, y, w, h = map(lambda x: int(round(x, 0)), det[:4])
x = x * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
y = y * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
w = w * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = h * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
# 先将框画到draw_img,argb
draw_img.draw_rectangle(x,y, w, h, color=(255, 255, 0, 255))
# 将draw_img拷贝到osd_img
draw_img.copy_to(osd_img)
# 将osd_img显示到hdmi上
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# 清空draw_img
draw_img.clear()
# 将draw_img拷贝到osd_img
draw_img.copy_to(osd_img)
# 将透明图显示到hdmi上
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
使用示例:
from media.display import *
# 显示初始化
display_init()
while True:
......
dets = kpu_run(kpu_face_detect,rgb888p_img)
# 将检测框画到显示屏幕
display_draw(dets)
......
# 显示资源释放
display_deinit()
1.6.3 media#
from media.media import *
global buffer,media_source,media_sink #for media
def media_init():
# (1)配置媒体缓冲区
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
# (2)创建由参数media_source和media_sink指定的媒体链路,链路创建成功后,数据流会自动从media_source流入media_sink,无需用户干预
global media_source, media_sink
# 创建指向指定sensor id,chn0的source
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
# 创建指向指定显示的sink
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
# 创建子sensor的chn0的输出,到指定显示的链路
media.create_link(media_source, media_sink)
# (3)初始化K230 CanMV平台媒体缓冲区
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
# (4)构建用于画图的对象
# 使用media模块构建osd_image内存,用于显示
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 创建用于画框、画点的Image
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 用于画框、画点结果,防止画的过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# 释放media资源
global buffer,media_source, media_sink
# (1)释放buffer
media.release_buffer(buffer)
# (2)销毁已经创建的媒体链路
media.destroy_link(media_source, media_sink)
# (3)去初始化K230 CanMV平台媒体缓冲区
ret = media.buffer_deinit()
return ret
使用示例:
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
try:
ret = media_init() #媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
if ret:
print("face_detect_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0) # 启动camera
time.sleep(5) # sleep 5s保证camera启动完全,可以拿到正确的图像
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
if rgb888p_img == -1:
# 若读取失败,则释放当前帧
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
# (2)kpu推理,获取推理结果
dets = kpu_run(kpu_face_detect,rgb888p_img)
# (3)将推理结果画到原图
display_draw(dets)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
# 注:无论程序是否正常停止,确保释放以下资源。保证下次的正确运行
if rgb888p_img is not None:
#先release掉申请的内存,再stop camera
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 释放camera资源
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
kpu_deinit(kpu_face_detect)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_detect_test, buffer_deinit failed")
return ret
2.人脸检测#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel参数设置
# kmodel输入shape
kmodel_input_shape = (1,3,320,320)
# ai原图padding
rgb_mean = [104,117,123]
# kmodel其它参数设置
confidence_threshold = 0.5
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
# 文件配置
# kmodel文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# anchor文件配置
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
# 当前kmodel
global current_kmodel_obj
# ai2d: ai2d实例
# ai2d_input_tensor: ai2d输入
# ai2d_output_tensor:ai2d输出
# ai2d_builder: 根据ai2d参数,构建的ai2d_builder对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder #for ai2d
print('anchors_path:',anchors_path)
# 读取anchor文件,为后处理做准备
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("ai2d_init",debug_mode > 0):
# (1)创建ai2d对象
global ai2d
ai2d = nn.ai2d()
# (2)设置ai2d参数
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# (3)创建ai2d_output_tensor,用于保存ai2d输出
global ai2d_output_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
# (4)ai2d_builder,根据ai2d参数、输入输出大小创建ai2d_builder对象
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], kmodel_input_shape)
def ai2d_run(rgb888p_img):
# 对原图rgb888p_img进行预处理
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_output_tensor
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行ai2d_builder,将结果保存到ai2d_output_tensor中
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
def ai2d_release():
# 释放ai2d_input_tensor
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
def kpu_init(kmodel_file):
# 初始化kpu对象,并加载kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
# 初始化kpu对象
kpu_obj = nn.kpu()
# 加载kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化ai2d
ai2d_init()
return kpu_obj
def kpu_pre_process(rgb888p_img):
# 使用ai2d对原图进行预处理(padding,resize)
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor
# 将ai2d输出设置为kpu输入
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor)
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def kpu_run(kpu_obj,rgb888p_img):
# kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2)kpu推理
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放ai2d资源
ai2d_release()
# (4)获取kpu输出
results = kpu_get_output()
# (5)kpu结果后处理
with ScopedTiming("kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,kmodel_input_shape[2],prior_data,[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0]
def kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_output_tensor
del kpu_obj #删除kpu_obj变量,释放对它所引用对象的内存引用
del ai2d #删除ai2d变量,释放对它所引用对象的内存引用
del ai2d_output_tensor #删除ai2d_output_tensor变量,释放对它所引用对象的内存引用
#********************for media_utils.py********************
global draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# for display,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.2
def display_init():
# hdmi显示初始化
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets):
# hdmi画检测框
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
for det in dets:
x, y, w, h = map(lambda x: int(round(x, 0)), det[:4])
x = x * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
y = y * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
w = w * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = h * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
draw_img.draw_rectangle(x,y, w, h, color=(255, 255, 0, 255), thickness = 2)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_detect_inference():
print("face_detect_test start")
# kpu初始化
kpu_face_detect = kpu_init(kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取检测结果
dets = kpu_run(kpu_face_detect,rgb888p_img)
# (3.2)将结果画到显示器
display_draw(dets)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
kpu_deinit(kpu_face_detect)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_detect_test, buffer_deinit failed")
return ret
print("face_detect_test end")
return 0
if __name__ == '__main__':
face_detect_inference()
3.COCO目标检测#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai 原图输入分辨率
OUT_RGB888P_WIDTH = ALIGN_UP(320, 16)
OUT_RGB888P_HEIGHT = 320
#多目标检测 kmodel 输入 shape
kmodel_input_shape = (1,3,320,320)
#多目标检测 相关参数设置
confidence_threshold = 0.2 # 多目标检测分数阈值
nms_threshold = 0.2 # 非最大值抑制阈值
x_factor = float(OUT_RGB888P_WIDTH)/kmodel_input_shape[3] # 原始图像分辨率宽与kmodel宽输入大小比值
y_factor = float(OUT_RGB888P_HEIGHT)/kmodel_input_shape[2] # 原始图像分辨率高与kmodel高输入大小比值
keep_top_k = 50 # 最大输出检测框的数量
#文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/yolov8n_320.kmodel' # kmodel文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#颜色板 用于作图
color_four = [(255, 220, 20, 60), (255, 119, 11, 32), (255, 0, 0, 142), (255, 0, 0, 230),
(255, 106, 0, 228), (255, 0, 60, 100), (255, 0, 80, 100), (255, 0, 0, 70),
(255, 0, 0, 192), (255, 250, 170, 30), (255, 100, 170, 30), (255, 220, 220, 0),
(255, 175, 116, 175), (255, 250, 0, 30), (255, 165, 42, 42), (255, 255, 77, 255),
(255, 0, 226, 252), (255, 182, 182, 255), (255, 0, 82, 0), (255, 120, 166, 157),
(255, 110, 76, 0), (255, 174, 57, 255), (255, 199, 100, 0), (255, 72, 0, 118),
(255, 255, 179, 240), (255, 0, 125, 92), (255, 209, 0, 151), (255, 188, 208, 182),
(255, 0, 220, 176), (255, 255, 99, 164), (255, 92, 0, 73), (255, 133, 129, 255),
(255, 78, 180, 255), (255, 0, 228, 0), (255, 174, 255, 243), (255, 45, 89, 255),
(255, 134, 134, 103), (255, 145, 148, 174), (255, 255, 208, 186),
(255, 197, 226, 255), (255, 171, 134, 1), (255, 109, 63, 54), (255, 207, 138, 255),
(255, 151, 0, 95), (255, 9, 80, 61), (255, 84, 105, 51), (255, 74, 65, 105),
(255, 166, 196, 102), (255, 208, 195, 210), (255, 255, 109, 65), (255, 0, 143, 149),
(255, 179, 0, 194), (255, 209, 99, 106), (255, 5, 121, 0), (255, 227, 255, 205),
(255, 147, 186, 208), (255, 153, 69, 1), (255, 3, 95, 161), (255, 163, 255, 0),
(255, 119, 0, 170), (255, 0, 182, 199), (255, 0, 165, 120), (255, 183, 130, 88),
(255, 95, 32, 0), (255, 130, 114, 135), (255, 110, 129, 133), (255, 166, 74, 118),
(255, 219, 142, 185), (255, 79, 210, 114), (255, 178, 90, 62), (255, 65, 70, 15),
(255, 127, 167, 115), (255, 59, 105, 106), (255, 142, 108, 45), (255, 196, 172, 0),
(255, 95, 54, 80), (255, 128, 76, 255), (255, 201, 57, 1), (255, 246, 0, 122),
(255, 191, 162, 208)]
#标签 多目标检测的所有可识别类别
labels = ["person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"]
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# 多目标检测 非最大值抑制方法实现
def py_cpu_nms(boxes,scores,thresh):
"""Pure Python NMS baseline."""
x1 = boxes[:, 0]
y1 = boxes[:, 1]
x2 = boxes[:, 2]
y2 = boxes[:, 3]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = np.argsort(scores,axis = 0)[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
new_x1 = []
new_x2 = []
new_y1 = []
new_y2 = []
new_areas = []
for order_i in order:
new_x1.append(x1[order_i])
new_x2.append(x2[order_i])
new_y1.append(y1[order_i])
new_y2.append(y2[order_i])
new_areas.append(areas[order_i])
new_x1 = np.array(new_x1)
new_x2 = np.array(new_x2)
new_y1 = np.array(new_y1)
new_y2 = np.array(new_y2)
xx1 = np.maximum(x1[i], new_x1)
yy1 = np.maximum(y1[i], new_y1)
xx2 = np.minimum(x2[i], new_x2)
yy2 = np.minimum(y2[i], new_y2)
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
new_areas = np.array(new_areas)
ovr = inter / (areas[i] + new_areas - inter)
new_order = []
for ovr_i,ind in enumerate(ovr):
if ind < thresh:
new_order.append(order[ovr_i])
order = np.array(new_order,dtype=np.uint8)
return keep
# 多目标检测 接收kmodel输出的后处理方法
def kpu_post_process(output_data):
with ScopedTiming("kpu_post_process", debug_mode > 0):
boxes_ori = output_data[:,0:4]
scores_ori = output_data[:,4:]
confs_ori = np.max(scores_ori,axis=-1)
inds_ori = np.argmax(scores_ori,axis=-1)
boxes = []
scores = []
inds = []
for i in range(len(boxes_ori)):
if confs_ori[i] > confidence_threshold:
scores.append(confs_ori[i])
inds.append(inds_ori[i])
x = boxes_ori[i,0]
y = boxes_ori[i,1]
w = boxes_ori[i,2]
h = boxes_ori[i,3]
left = int((x - 0.5 * w) * x_factor)
top = int((y - 0.5 * h) * y_factor)
right = int((x + 0.5 * w) * x_factor)
bottom = int((y + 0.5 * h) * y_factor)
boxes.append([left,top,right,bottom])
if len(boxes)==0:
return []
boxes = np.array(boxes)
scores = np.array(scores)
inds = np.array(inds)
# do NMS
keep = py_cpu_nms(boxes,scores,nms_threshold)
dets = np.concatenate((boxes, scores.reshape((len(boxes),1)), inds.reshape((len(boxes),1))), axis=1)
dets_out = []
for keep_i in keep:
dets_out.append(dets[keep_i])
dets_out = np.array(dets_out)
# keep top-K faster NMS
dets_out = dets_out[:keep_top_k, :]
return dets_out
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_out_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_out_tensor = nn.from_numpy(data)
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH], kmodel_input_shape)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_out_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_out_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_out_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_out_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
result = result.reshape((result.shape[0] * result.shape[1], result.shape[2]))
result = result.transpose()
tmp2 = result.copy()
del data
results.append(tmp2)
return results
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2) kpu运行
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3) 释放ai2d资源
ai2d_release()
# (4) 获取kpu输出
results = kpu_get_output()
# (5) kpu结果后处理
dets = kpu_post_process(results[0])
# (6) 返回多目标检测结果
return dets
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_out_tensor
del kpu_obj
del ai2d
del ai2d_out_tensor
#media_utils.py
global draw_img,osd_img #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 将所有目标检测框以及类别、分数值的作图
def display_draw(dets):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
for det in dets:
x1, y1, x2, y2 = map(lambda x: int(round(x, 0)), det[:4])
w = (x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = (y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT
draw_img.draw_rectangle(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH,
y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT, w, h, color=color_four[int(det[5])])
draw_img.draw_string( int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH) , int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT)-50,
" " + labels[int(det[5])] + " " + str(round(det[4],2)) , color=color_four[int(det[5])] , scale=4)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for ob_detect.py**********
def ob_detect_inference():
print("ob_detect start")
kpu_ob_detect = kpu_init(kmodel_file) # 创建多目标检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("ob_detect, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("ob_detect, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = kpu_run(kpu_ob_detect,rgb888p_img) # 执行多目标检测 kpu运行 以及 后处理过程
display_draw(dets) # 将得到的检测结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止camera
display_deinit() # 释放 display
kpu_deinit(kpu_ob_detect) # 释放 kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放 整个media
if ret:
print("ob_detect, buffer_deinit failed")
return ret
print("ob_detect_test end")
return 0
if __name__ == '__main__':
ob_detect_inference()
4.yolov8-seg#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(320, 16)
OUT_RGB888P_HEIGHT = 320
#多目标分割 kmodel 输入参数配置
kmodel_input_shape = (1,3,320,320) # kmodel输入分辨率
rgb_mean = [114,114,114] # ai2d padding 值
#多目标分割 相关参数设置
confidence_threshold = 0.2 # 多目标分割分数阈值
nms_threshold = 0.5 # 非最大值抑制阈值
mask_thres = 0.5 # 多目标分割掩码阈值
#文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/yolov8n_seg_320.kmodel' # kmodel文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#标签 多目标分割的所有可识别类别
labels = ["person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush"]
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# 多目标分割 接收kmodel输出的后处理方法
def kpu_post_process(output_datas):
with ScopedTiming("kpu_post_process", debug_mode > 0):
global masks
mask_dets = aidemo.segment_postprocess(output_datas,[OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH],[kmodel_input_shape[2],kmodel_input_shape[3]],[DISPLAY_HEIGHT,DISPLAY_WIDTH],confidence_threshold,nms_threshold,mask_thres,masks)
return mask_dets
# 获取kmodel输入图像resize比例 以及 padding的上下左右像素数量
def get_pad_param():
#右padding或下padding
dst_w = kmodel_input_shape[3]
dst_h = kmodel_input_shape[2]
ratio_w = float(dst_w) / OUT_RGB888P_WIDTH
ratio_h = float(dst_h) / OUT_RGB888P_HEIGHT
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGHT)
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
top = (int)(round(dh - 0.1))
bottom = (int)(round(dh + 0.1))
left = (int)(round(dw - 0.1))
right = (int)(round(dw + 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_pad_param(True, get_pad_param(), 0, rgb_mean)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_out_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_out_tensor = nn.from_numpy(data)
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH], kmodel_input_shape)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_out_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_out_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_out_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_out_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
data_0 = current_kmodel_obj.get_output_tensor(0)
result_0 = data_0.to_numpy()
del data_0
results.append(result_0)
data_1 = current_kmodel_obj.get_output_tensor(1)
result_1 = data_1.to_numpy()
del data_1
results.append(result_1)
return results
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2) kpu 运行
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3) 释放ai2d资源
ai2d_release()
# (4) 获取kpu输出
results = kpu_get_output()
# (5) kpu结果后处理
seg_res = kpu_post_process(results)
# (6) 返回 分割 mask 结果
return seg_res
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_out_tensor
del kpu_obj
del ai2d
del ai2d_out_tensor
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 将所有目标分割对象以及类别、分数值的作图
def display_draw(seg_res):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img,masks
if seg_res[0]:
dets = seg_res[0]
ids = seg_res[1]
scores = seg_res[2]
for i, det in enumerate(dets):
x1, y1, w, h = map(lambda x: int(round(x, 0)), det)
draw_img.draw_string( int(x1) , int(y1)-50, " " + labels[int(ids[i])] + " " + str(round(scores[i],2)) , color=(255,0,0,0), scale=4)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img, masks
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
masks = np.zeros((1,DISPLAY_HEIGHT,DISPLAY_WIDTH,4))
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888,alloc=image.ALLOC_REF,data=masks)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for seg.py**********
def seg_inference():
print("seg start")
kpu_seg = kpu_init(kmodel_file) # 创建多目标分割的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("seg, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("seg, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
seg_res = kpu_run(kpu_seg,rgb888p_img) # 执行多目标分割 kpu 运行 以及 后处理过程
display_draw(seg_res) # 将得到的分割结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
#gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
kpu_deinit(kpu_seg) # 释放 kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放 整个media
if ret:
print("seg, buffer_deinit failed")
return ret
print("seg end")
return 0
if __name__ == '__main__':
seg_inference()
5.车牌检测#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGHT = 1080
#车牌检测 kmodel 输入shape
kmodel_input_shape = (1,3,640,640)
#车牌检测 相关参数设置
obj_thresh = 0.2 #车牌检测分数阈值
nms_thresh = 0.2 #检测框 非极大值抑制 阈值
#文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/LPD_640.kmodel' # kmodel 文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# 车牌检测 接收kmodel输出的后处理方法
def kpu_post_process(output_data):
with ScopedTiming("kpu_post_process", debug_mode > 0):
results = aidemo.licence_det_postprocess(output_data,[OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH],[kmodel_input_shape[2],kmodel_input_shape[3]],obj_thresh,nms_thresh)
return results
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_out_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_out_tensor = nn.from_numpy(data)
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH], kmodel_input_shape)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_out_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_out_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_out_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_out_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
tmp2 = result.copy()
del data
results.append(tmp2)
return results
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2) kpu 运行
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3) 释放ai2d资源
ai2d_release()
# (4) 获取kpu输出
results = kpu_get_output()
# (5) kpu结果后处理
dets = kpu_post_process(results)
# (6) 返回 车牌检测框 结果
return dets
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_out_tensor
del kpu_obj
del ai2d
del ai2d_out_tensor
#media_utils.py
global draw_img,osd_img #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 将所有车牌检测框绘制到屏幕上
def display_draw(dets):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
point_8 = np.zeros((8),dtype=np.int16)
for det in dets:
for i in range(4):
x = det[i * 2 + 0]/OUT_RGB888P_WIDTH*DISPLAY_WIDTH
y = det[i * 2 + 1]/OUT_RGB888P_HEIGHT*DISPLAY_HEIGHT
point_8[i * 2 + 0] = int(x)
point_8[i * 2 + 1] = int(y)
for i in range(4):
draw_img.draw_line(point_8[i * 2 + 0],point_8[i * 2 + 1],point_8[(i+1) % 4 * 2 + 0],point_8[(i+1) % 4 * 2 + 1],color=(255, 0, 255, 0),thickness=2)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_BGR_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for licence_det.py**********
def licence_det_inference():
print("licence_det start")
kpu_licence_det = kpu_init(kmodel_file) # 创建车牌检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("licence_det, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("licence_det, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = kpu_run(kpu_licence_det,rgb888p_img) # 执行车牌检测 kpu 运行 以及后处理过程
display_draw(dets) # 将得到的 检测结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
kpu_deinit(kpu_licence_det) # 释放 kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放整个media
if ret:
print("licence_det, buffer_deinit failed")
return ret
print("licence_det end")
return 0
if __name__ == '__main__':
licence_det_inference()
6.OCR检测#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import os #操作系统接口模块
import aicube #aicube模块,封装检测分割等任务相关后处理
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
# ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(640, 16)
OUT_RGB888P_HEIGH = 360
# kmodel输入参数配置
kmodel_input_shape_det = (1,3,640,640) # kmodel输入分辨率
rgb_mean = [0,0,0] # ai2d padding的值
# kmodel相关参数设置
mask_threshold = 0.25 # 二值化mask阈值
box_threshold = 0.3 # 检测框分数阈值
# 文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file_det = root_dir + 'kmodel/ocr_det_int16.kmodel' # kmodel加载路径
debug_mode = 0 # 调试模式 大于0(调试)、 反之 (不调试)
# scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
# ai utils
global current_kmodel_obj # 定义全局kpu对象
global ai2d_det,ai2d_input_tensor_det,ai2d_output_tensor_det,ai2d_builder_det,ai2d_input_det # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# padding方法,一边padding,右padding或者下padding
def get_pad_one_side_param(out_img_size,input_img_size):
# 右padding或下padding
dst_w = out_img_size[0]
dst_h = out_img_size[1]
input_width = input_img_size[0]
input_high = input_img_size[1]
ratio_w = dst_w / input_width
ratio_h = dst_h / input_high
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
new_w = (int)(ratio * input_width)
new_h = (int)(ratio * input_high)
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
# ai2d 初始化,用于实现输入的预处理
def ai2d_init_det():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d_det
ai2d_det = nn.ai2d()
ai2d_det.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d_det.set_pad_param(True, get_pad_one_side_param([kmodel_input_shape_det[3],kmodel_input_shape_det[2]], [OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH]), 0, [0, 0, 0])
ai2d_det.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_output_tensor_det
data = np.ones(kmodel_input_shape_det, dtype=np.uint8)
ai2d_output_tensor_det = nn.from_numpy(data)
global ai2d_builder_det
ai2d_builder_det = ai2d_det.build([1, 3, OUT_RGB888P_HEIGH, OUT_RGB888P_WIDTH], [1, 3, kmodel_input_shape_det[2], kmodel_input_shape_det[3]])
# ai2d 运行,完成ai2d_init_det设定的预处理
def ai2d_run_det(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor_det,ai2d_builder_det,ai2d_input_det
ai2d_input_det = rgb888p_img.to_numpy_ref()
ai2d_input_tensor_det = nn.from_numpy(ai2d_input_det)
global ai2d_output_tensor_det
ai2d_builder_det.run(ai2d_input_tensor_det, ai2d_output_tensor_det)
# ai2d 释放输入tensor
def ai2d_release_det():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor_det
del ai2d_input_tensor_det
# kpu 初始化
def kpu_init_det(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init_det()
return kpu_obj
# 预处理方法
def kpu_pre_process_det(rgb888p_img):
# 运行ai2d,将ai2d预处理的输出设置为kmodel的输入tensor
ai2d_run_det(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor_det
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor_det)
# 获取kmodel的推理输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
# kpu 运行
def kpu_run_det(kpu_obj,rgb888p_img):
# kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
#(1)原图像预处理并设置模型输入
kpu_pre_process_det(rgb888p_img)
#(2)kpu推理
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
#(3)释放ai2d资源
ai2d_release_det()
#(4)获取kpu输出
results = kpu_get_output()
#(5)CHW转HWC
global ai2d_input_det
tmp = (ai2d_input_det.shape[0], ai2d_input_det.shape[1], ai2d_input_det.shape[2])
ai2d_input_det = ai2d_input_det.reshape((ai2d_input_det.shape[0], ai2d_input_det.shape[1] * ai2d_input_det.shape[2]))
ai2d_input_det = ai2d_input_det.transpose()
tmp2 = ai2d_input_det.copy()
tmp2 = tmp2.reshape((tmp[1], tmp[2], tmp[0]))
#(6)后处理,aicube.ocr_post_process接口说明:
# 接口:aicube.ocr_post_process(threshold_map,ai_isp,kmodel_input_shape,isp_shape,mask_threshold,box_threshold);
# 参数说明:
# threshold_map: DBNet模型的输出为(N,kmodel_input_shape_det[2],kmodel_input_shape_det[3],2),两个通道分别为threshold map和segmentation map
# 后处理过程只使用threshold map,因此将results[0][:,:,:,0] reshape成一维传给接口使用。
# ai_isp:后处理还会返回基于原图的检测框裁剪数据,因此要将原图数据reshape为一维传给接口处理。
# kmodel_input_shape:kmodel输入分辨率。
# isp_shape:AI原图分辨率。要将kmodel输出分辨率的检测框坐标映射到原图分辨率上,需要使用这两个分辨率的值。
# mask_threshold:用于二值化图像获得文本区域。
# box_threshold:检测框分数阈值,低于该阈值的检测框不计入结果。
with ScopedTiming("kpu_post",debug_mode > 0):
# 调用aicube模块的ocr_post_process完成ocr检测的后处理
# det_results结构为[[crop_array_nhwc,[p1_x,p1_y,p2_x,p2_y,p3_x,p3_y,p4_x,p4_y]],...]
det_results = aicube.ocr_post_process(results[0][:, :, :, 0].reshape(-1), tmp2.reshape(-1),
[kmodel_input_shape_det[3], kmodel_input_shape_det[2]],
[OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH], mask_threshold, box_threshold)
return det_results
# kpu 释放内存
def kpu_deinit_det(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d_det,ai2d_output_tensor_det,ai2d_input_tensor_det
del kpu_obj
del ai2d_det
del ai2d_output_tensor_det
#del ai2d_input_tensor_det
#********************for media_utils.py********************
global draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
#display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程,将OCR检测后处理得到的框绘制到OSD上并显示
def display_draw(det_results):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if det_results:
draw_img.clear()
# 循环绘制所有检测到的框
for j in det_results:
# 将原图的坐标点转换成显示的坐标点,循环绘制四条直线,得到一个矩形框
for i in range(4):
x1 = j[1][(i * 2)] / OUT_RGB888P_WIDTH * DISPLAY_WIDTH
y1 = j[1][(i * 2 + 1)] / OUT_RGB888P_HEIGH * DISPLAY_HEIGHT
x2 = j[1][((i + 1) * 2) % 8] / OUT_RGB888P_WIDTH * DISPLAY_WIDTH
y2 = j[1][((i + 1) * 2 + 1) % 8] / OUT_RGB888P_HEIGH * DISPLAY_HEIGHT
draw_img.draw_line((int(x1), int(y1), int(x2), int(y2)), color=(255, 0, 0, 255),
thickness=5)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 启动视频流
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 捕获一帧图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 释放内存
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# 停止视频流
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放buffer,销毁link
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#
def ocr_det_inference():
print("ocr_det_test start")
kpu_ocr_det = kpu_init_det(kmodel_file_det) # 创建ocr检测任务的kpu对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("ocr_det_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图像
if rgb888p_img == -1:
print("ocr_det_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
det_results = kpu_run_det(kpu_ocr_det,rgb888p_img) # kpu运行获取kmodel的推理输出
display_draw(det_results) # 绘制检测结果,并显示
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # 释放内存
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止camera
display_deinit() # 释放display
kpu_deinit_det(kpu_ocr_det) # 释放kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放整个media
if ret:
print("ocr_det_test, buffer_deinit failed")
return ret
print("ocr_det_test end")
return 0
if __name__ == '__main__':
ocr_det_inference()
7.手掌检测#
import aicube #aicube模块,封装检测分割等任务相关后处理
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import time #时间统计
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import gc #垃圾回收模块
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
##ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGH = 1080
#kmodel输入shape
kmodel_input_shape = (1,3,512,512) # kmodel输入分辨率
#kmodel相关参数设置
confidence_threshold = 0.2 # 手掌检测阈值,用于过滤roi
nms_threshold = 0.5 # 手掌检测框阈值,用于过滤重复roi
kmodel_frame_size = [512,512] # 手掌检测输入图片尺寸
frame_size = [OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH] # 直接输入图片尺寸
strides = [8,16,32] # 输出特征图的尺寸与输入图片尺寸的比
num_classes = 1 # 模型输出类别数
nms_option = False # 是否所有检测框一起做NMS,False则按照不同的类分别应用NMS
labels = ["hand"] # 模型输出类别名称
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/hand_det.kmodel' # kmodel文件的路径
anchors = [26,27, 53,52, 75,71, 80,99, 106,82, 99,134, 140,113, 161,172, 245,276] #anchor设置
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
global ai2d_builder
global ai2d_output_tensor
# 计算padding值
ori_w = OUT_RGB888P_WIDTH
ori_h = OUT_RGB888P_HEIGH
width = kmodel_frame_size[0]
height = kmodel_frame_size[1]
ratiow = float(width) / ori_w
ratioh = float(height) / ori_h
if ratiow < ratioh:
ratio = ratiow
else:
ratio = ratioh
new_w = int(ratio * ori_w)
new_h = int(ratio * ori_h)
dw = float(width - new_w) / 2
dh = float(height - new_h) / 2
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw - 0.1))
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_pad_param(True, [0,0,0,0,top,bottom,left,right], 0, [114,114,114])
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,height,width])
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_output_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]*result.shape[2]*result.shape[3]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2)手掌检测 kpu 运行
with ScopedTiming("kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放手掌检测 ai2d 资源
ai2d_release()
# (4)获取手掌检测 kpu 输出
results = kpu_get_output()
# (5)手掌检测 kpu 结果后处理
dets = aicube.anchorbasedet_post_process( results[0], results[1], results[2], kmodel_frame_size, frame_size, strides, num_classes, confidence_threshold, nms_threshold, anchors, nms_option)
# (6)返回手掌检测结果
return dets
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d, ai2d_output_tensor
del kpu_obj
del ai2d
del ai2d_output_tensor
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 框出所有检测到的手以及标出得分
def display_draw(dets):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
for det_box in dets:
x1, y1, x2, y2 = det_box[2],det_box[3],det_box[4],det_box[5]
w = float(x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = float(y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
x1 = int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y1 = int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x2 = int(x2 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y2 = int(y2 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
if (h<(0.1*DISPLAY_HEIGHT)):
continue
if (w<(0.25*DISPLAY_WIDTH) and ((x1<(0.03*DISPLAY_WIDTH)) or (x2>(0.97*DISPLAY_WIDTH)))):
continue
if (w<(0.15*DISPLAY_WIDTH) and ((x1<(0.01*DISPLAY_WIDTH)) or (x2>(0.99*DISPLAY_WIDTH)))):
continue
draw_img.draw_rectangle(x1 , y1 , int(w) , int(h) , color=(255, 0, 255, 0))
draw_img.draw_string( x1 , y1-50, " " + labels[det_box[0]] + " " + str(round(det_box[1],2)) , color=(255,0, 255, 0), scale=4)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for hand_detect.py**********
def hand_detect_inference():
print("hand_detect_test start")
kpu_hand_detect = kpu_init(kmodel_file) # 创建手掌检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("hand_detect_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("hand_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = kpu_run(kpu_hand_detect,rgb888p_img) # 执行手掌检测 kpu 运行 以及 后处理过程
display_draw(dets) # 将得到的检测结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
#gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
kpu_deinit(kpu_hand_detect) # 释放 kpu
gc.collect()
ret = media_deinit() # 释放 整个media
if ret:
print("hand_detect_test, buffer_deinit failed")
return ret
print("hand_detect_test end")
return 0
if __name__ == '__main__':
hand_detect_inference()
8.人体检测#
import aicube #aicube模块,封装检测分割等任务相关后处理
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import time #时间统计
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import gc #垃圾回收模块
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
##ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGH = 1080
#kmodel输入shape
kmodel_input_shape = (1,3,640,640) # kmodel输入分辨率
#kmodel相关参数设置
confidence_threshold = 0.2 # 行人检测阈值,用于过滤roi
nms_threshold = 0.6 # 行人检测框阈值,用于过滤重复roi
kmodel_frame_size = [640,640] # 行人检测输入图片尺寸
frame_size = [OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH] # 直接输入图片尺寸
strides = [8,16,32] # 输出特征图的尺寸与输入图片尺寸的比
num_classes = 1 # 模型输出类别数
nms_option = False # 是否所有检测框一起做NMS,False则按照不同的类分别应用NMS
labels = ["person"] # 模型输出类别名称
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/person_detect_yolov5n.kmodel' # kmodel文件的路径
anchors = [10, 13, 16, 30, 33, 23, 30, 61, 62, 45, 59, 119, 116, 90, 156, 198, 373, 326] #anchor设置
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
total_debug_mode = 1
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
global ai2d_builder
global ai2d_output_tensor
# 计算padding值
ori_w = OUT_RGB888P_WIDTH
ori_h = OUT_RGB888P_HEIGH
width = kmodel_frame_size[0]
height = kmodel_frame_size[1]
ratiow = float(width) / ori_w
ratioh = float(height) / ori_h
if ratiow < ratioh:
ratio = ratiow
else:
ratio = ratioh
new_w = int(ratio * ori_w)
new_h = int(ratio * ori_h)
dw = float(width - new_w) / 2
dh = float(height - new_h) / 2
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw - 0.1))
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_pad_param(True, [0,0,0,0,top,bottom,left,right], 0, [114,114,114])
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,height,width])
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_output_tensor = nn.from_numpy(data)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_output_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_output_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]*result.shape[2]*result.shape[3]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2)行人检测 kpu 运行
with ScopedTiming("kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放行人检测 ai2d 资源
ai2d_release()
# (4)获取行人检测 kpu 输出
results = kpu_get_output()
# (5)行人检测 kpu 结果后处理
with ScopedTiming("kpu_post_process",debug_mode > 0):
dets = aicube.anchorbasedet_post_process( results[0], results[1], results[2], kmodel_frame_size, frame_size, strides, num_classes, confidence_threshold, nms_threshold, anchors, nms_option)
# (6)返回行人检测结果
return dets
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d, ai2d_output_tensor
del kpu_obj
del ai2d
del ai2d_output_tensor
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 框出所有检测到的人以及标出得分
def display_draw(dets):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
for det_box in dets:
x1, y1, x2, y2 = det_box[2],det_box[3],det_box[4],det_box[5]
w = float(x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = float(y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
x1 = int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y1 = int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x2 = int(x2 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y2 = int(y2 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
if (h<(0.1*DISPLAY_HEIGHT)):
continue
if (w<(0.25*DISPLAY_WIDTH) and ((x1<(0.03*DISPLAY_WIDTH)) or (x2>(0.97*DISPLAY_WIDTH)))):
continue
if (w<(0.15*DISPLAY_WIDTH) and ((x1<(0.01*DISPLAY_WIDTH)) or (x2>(0.99*DISPLAY_WIDTH)))):
continue
draw_img.draw_rectangle(x1 , y1 , int(w) , int(h) , color=(255, 0, 255, 0),thickness = 2)
draw_img.draw_string( x1 , y1-50, " " + labels[det_box[0]] + " " + str(round(det_box[1],2)) , color=(255,0, 255, 0), scale=4)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for person_detect.py**********
def person_detect_inference():
print("person_detect_test start")
kpu_person_detect = kpu_init(kmodel_file) # 创建行人检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("person_detect_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
while True:
with ScopedTiming("total",total_debug_mode):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("person_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = kpu_run(kpu_person_detect,rgb888p_img) # 执行行人检测 kpu 运行 以及 后处理过程
display_draw(dets) # 将得到的检测结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
kpu_deinit(kpu_person_detect) # 释放 kpu
gc.collect()
ret = media_deinit() # 释放 整个media
if ret:
print("person_detect_test, buffer_deinit failed")
return ret
print("person_detect_test end")
return 0
if __name__ == '__main__':
person_detect_inference()
9.人体姿态估计#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGHT = 1080
#人体关键点检测 kmodel 输入参数配置
kmodel_input_shape = (1,3,640,640) # kmodel输入分辨率
rgb_mean = [114,114,114] # ai2d padding 值
#人体关键点 相关参数设置
confidence_threshold = 0.2 # 人体关键点检测分数阈值
nms_threshold = 0.5 # 非最大值抑制阈值
#文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file = root_dir + 'kmodel/yolov8n-pose.kmodel' # kmodel文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#骨骼信息
SKELETON = [(16, 14),(14, 12),(17, 15),(15, 13),(12, 13),(6, 12),(7, 13),(6, 7),(6, 8),(7, 9),(8, 10),(9, 11),(2, 3),(1, 2),(1, 3),(2, 4),(3, 5),(4, 6),(5, 7)]
#肢体颜色
LIMB_COLORS = [(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255),(255, 255, 51, 255),(255, 255, 51, 255),(255, 255, 51, 255),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0)]
#关键点颜色
KPS_COLORS = [(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 0, 255, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 255, 128, 0),(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255),(255, 51, 153, 255)]
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global ai2d,ai2d_input_tensor,ai2d_output_tensor,ai2d_builder # 定义全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
# 人体关键点检测 接收kmodel输出的后处理方法
def kpu_post_process(output_datas):
with ScopedTiming("kpu_post_process", debug_mode > 0):
results = aidemo.person_kp_postprocess(output_datas,[OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH],[kmodel_input_shape[2],kmodel_input_shape[3]],confidence_threshold,nms_threshold)
return results
# 获取kmodel输入图像resize比例 以及 padding的上下左右像素数量
def get_pad_param():
#右padding或下padding
dst_w = kmodel_input_shape[3]
dst_h = kmodel_input_shape[2]
ratio_w = float(dst_w) / OUT_RGB888P_WIDTH
ratio_h = float(dst_h) / OUT_RGB888P_HEIGHT
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGHT)
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
top = (int)(round(dh))
bottom = (int)(round(dh))
left = (int)(round(dw))
right = (int)(round(dw))
return [0, 0, 0, 0, top, bottom, left, right]
# ai2d 初始化
def ai2d_init():
with ScopedTiming("ai2d_init",debug_mode > 0):
global ai2d
ai2d = nn.ai2d()
ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d.set_pad_param(True, get_pad_param(), 0, rgb_mean)
ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_out_tensor
data = np.ones(kmodel_input_shape, dtype=np.uint8)
ai2d_out_tensor = nn.from_numpy(data)
global ai2d_builder
ai2d_builder = ai2d.build([1,3,OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH], kmodel_input_shape)
# ai2d 运行
def ai2d_run(rgb888p_img):
with ScopedTiming("ai2d_run",debug_mode > 0):
global ai2d_input_tensor,ai2d_out_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
ai2d_input_tensor = nn.from_numpy(ai2d_input)
ai2d_builder.run(ai2d_input_tensor, ai2d_out_tensor)
# ai2d 释放内存
def ai2d_release():
with ScopedTiming("ai2d_release",debug_mode > 0):
global ai2d_input_tensor
del ai2d_input_tensor
# kpu 初始化
def kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init()
return kpu_obj
# kpu 输入预处理
def kpu_pre_process(rgb888p_img):
ai2d_run(rgb888p_img)
with ScopedTiming("kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,ai2d_out_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_out_tensor)
# kpu 获得 kmodel 输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result
# kpu 运行
def kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
kpu_pre_process(rgb888p_img)
# (2) kpu 运行
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3) 释放ai2d资源
ai2d_release()
# (4) 获取kpu输出
results = kpu_get_output()
# (5) kpu结果后处理
kp_res = kpu_post_process(results)
# (6) 返回 人体关键点检测 结果
return kp_res
# kpu 释放内存
def kpu_deinit(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d,ai2d_out_tensor
del kpu_obj
del ai2d
del ai2d_out_tensor
#media_utils.py
global draw_img,osd_img #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 将所有目标关键点作图
def display_draw(res):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if res[0]:
draw_img.clear()
kpses = res[1]
for i in range(len(res[0])):
for k in range(17+2):
if (k < 17):
kps_x = round(kpses[i][k][0])
kps_y = round(kpses[i][k][1])
kps_s = kpses[i][k][2]
kps_x1 = int(float(kps_x) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
kps_y1 = int(float(kps_y) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT)
if (kps_s > 0):
draw_img.draw_circle(kps_x1,kps_y1,5,KPS_COLORS[k],4)
ske = SKELETON[k]
pos1_x = round(kpses[i][ske[0]-1][0])
pos1_y = round(kpses[i][ske[0]-1][1])
pos1_x_ = int(float(pos1_x) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
pos1_y_ = int(float(pos1_y) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT)
pos2_x = round(kpses[i][(ske[1] -1)][0])
pos2_y = round(kpses[i][(ske[1] -1)][1])
pos2_x_ = int(float(pos2_x) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
pos2_y_ = int(float(pos2_y) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGHT)
pos1_s = kpses[i][(ske[0] -1)][2]
pos2_s = kpses[i][(ske[1] -1)][2]
if (pos1_s > 0.0 and pos2_s >0.0):
draw_img.draw_line(pos1_x,pos1_y,pos2_x,pos2_y,LIMB_COLORS[k],4)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for person_kp_detect.py**********
def person_kp_detect_inference():
print("person_kp_detect start")
kpu_person_kp_detect = kpu_init(kmodel_file) # 创建人体关键点检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("person_kp_detect, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("person_kp_detect, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
person_kp_detect_res = kpu_run(kpu_person_kp_detect,rgb888p_img) # 执行人体关键点检测 kpu 运行 以及 后处理过程
display_draw(person_kp_detect_res) # 将得到的人体关键点结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
#gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
kpu_deinit(kpu_person_kp_detect) # 释放 kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放 整个media
if ret:
print("person_kp_detect, buffer_deinit failed")
return ret
print("person_kp_detect end")
return 0
if __name__ == '__main__':
person_kp_detect_inference()
10.KWS#
from media.pyaudio import * # 音频模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import media.wave as wave # wav音频处理模块
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import aidemo # aidemo模块,封装ai demo相关前处理、后处理等操作
import time # 时间统计
import struct # 字节字符转换模块
import gc # 垃圾回收模块
import os # 操作系统接口模块
# key word spotting任务
# 检测阈值
THRESH = 0.5
# 有关音频流的宏变量
SAMPLE_RATE = 16000 # 采样率16000Hz,即每秒采样16000次
CHANNELS = 1 # 通道数 1为单声道,2为立体声
FORMAT = paInt16 # 音频输入输出格式 paInt16
CHUNK = int(0.3 * 16000) # 每次读取音频数据的帧数,设置为0.3s的帧数16000*0.3=4800
root_dir='/sdcard/app/tests/'
kmodel_file_kws = root_dir+"kmodel/kws.kmodel" # kmodel加载路径
reply_wav_file = root_dir+"utils/wozai.wav" # kws唤醒词回复音频路径
debug_mode = 0 # 调试模式,大于0(调试)、 反之 (不调试)
# scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
global current_kmodel_obj # 定义全局kpu对象
global p,cache_np,fp,input_stream,output_stream,audio_input_tensor,cache_input_tensor # 定义全局音频流对象,输入输出流对象,并且定义kws处理接口FeaturePipeline对象fp,输入输出tensor和缓冲cache_np
# 初始化kws音频流相关变量
def init_kws():
with ScopedTiming("init_kws",debug_mode > 0):
global p,cache_np,fp,input_stream,output_stream,cache_input_tensor
# 初始化模型的cache输入
cache_np = np.zeros((1, 256, 105), dtype=np.float)
cache_input_tensor = nn.from_numpy(cache_np)
# 初始化音频预处理接口
fp = aidemo.kws_fp_create()
# 初始化音频流
p = PyAudio()
p.initialize(CHUNK)
ret = media.buffer_init()
if ret:
print("record_audio, buffer_init failed")
# 用于采集实时音频数据
input_stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=SAMPLE_RATE,
input=True,
frames_per_buffer=CHUNK
)
# 用于播放回复音频
output_stream = p.open(
format=FORMAT,
channels=CHANNELS,
rate=SAMPLE_RATE,
output=True,
frames_per_buffer=CHUNK
)
# kws 初始化kpu
def kpu_init_kws():
with ScopedTiming("init_kpu",debug_mode > 0):
# 初始化kpu并加载kmodel
kpu = nn.kpu()
kpu.load_kmodel(kmodel_file_kws)
return kpu
# kws音频预处理
def kpu_pre_process_kws(pcm_data_list):
global current_kmodel_obj
global fp,input_stream,audio_input_tensor,cache_input_tensor
with ScopedTiming("pre_process",debug_mode > 0):
# 将pcm数据处理为模型输入的特征向量
mp_feats = aidemo.kws_preprocess(fp, pcm_data_list)[0]
mp_feats_np = np.array(mp_feats)
mp_feats_np = mp_feats_np.reshape((1, 30, 40))
audio_input_tensor = nn.from_numpy(mp_feats_np)
cache_input_tensor = nn.from_numpy(cache_np)
current_kmodel_obj.set_input_tensor(0, audio_input_tensor)
current_kmodel_obj.set_input_tensor(1, cache_input_tensor)
# kws任务kpu运行并完成后处理
def kpu_run_kws(kpu_obj,pcm_data_list):
global current_kmodel_obj,cache_np,output_stream
current_kmodel_obj = kpu_obj
# (1)kws音频数据预处理
kpu_pre_process_kws(pcm_data_list)
# (2)kpu推理
with ScopedTiming("kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)获取模型输出
logits = kpu_obj.get_output_tensor(0)
cache_tensor = kpu_obj.get_output_tensor(1) # 更新缓存输入
logits_np = logits.to_numpy()
cache_np=cache_tensor.to_numpy()
del logits
del cache_tensor
# (4)后处理argmax
max_logits = np.max(logits_np, axis=1)[0]
max_p = np.max(max_logits)
idx = np.argmax(max_logits)
# 如果分数大于阈值,且idx==1(即包含唤醒词),播放回复音频
if max_p > THRESH and idx == 1:
print("====Detected XiaonanXiaonan!====")
wf = wave.open(reply_wav_file, "rb")
wav_data = wf.read_frames(CHUNK)
while wav_data:
output_stream.write(wav_data)
wav_data = wf.read_frames(CHUNK)
time.sleep(1) # 时间缓冲,用于播放声音
wf.close()
else:
print("Deactivated!")
# kws推理过程
def kws_inference():
# 记录音频帧帧数
global p,fp,input_stream,output_stream
# 初始化
init_kws()
kpu_kws=kpu_init_kws()
pcm_data_list = []
try:
while True:
with ScopedTiming("total", 1):
pcm_data_list.clear()
# 对实时音频流进行推理
pcm_data = input_stream.read() # 获取的音频流数据字节数,len(pcm_data)=0.3*16000*2=9600,即以16000Hz的采样率采样0.3s,每次采样数据为paInt16格式占2个字节
# 获取音频流数据
for i in range(0, len(pcm_data), 2):
# 每两个字节组织成一个有符号整数,然后将其转换为浮点数,即为一次采样的数据,加入到当前一帧(0.3s)的数据列表中
int_pcm_data = struct.unpack("<h", pcm_data[i:i+2])[0]
float_pcm_data = float(int_pcm_data)
pcm_data_list.append(float_pcm_data)
# kpu运行和后处理
kpu_run_kws(kpu_kws,pcm_data_list)
finally:
input_stream.stop_stream()
output_stream.stop_stream()
input_stream.close()
output_stream.close()
p.terminate()
media.buffer_deinit()
aidemo.kws_fp_destroy(fp)
#gc.collect()
if __name__=="__main__":
kws_inference()
三、AI Demo多模型示例解析#
1. 人脸关键点检测#
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * # 摄像头模块
from media.display import * # 显示模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo # aidemo模块,封装ai demo相关后处理、画图操作
import image # 图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time # 时间统计
import gc # 垃圾回收模块
import os # 操作系统接口模块
import math # 数学模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel参数设置
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸关键点kmodel输入shape
fld_kmodel_input_shape = (1,3,192,192)
# ai原图padding
rgb_mean = [104,117,123]
#人脸检测kmodel其它参数设置
confidence_threshold = 0.5 # 人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
# 文件配置
# 人脸检测kmodel
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸关键点kmodel
fr_kmodel_file = root_dir + 'kmodel/face_landmark.kmodel'
# anchor文件
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
# 人脸关键点不同部位关键点列表
dict_kp_seq = [
[43, 44, 45, 47, 46, 50, 51, 49, 48], # left_eyebrow
[97, 98, 99, 100, 101, 105, 104, 103, 102], # right_eyebrow
[35, 36, 33, 37, 39, 42, 40, 41], # left_eye
[89, 90, 87, 91, 93, 96, 94, 95], # right_eye
[34, 88], # pupil
[72, 73, 74, 86], # bridge_nose
[77, 78, 79, 80, 85, 84, 83], # wing_nose
[52, 55, 56, 53, 59, 58, 61, 68, 67, 71, 63, 64], # out_lip
[65, 54, 60, 57, 69, 70, 62, 66], # in_lip
[1, 9, 10, 11, 12, 13, 14, 15, 16, 2, 3, 4, 5, 6, 7, 8, 0, 24, 23, 22, 21, 20, 19, 18, 32, 31, 30, 29, 28, 27, 26, 25, 17] # basin
]
# 人脸关键点不同部位(顺序同dict_kp_seq)颜色配置,argb
color_list_for_osd_kp = [
(255, 0, 255, 0),
(255, 0, 255, 0),
(255, 255, 0, 255),
(255, 255, 0, 255),
(255, 255, 0, 0),
(255, 255, 170, 0),
(255, 255, 255, 0),
(255, 0, 255, 255),
(255, 255, 220, 50),
(255, 30, 30, 255)
]
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu对象
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fld_ai2d: 人脸关键点ai2d实例
# fld_ai2d_input_tensor: 人脸关键点ai2d输入
# fld_ai2d_output_tensor:人脸关键点ai2d输入
# fld_ai2d_builder: 根据人脸关键点ai2d参数,构建的人脸关键点ai2d_builder对象
global fld_ai2d,fld_ai2d_input_tensor,fld_ai2d_output_tensor,fld_ai2d_builder
global matrix_dst #人脸仿射变换矩阵
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
# (2)设置人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
#(3)人脸检测ai2d_builder,根据人脸检测ai2d参数、输入输出大小创建ai2d_builder对象
global fd_ai2d_builder
fd_ai2d_builder = fd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fd_kmodel_input_shape)
#(4)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fd_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d_input_tensor
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor
del fd_ai2d_input_tensor
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
def get_affine_matrix(bbox):
# 获取仿射矩阵,用于将边界框映射到模型输入空间
with ScopedTiming("get_affine_matrix", debug_mode > 1):
# 从边界框提取坐标和尺寸
x1, y1, w, h = map(lambda x: int(round(x, 0)), bbox[:4])
# 计算缩放比例,使得边界框映射到模型输入空间的一部分
scale_ratio = (fld_kmodel_input_shape[2]) / (max(w, h) * 1.5)
# 计算边界框中心点在模型输入空间的坐标
cx = (x1 + w / 2) * scale_ratio
cy = (y1 + h / 2) * scale_ratio
# 计算模型输入空间的一半长度
half_input_len = fld_kmodel_input_shape[2] / 2
# 创建仿射矩阵并进行设置
matrix_dst = np.zeros((2, 3), dtype=np.float)
matrix_dst[0, 0] = scale_ratio
matrix_dst[0, 1] = 0
matrix_dst[0, 2] = half_input_len - cx
matrix_dst[1, 0] = 0
matrix_dst[1, 1] = scale_ratio
matrix_dst[1, 2] = half_input_len - cy
return matrix_dst
def fld_ai2d_init():
# 人脸关键点ai2d初始化
with ScopedTiming("fld_ai2d_init",debug_mode > 0):
# (1)创建人脸关键点ai2d对象
global fld_ai2d
fld_ai2d = nn.ai2d()
# (2)创建人脸关键点ai2d_output_tensor对象,用于存放ai2d输出
global fld_ai2d_output_tensor
data = np.ones(fld_kmodel_input_shape, dtype=np.uint8)
fld_ai2d_output_tensor = nn.from_numpy(data)
def fld_ai2d_run(rgb888p_img,det):
# 人脸关键点ai2d推理
with ScopedTiming("fld_ai2d_run",debug_mode > 0):
global fld_ai2d,fld_ai2d_input_tensor,fld_ai2d_output_tensor
#(1)根据原图ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fld_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)根据新的det设置新的人脸关键点ai2d参数
fld_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global matrix_dst
matrix_dst = get_affine_matrix(det)
affine_matrix = [matrix_dst[0][0],matrix_dst[0][1],matrix_dst[0][2],
matrix_dst[1][0],matrix_dst[1][1],matrix_dst[1][2]]
fld_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,affine_matrix)
# (3)根据新的人脸关键点ai2d参数,构建人脸关键点ai2d_builder
global fld_ai2d_builder
fld_ai2d_builder = fld_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fld_kmodel_input_shape)
# (4)推理人脸关键点ai2d,将预处理的结果保存到fld_ai2d_output_tensor
fld_ai2d_builder.run(fld_ai2d_input_tensor, fld_ai2d_output_tensor)
def fld_ai2d_release():
# 释放人脸关键点ai2d_input_tensor、ai2d_builder
with ScopedTiming("fld_ai2d_release",debug_mode > 0):
global fld_ai2d_input_tensor,fld_ai2d_builder
del fld_ai2d_input_tensor
del fld_ai2d_builder
def fld_kpu_init(kmodel_file):
# 人脸关键点kpu初始化
with ScopedTiming("fld_kpu_init",debug_mode > 0):
# 初始化人脸关键点kpu对象
kpu_obj = nn.kpu()
# 加载人脸关键点kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸关键点ai2d
fld_ai2d_init()
return kpu_obj
def fld_kpu_pre_process(rgb888p_img,det):
# 人脸关键点kpu预处理
# 人脸关键点ai2d推理,根据det对原图进行预处理
fld_ai2d_run(rgb888p_img,det)
with ScopedTiming("fld_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fld_ai2d_output_tensor
# 将人脸关键点ai2d输出设置为人脸关键点kpu输入
current_kmodel_obj.set_input_tensor(0, fld_ai2d_output_tensor)
#ai2d_out_data = fld_ai2d_output_tensor.to_numpy()
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
def fld_kpu_get_output():
with ScopedTiming("fld_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取人脸关键点kpu输出
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result
def fld_kpu_post_process(pred):
# 人脸关键点kpu推理结果后处理
with ScopedTiming("fld_kpu_post_process",debug_mode > 0):
# (1)将人脸关键点输出变换模型输入
half_input_len = fld_kmodel_input_shape[2] // 2
pred = pred.flatten()
for i in range(len(pred)):
pred[i] += (pred[i] + 1) * half_input_len
# (2)获取仿射矩阵的逆矩阵
global matrix_dst
matrix_dst_inv = aidemo.invert_affine_transform(matrix_dst)
matrix_dst_inv = matrix_dst_inv.flatten()
# (3)对每个关键点进行逆变换
half_out_len = len(pred) // 2
for kp_id in range(half_out_len):
old_x = pred[kp_id * 2]
old_y = pred[kp_id * 2 + 1]
# 逆变换公式
new_x = old_x * matrix_dst_inv[0] + old_y * matrix_dst_inv[1] + matrix_dst_inv[2]
new_y = old_x * matrix_dst_inv[3] + old_y * matrix_dst_inv[4] + matrix_dst_inv[5]
pred[kp_id * 2] = new_x
pred[kp_id * 2 + 1] = new_y
return pred
def fld_kpu_run(kpu_obj,rgb888p_img,det):
# 人脸关键点kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)人脸关键点kpu预处理,设置kpu输入
fld_kpu_pre_process(rgb888p_img,det)
# (2)人脸关键点kpu推理
with ScopedTiming("fld_kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸关键点ai2d
fld_ai2d_release()
# (4)获取人脸关键点kpu输出
result = fld_kpu_get_output()
# (5)人脸关键点后处理
result = fld_kpu_post_process(result)
return result
def fld_kpu_deinit(kpu_obj):
# 人脸关键点kpu释放
with ScopedTiming("fld_kpu_deinit",debug_mode > 0):
global fld_ai2d,fld_ai2d_output_tensor
del kpu_obj # 删除kpu_obj变量,释放对它所引用对象的内存引用
del fld_ai2d # 删除fld_ai2d变量,释放对它所引用对象的内存引用
del fld_ai2d_output_tensor # 删除fld_ai2d_output_tensor变量,释放对它所引用对象的内存引用
#********************for media_utils.py********************
global draw_img_ulab,draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# for display,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.2
def display_init():
# 设置使用hdmi进行显示
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets,landmark_preds):
# 在显示器画人脸轮廓
with ScopedTiming("display_draw",debug_mode >0):
global draw_img_ulab,draw_img,osd_img
if dets:
draw_img.clear()
for pred in landmark_preds:
# (1)获取单个人脸框对应的人脸关键点
for sub_part_index in range(len(dict_kp_seq)):
# (2)构建人脸某个区域关键点集
sub_part = dict_kp_seq[sub_part_index]
face_sub_part_point_set = []
for kp_index in range(len(sub_part)):
real_kp_index = sub_part[kp_index]
x, y = pred[real_kp_index * 2], pred[real_kp_index * 2 + 1]
x = int(x * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y = int(y * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
face_sub_part_point_set.append((x, y))
# (3)画人脸不同区域的轮廓
if sub_part_index in (9, 6):
color = np.array(color_list_for_osd_kp[sub_part_index],dtype = np.uint8)
face_sub_part_point_set = np.array(face_sub_part_point_set)
aidemo.polylines(draw_img_ulab, face_sub_part_point_set,False,color,5,8,0)
elif sub_part_index == 4:
color = color_list_for_osd_kp[sub_part_index]
for kp in face_sub_part_point_set:
x,y = kp[0],kp[1]
draw_img.draw_circle(x,y ,2, color, 1)
else:
color = np.array(color_list_for_osd_kp[sub_part_index],dtype = np.uint8)
face_sub_part_point_set = np.array(face_sub_part_point_set)
aidemo.contours(draw_img_ulab, face_sub_part_point_set,-1,color,2,8)
# (4)将轮廓结果拷贝到osd
draw_img.copy_to(osd_img)
# (5)将osd显示到屏幕
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# (1)清空用来画框的图像
draw_img.clear()
# (2)清空osd
draw_img.copy_to(osd_img)
# (3)显示透明图层
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img_ulab,draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框
draw_img_ulab = np.zeros((DISPLAY_HEIGHT,DISPLAY_WIDTH,4),dtype=np.uint8)
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_REF,data = draw_img_ulab)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_landmark_inference():
print("face_landmark_test start")
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸关键点kpu初始化
kpu_face_landmark = fld_kpu_init(fr_kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取人脸检测结果
dets = fd_kpu_run(kpu_face_detect,rgb888p_img)
# (3.2)针对每个人脸框,推理得到对应人脸关键点
landmark_result = []
for det in dets:
ret = fld_kpu_run(kpu_face_landmark,rgb888p_img,det)
landmark_result.append(ret)
# (3.3)将人脸关键点画到屏幕上
display_draw(dets,landmark_result)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
#with ScopedTiming("gc collect", debug_mode > 0):
#gc.collect()
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fld_kpu_deinit(kpu_face_landmark)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_landmark_test, buffer_deinit failed")
return ret
print("face_landmark_test end")
return 0
if __name__ == '__main__':
face_landmark_inference()
2. 人脸识别#
人脸注册:
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import os #操作系统接口模块
import math #数学模块
#********************for config.py********************
# kmodel输入shape
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸识别kmodel输入shape
fr_kmodel_input_shape = (1,3,112,112)
# ai原图padding
rgb_mean = [104,117,123]
#kmodel相关参数设置
#人脸检测
confidence_threshold = 0.5 #人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
#人脸识别
max_register_face = 100 #数据库最多人脸个数
feature_num = 128 #人脸识别特征维度
# 文件配置
# 人脸检测kmodel
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸识别kmodel
fr_kmodel_file = root_dir + 'kmodel/face_recognition.kmodel'
# 人脸检测anchor
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 人脸注册数据库
database_dir = root_dir + 'utils/db/'
# 人脸注册数据库原图
database_img_dir = root_dir + 'utils/db_img/'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu实例
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fr_ai2d: 人脸识别ai2d实例
# fr_ai2d_input_tensor: 人脸识别ai2d输入
# fr_ai2d_output_tensor: 人脸识别ai2d输入
# fr_ai2d_builder: 根据人脸识别ai2d参数,构建的人脸识别ai2d_builder对象
global fr_ai2d,fr_ai2d_input_tensor,fr_ai2d_output_tensor,fr_ai2d_builder
global valid_register_face #数据库中有效人脸个数
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param(rgb888p_img):
# 右padding或下padding,获取padding参数
with ScopedTiming("get_pad_one_side_param", debug_mode > 1):
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / rgb888p_img.shape[3]
ratio_h = dst_h / rgb888p_img.shape[2]
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * rgb888p_img.shape[3])
new_h = (int)(ratio * rgb888p_img.shape[2])
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
#(2)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
fd_ai2d_input_tensor = nn.from_numpy(rgb888p_img)
# (2)根据新的图像设置新的人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(rgb888p_img), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# (3)根据新的人脸检测ai2d参数,构建人脸检测ai2d_builder
fd_ai2d_builder = fd_ai2d.build(rgb888p_img.shape, fd_kmodel_input_shape)
# (4)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d部分资源
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_builder
del fd_ai2d_input_tensor
del fd_ai2d_builder
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[rgb888p_img.shape[3],rgb888p_img.shape[2]],results)
# (6)返回人脸关键点
if len(post_ret)==0:
return post_ret
else:
return post_ret[0],post_ret[1] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
# 标准5官
umeyama_args_112 = [
38.2946 , 51.6963 ,
73.5318 , 51.5014 ,
56.0252 , 71.7366 ,
41.5493 , 92.3655 ,
70.7299 , 92.2041
]
def svd22(a):
# svd
s = [0.0, 0.0]
u = [0.0, 0.0, 0.0, 0.0]
v = [0.0, 0.0, 0.0, 0.0]
s[0] = (math.sqrt((a[0] - a[3]) ** 2 + (a[1] + a[2]) ** 2) + math.sqrt((a[0] + a[3]) ** 2 + (a[1] - a[2]) ** 2)) / 2
s[1] = abs(s[0] - math.sqrt((a[0] - a[3]) ** 2 + (a[1] + a[2]) ** 2))
v[2] = math.sin((math.atan2(2 * (a[0] * a[1] + a[2] * a[3]), a[0] ** 2 - a[1] ** 2 + a[2] ** 2 - a[3] ** 2)) / 2) if \
s[0] > s[1] else 0
v[0] = math.sqrt(1 - v[2] ** 2)
v[1] = -v[2]
v[3] = v[0]
u[0] = -(a[0] * v[0] + a[1] * v[2]) / s[0] if s[0] != 0 else 1
u[2] = -(a[2] * v[0] + a[3] * v[2]) / s[0] if s[0] != 0 else 0
u[1] = (a[0] * v[1] + a[1] * v[3]) / s[1] if s[1] != 0 else -u[2]
u[3] = (a[2] * v[1] + a[3] * v[3]) / s[1] if s[1] != 0 else u[0]
v[0] = -v[0]
v[2] = -v[2]
return u, s, v
def image_umeyama_112(src):
# 使用Umeyama算法计算仿射变换矩阵
SRC_NUM = 5
SRC_DIM = 2
src_mean = [0.0, 0.0]
dst_mean = [0.0, 0.0]
for i in range(0,SRC_NUM * 2,2):
src_mean[0] += src[i]
src_mean[1] += src[i + 1]
dst_mean[0] += umeyama_args_112[i]
dst_mean[1] += umeyama_args_112[i + 1]
src_mean[0] /= SRC_NUM
src_mean[1] /= SRC_NUM
dst_mean[0] /= SRC_NUM
dst_mean[1] /= SRC_NUM
src_demean = [[0.0, 0.0] for _ in range(SRC_NUM)]
dst_demean = [[0.0, 0.0] for _ in range(SRC_NUM)]
for i in range(SRC_NUM):
src_demean[i][0] = src[2 * i] - src_mean[0]
src_demean[i][1] = src[2 * i + 1] - src_mean[1]
dst_demean[i][0] = umeyama_args_112[2 * i] - dst_mean[0]
dst_demean[i][1] = umeyama_args_112[2 * i + 1] - dst_mean[1]
A = [[0.0, 0.0], [0.0, 0.0]]
for i in range(SRC_DIM):
for k in range(SRC_DIM):
for j in range(SRC_NUM):
A[i][k] += dst_demean[j][i] * src_demean[j][k]
A[i][k] /= SRC_NUM
T = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
U, S, V = svd22([A[0][0], A[0][1], A[1][0], A[1][1]])
T[0][0] = U[0] * V[0] + U[1] * V[2]
T[0][1] = U[0] * V[1] + U[1] * V[3]
T[1][0] = U[2] * V[0] + U[3] * V[2]
T[1][1] = U[2] * V[1] + U[3] * V[3]
scale = 1.0
src_demean_mean = [0.0, 0.0]
src_demean_var = [0.0, 0.0]
for i in range(SRC_NUM):
src_demean_mean[0] += src_demean[i][0]
src_demean_mean[1] += src_demean[i][1]
src_demean_mean[0] /= SRC_NUM
src_demean_mean[1] /= SRC_NUM
for i in range(SRC_NUM):
src_demean_var[0] += (src_demean_mean[0] - src_demean[i][0]) * (src_demean_mean[0] - src_demean[i][0])
src_demean_var[1] += (src_demean_mean[1] - src_demean[i][1]) * (src_demean_mean[1] - src_demean[i][1])
src_demean_var[0] /= SRC_NUM
src_demean_var[1] /= SRC_NUM
scale = 1.0 / (src_demean_var[0] + src_demean_var[1]) * (S[0] + S[1])
T[0][2] = dst_mean[0] - scale * (T[0][0] * src_mean[0] + T[0][1] * src_mean[1])
T[1][2] = dst_mean[1] - scale * (T[1][0] * src_mean[0] + T[1][1] * src_mean[1])
T[0][0] *= scale
T[0][1] *= scale
T[1][0] *= scale
T[1][1] *= scale
return T
def get_affine_matrix(sparse_points):
# 获取affine变换矩阵
with ScopedTiming("get_affine_matrix", debug_mode > 1):
# 使用Umeyama算法计算仿射变换矩阵
matrix_dst = image_umeyama_112(sparse_points)
matrix_dst = [matrix_dst[0][0],matrix_dst[0][1],matrix_dst[0][2],
matrix_dst[1][0],matrix_dst[1][1],matrix_dst[1][2]]
return matrix_dst
def fr_ai2d_init():
with ScopedTiming("fr_ai2d_init",debug_mode > 0):
# (1)人脸识别ai2d初始化
global fr_ai2d
fr_ai2d = nn.ai2d()
# (2)人脸识别ai2d_output_tensor初始化,用于存放ai2d输出
global fr_ai2d_output_tensor
data = np.ones(fr_kmodel_input_shape, dtype=np.uint8)
fr_ai2d_output_tensor = nn.from_numpy(data)
def fr_ai2d_run(rgb888p_img,sparse_points):
# 人脸识别ai2d推理
with ScopedTiming("fr_ai2d_run",debug_mode > 0):
global fr_ai2d,fr_ai2d_input_tensor,fr_ai2d_output_tensor
#(1)根据原图创建人脸识别ai2d_input_tensor对象
fr_ai2d_input_tensor = nn.from_numpy(rgb888p_img)
#(2)根据新的人脸关键点设置新的人脸识别ai2d参数
fr_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
affine_matrix = get_affine_matrix(sparse_points)
fr_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,affine_matrix)
global fr_ai2d_builder
# (3)根据新的人脸识别ai2d参数,构建识别ai2d_builder
fr_ai2d_builder = fr_ai2d.build(rgb888p_img.shape, fr_kmodel_input_shape)
# (4)推理人脸识别ai2d,将预处理的结果保存到fr_ai2d_output_tensor
fr_ai2d_builder.run(fr_ai2d_input_tensor, fr_ai2d_output_tensor)
def fr_ai2d_release():
# 释放人脸识别ai2d_input_tensor、ai2d_builder
with ScopedTiming("fr_ai2d_release",debug_mode > 0):
global fr_ai2d_input_tensor,fr_ai2d_builder
del fr_ai2d_input_tensor
del fr_ai2d_builder
def fr_kpu_init(kmodel_file):
# 人脸识别kpu初始化
with ScopedTiming("fr_kpu_init",debug_mode > 0):
# 初始化人脸识别kpu对象
kpu_obj = nn.kpu()
# 加载人脸识别kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸识别ai2d
fr_ai2d_init()
return kpu_obj
def fr_kpu_pre_process(rgb888p_img,sparse_points):
# 人脸识别kpu预处理
# 人脸识别ai2d推理,根据关键点对原图进行预处理
fr_ai2d_run(rgb888p_img,sparse_points)
with ScopedTiming("fr_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fr_ai2d_output_tensor
# 将人脸识别ai2d输出设置为人脸识别kpu输入
current_kmodel_obj.set_input_tensor(0, fr_ai2d_output_tensor)
#ai2d_out_data = fr_ai2d_output_tensor.to_numpy()
#print('ai2d_out_data.shape:',ai2d_out_data.shape)
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
def fr_kpu_get_output():
# 获取人脸识别kpu输出
with ScopedTiming("fr_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result[0]
def fr_kpu_run(kpu_obj,rgb888p_img,sparse_points):
# 人脸识别kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)人脸识别kpu预处理,设置kpu输入
fr_kpu_pre_process(rgb888p_img,sparse_points)
# (2)人脸识别kpu推理
with ScopedTiming("fr kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸识别ai2d
fr_ai2d_release()
# (4)获取人脸识别kpu输出
results = fr_kpu_get_output()
return results
def fr_kpu_deinit(kpu_obj):
# 人脸识别kpu相关资源释放
with ScopedTiming("fr_kpu_deinit",debug_mode > 0):
global fr_ai2d
del kpu_obj
del fr_ai2d
#********************for face_detect.py********************
def image2rgb888array(img): #4维
# 将Image转换为rgb888格式
with ScopedTiming("fr_kpu_deinit",debug_mode > 0):
img_data_rgb888=img.to_rgb888()
# hwc,rgb888
img_hwc=img_data_rgb888.to_numpy_ref()
shape=img_hwc.shape
img_tmp = img_hwc.reshape((shape[0] * shape[1], shape[2]))
img_tmp_trans = img_tmp.transpose()
img_res=img_tmp_trans.copy()
# chw,rgb888
img_return=img_res.reshape((1,shape[2],shape[0],shape[1]))
return img_return
def face_registration_inference():
print("face_registration_test start")
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸识别kpu初始化
kpu_face_reg = fr_kpu_init(fr_kmodel_file)
try:
# 获取图像列表
img_list = os.listdir(database_img_dir)
for img_file in img_list:
with ScopedTiming("total",1):
# (1)读取一张图像
full_img_file = database_img_dir + img_file
print(full_img_file)
img = image.Image(full_img_file)
rgb888p_img_ndarry = image2rgb888array(img)
#(2)推理得到人脸检测kpu,得到人脸检测框、人脸五点
dets,landms = fd_kpu_run(kpu_face_detect,rgb888p_img_ndarry)
if dets:
if dets.shape[0] == 1:
#(3)若是只检测到一张人脸,则将该人脸注册到数据库
db_i_name = img_file.split('.')[0]
for landm in landms:
reg_result = fr_kpu_run(kpu_face_reg,rgb888p_img_ndarry,landm)
#print('\nwrite bin:',database_dir+'{}.bin'.format(db_i_name))
with open(database_dir+'{}.bin'.format(db_i_name), "wb") as file:
file.write(reg_result.tobytes())
else:
print('Only one person in a picture when you sign up')
else:
print('No person detected')
gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fr_kpu_deinit(kpu_face_reg)
# 垃圾回收
gc.collect()
time.sleep(1)
print("face_registration_test end")
return 0
if __name__ == '__main__':
face_registration_inference()
人脸识别:
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * # 摄像头模块
from media.display import * # 显示模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo # aidemo模块,封装ai demo相关后处理、画图操作
import image # 图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time # 时间统计
import gc # 垃圾回收模块
import os # 操作系统接口模块
import math # 数学模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel输入shape
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸识别kmodel输入shape
fr_kmodel_input_shape = (1,3,112,112)
# ai原图padding
rgb_mean = [104,117,123]
#kmodel相关参数设置
#人脸检测
confidence_threshold = 0.5 #人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
#人脸识别
max_register_face = 100 # 数据库最多人脸个数
feature_num = 128 # 人脸识别特征维度
face_recognition_threshold = 0.75 # 人脸识别阈值
#文件配置
# 人脸检测kmodel
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸识别kmodel
fr_kmodel_file = root_dir + 'kmodel/face_recognition.kmodel'
# 人脸检测anchor
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 人脸数据库
database_dir = root_dir + 'utils/db/'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu实例
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fr_ai2d: 人脸识别ai2d实例
# fr_ai2d_input_tensor: 人脸识别ai2d输入
# fr_ai2d_output_tensor: 人脸识别ai2d输入
# fr_ai2d_builder: 根据人脸识别ai2d参数,构建的人脸识别ai2d_builder对象
global fr_ai2d,fr_ai2d_input_tensor,fr_ai2d_output_tensor,fr_ai2d_builder
# valid_register_face: 数据库中有效人脸个数
# db_name: 数据库人名列表
# db_data: 数据库特征列表
global valid_register_face,db_name,db_data
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
# (2)设置人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
#(3)人脸检测ai2d_builder,根据人脸检测ai2d参数、输入输出大小创建ai2d_builder对象
global fd_ai2d_builder
fd_ai2d_builder = fd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fd_kmodel_input_shape)
#(4)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fd_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d_input_tensor
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor
del fd_ai2d_input_tensor
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret,post_ret
else:
return post_ret[0],post_ret[1] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
##for database
def database_init():
# 数据初始化,构建数据库人名列表和数据库特征列表
with ScopedTiming("database_init", debug_mode > 1):
global valid_register_face,db_name,db_data
valid_register_face = 0
db_name = []
db_data = []
db_file_list = os.listdir(database_dir)
for db_file in db_file_list:
if not db_file.endswith('.bin'):
continue
if valid_register_face >= max_register_face:
break
valid_index = valid_register_face
full_db_file = database_dir + db_file
with open(full_db_file, 'rb') as f:
data = f.read()
feature = np.frombuffer(data, dtype=np.float)
db_data.append(feature)
name = db_file.split('.')[0]
db_name.append(name)
valid_register_face += 1
def database_reset():
# 数据库清空
with ScopedTiming("database_reset", debug_mode > 1):
global valid_register_face,db_name,db_data
print("database clearing...")
db_name = []
db_data = []
valid_register_face = 0
print("database clear Done!")
def database_search(feature):
# 数据库查询
with ScopedTiming("database_search", debug_mode > 1):
global valid_register_face,db_name,db_data
v_id = -1
v_score_max = 0.0
# 将当前人脸特征归一化
feature /= np.linalg.norm(feature)
# 遍历当前人脸数据库,统计最高得分
for i in range(valid_register_face):
db_feature = db_data[i]
db_feature /= np.linalg.norm(db_feature)
# 计算数据库特征与当前人脸特征相似度
v_score = np.dot(feature, db_feature)/2 + 0.5
if v_score > v_score_max:
v_score_max = v_score
v_id = i
if v_id == -1:
# 数据库中无人脸
return 'unknown'
elif v_score_max < face_recognition_threshold:
# 小于人脸识别阈值,未识别
print('v_score_max:',v_score_max)
return 'unknown'
else:
# 识别成功
result = 'name: {}, score:{}'.format(db_name[v_id],v_score_max)
return result
# 标准5官
umeyama_args_112 = [
38.2946 , 51.6963 ,
73.5318 , 51.5014 ,
56.0252 , 71.7366 ,
41.5493 , 92.3655 ,
70.7299 , 92.2041
]
def svd22(a):
# svd
s = [0.0, 0.0]
u = [0.0, 0.0, 0.0, 0.0]
v = [0.0, 0.0, 0.0, 0.0]
s[0] = (math.sqrt((a[0] - a[3]) ** 2 + (a[1] + a[2]) ** 2) + math.sqrt((a[0] + a[3]) ** 2 + (a[1] - a[2]) ** 2)) / 2
s[1] = abs(s[0] - math.sqrt((a[0] - a[3]) ** 2 + (a[1] + a[2]) ** 2))
v[2] = math.sin((math.atan2(2 * (a[0] * a[1] + a[2] * a[3]), a[0] ** 2 - a[1] ** 2 + a[2] ** 2 - a[3] ** 2)) / 2) if \
s[0] > s[1] else 0
v[0] = math.sqrt(1 - v[2] ** 2)
v[1] = -v[2]
v[3] = v[0]
u[0] = -(a[0] * v[0] + a[1] * v[2]) / s[0] if s[0] != 0 else 1
u[2] = -(a[2] * v[0] + a[3] * v[2]) / s[0] if s[0] != 0 else 0
u[1] = (a[0] * v[1] + a[1] * v[3]) / s[1] if s[1] != 0 else -u[2]
u[3] = (a[2] * v[1] + a[3] * v[3]) / s[1] if s[1] != 0 else u[0]
v[0] = -v[0]
v[2] = -v[2]
return u, s, v
def image_umeyama_112(src):
# 使用Umeyama算法计算仿射变换矩阵
SRC_NUM = 5
SRC_DIM = 2
src_mean = [0.0, 0.0]
dst_mean = [0.0, 0.0]
for i in range(0,SRC_NUM * 2,2):
src_mean[0] += src[i]
src_mean[1] += src[i + 1]
dst_mean[0] += umeyama_args_112[i]
dst_mean[1] += umeyama_args_112[i + 1]
src_mean[0] /= SRC_NUM
src_mean[1] /= SRC_NUM
dst_mean[0] /= SRC_NUM
dst_mean[1] /= SRC_NUM
src_demean = [[0.0, 0.0] for _ in range(SRC_NUM)]
dst_demean = [[0.0, 0.0] for _ in range(SRC_NUM)]
for i in range(SRC_NUM):
src_demean[i][0] = src[2 * i] - src_mean[0]
src_demean[i][1] = src[2 * i + 1] - src_mean[1]
dst_demean[i][0] = umeyama_args_112[2 * i] - dst_mean[0]
dst_demean[i][1] = umeyama_args_112[2 * i + 1] - dst_mean[1]
A = [[0.0, 0.0], [0.0, 0.0]]
for i in range(SRC_DIM):
for k in range(SRC_DIM):
for j in range(SRC_NUM):
A[i][k] += dst_demean[j][i] * src_demean[j][k]
A[i][k] /= SRC_NUM
T = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
U, S, V = svd22([A[0][0], A[0][1], A[1][0], A[1][1]])
T[0][0] = U[0] * V[0] + U[1] * V[2]
T[0][1] = U[0] * V[1] + U[1] * V[3]
T[1][0] = U[2] * V[0] + U[3] * V[2]
T[1][1] = U[2] * V[1] + U[3] * V[3]
scale = 1.0
src_demean_mean = [0.0, 0.0]
src_demean_var = [0.0, 0.0]
for i in range(SRC_NUM):
src_demean_mean[0] += src_demean[i][0]
src_demean_mean[1] += src_demean[i][1]
src_demean_mean[0] /= SRC_NUM
src_demean_mean[1] /= SRC_NUM
for i in range(SRC_NUM):
src_demean_var[0] += (src_demean_mean[0] - src_demean[i][0]) * (src_demean_mean[0] - src_demean[i][0])
src_demean_var[1] += (src_demean_mean[1] - src_demean[i][1]) * (src_demean_mean[1] - src_demean[i][1])
src_demean_var[0] /= SRC_NUM
src_demean_var[1] /= SRC_NUM
scale = 1.0 / (src_demean_var[0] + src_demean_var[1]) * (S[0] + S[1])
T[0][2] = dst_mean[0] - scale * (T[0][0] * src_mean[0] + T[0][1] * src_mean[1])
T[1][2] = dst_mean[1] - scale * (T[1][0] * src_mean[0] + T[1][1] * src_mean[1])
T[0][0] *= scale
T[0][1] *= scale
T[1][0] *= scale
T[1][1] *= scale
return T
def get_affine_matrix(sparse_points):
# 获取放射变换矩阵
with ScopedTiming("get_affine_matrix", debug_mode > 1):
# 使用Umeyama算法计算仿射变换矩阵
matrix_dst = image_umeyama_112(sparse_points)
matrix_dst = [matrix_dst[0][0],matrix_dst[0][1],matrix_dst[0][2],
matrix_dst[1][0],matrix_dst[1][1],matrix_dst[1][2]]
return matrix_dst
def fr_ai2d_init():
with ScopedTiming("fr_ai2d_init",debug_mode > 0):
# (1)人脸识别ai2d初始化
global fr_ai2d
fr_ai2d = nn.ai2d()
# (2)人脸识别ai2d_output_tensor初始化,用于存放ai2d输出
global fr_ai2d_output_tensor
data = np.ones(fr_kmodel_input_shape, dtype=np.uint8)
fr_ai2d_output_tensor = nn.from_numpy(data)
def fr_ai2d_run(rgb888p_img,sparse_points):
# 人脸识别ai2d推理
with ScopedTiming("fr_ai2d_run",debug_mode > 0):
global fr_ai2d,fr_ai2d_input_tensor,fr_ai2d_output_tensor
#(1)根据原图创建人脸识别ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fr_ai2d_input_tensor = nn.from_numpy(ai2d_input)
#(2)根据新的人脸关键点设置新的人脸识别ai2d参数
fr_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
affine_matrix = get_affine_matrix(sparse_points)
fr_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,affine_matrix)
global fr_ai2d_builder
# (3)根据新的人脸识别ai2d参数,构建识别ai2d_builder
fr_ai2d_builder = fr_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fr_kmodel_input_shape)
# (4)推理人脸识别ai2d,将预处理的结果保存到fr_ai2d_output_tensor
fr_ai2d_builder.run(fr_ai2d_input_tensor, fr_ai2d_output_tensor)
def fr_ai2d_release():
# 释放人脸识别ai2d_input_tensor、ai2d_builder
with ScopedTiming("fr_ai2d_release",debug_mode > 0):
global fr_ai2d_input_tensor,fr_ai2d_builder
del fr_ai2d_input_tensor
del fr_ai2d_builder
def fr_kpu_init(kmodel_file):
# 人脸识别kpu初始化
with ScopedTiming("fr_kpu_init",debug_mode > 0):
# 初始化人脸识别kpu对象
kpu_obj = nn.kpu()
# 加载人脸识别kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸识别ai2d
fr_ai2d_init()
# 数据库初始化
database_init()
return kpu_obj
def fr_kpu_pre_process(rgb888p_img,sparse_points):
# 人脸识别kpu预处理
# 人脸识别ai2d推理,根据关键点对原图进行预处理
fr_ai2d_run(rgb888p_img,sparse_points)
with ScopedTiming("fr_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fr_ai2d_output_tensor
# 将人脸识别ai2d输出设置为人脸识别kpu输入
current_kmodel_obj.set_input_tensor(0, fr_ai2d_output_tensor)
#ai2d_out_data = fr_ai2d_output_tensor.to_numpy()
#print('ai2d_out_data.shape:',ai2d_out_data.shape)
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
def fr_kpu_get_output():
# 获取人脸识别kpu输出
with ScopedTiming("fr_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result[0]
def fr_kpu_run(kpu_obj,rgb888p_img,sparse_points):
# 人脸识别kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)人脸识别kpu预处理,设置kpu输入
fr_kpu_pre_process(rgb888p_img,sparse_points)
# (2)人脸识别kpu推理
with ScopedTiming("fr kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸识别ai2d
fr_ai2d_release()
# (4)获取人脸识别kpu输出
results = fr_kpu_get_output()
# (5)在数据库中查找当前人脸特征
recg_result = database_search(results)
return recg_result
def fr_kpu_deinit(kpu_obj):
# 人脸识别kpu相关资源释放
with ScopedTiming("fr_kpu_deinit",debug_mode > 0):
global fr_ai2d
del kpu_obj
del fr_ai2d
#********************for media_utils.py********************
global draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# for display,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.2
def display_init():
# hdmi显示初始化
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets,recg_results):
# 在显示器上写人脸识别结果
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets:
draw_img.clear()
for i,det in enumerate(dets):
# (1)画人脸框
x1, y1, w, h = map(lambda x: int(round(x, 0)), det[:4])
x1 = x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
y1 = y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
w = w * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = h * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
draw_img.draw_rectangle(x1,y1, w, h, color=(255,0, 255, 0))
# (2)写人脸识别结果
recg_text = recg_results[i]
draw_img.draw_string(x1,y1,recg_text,color=(255,0, 0, 255),scale=4)
# (3)将画图结果拷贝到osd
draw_img.copy_to(osd_img)
# (4)将osd显示到屏幕
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# (1)清空用来画框的图像
draw_img.clear()
# (2)清空osd
draw_img.copy_to(osd_img)
# (3)显示透明图层
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_BGR_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_recognition_inference():
print("face_recognition_test start")
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸关键点kpu初始化
kpu_face_recg = fr_kpu_init(fr_kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取人脸检测结果
dets,landms = fd_kpu_run(kpu_face_detect,rgb888p_img)
recg_result = []
for landm in landms:
# (3.2)针对每个人脸五官点,推理得到人脸特征,并计算特征在数据库中相似度
ret = fr_kpu_run(kpu_face_recg,rgb888p_img,landm)
recg_result.append(ret)
# (3.3)将识别结果画到显示器上
display_draw(dets,recg_result)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
gc.collect()
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fr_kpu_deinit(kpu_face_recg)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_recognition_test, buffer_deinit failed")
return ret
print("face_recognition_test end")
return 0
if __name__ == '__main__':
face_recognition_inference()
3.人脸姿态角#
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * # 摄像头模块
from media.display import * # 显示模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo # aidemo模块,封装ai demo相关后处理、画图操作
import image # 图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time # 时间统计
import gc # 垃圾回收模块
import os # 操作系统接口模块
import math # 数学模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel参数设置
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸姿态估计kmodel输入shape
fp_kmodel_input_shape = (1,3,120,120)
# ai原图padding
rgb_mean = [104,117,123]
#人脸检测kmodel其它参数设置
confidence_threshold = 0.5 # 人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
# 文件配置
# 人脸检测kmodel文件配置
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸姿态估计kmodel文件配置
fp_kmodel_file = root_dir + 'kmodel/face_pose.kmodel'
# anchor文件配置
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu对象
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fld_ai2d: 人脸姿态估计ai2d实例
# fld_ai2d_input_tensor: 人脸姿态估计ai2d输入
# fld_ai2d_output_tensor:人脸姿态估计ai2d输入
# fld_ai2d_builder: 根据人脸姿态估计ai2d参数,构建的人脸姿态估计ai2d_builder对象
global fp_ai2d,fp_ai2d_input_tensor,fp_ai2d_output_tensor,fp_ai2d_builder
global matrix_dst #人脸仿射变换矩阵
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
# (2)设置人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
#(3)人脸检测ai2d_builder,根据人脸检测ai2d参数、输入输出大小创建ai2d_builder对象
global fd_ai2d_builder
fd_ai2d_builder = fd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fd_kmodel_input_shape)
#(4)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fd_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d_input_tensor
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor
del fd_ai2d_input_tensor
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
def get_affine_matrix(bbox):
# 获取仿射矩阵,用于将边界框映射到模型输入空间
with ScopedTiming("get_affine_matrix", debug_mode > 1):
# 设置缩放因子
factor = 2.7
# 从边界框提取坐标和尺寸
x1, y1, w, h = map(lambda x: int(round(x, 0)), bbox[:4])
# 模型输入大小
edge_size = fp_kmodel_input_shape[2]
# 平移距离,使得模型输入空间的中心对准原点
trans_distance = edge_size / 2.0
# 计算边界框中心点的坐标
center_x = x1 + w / 2.0
center_y = y1 + h / 2.0
# 计算最大边长
maximum_edge = factor * (h if h > w else w)
# 计算缩放比例
scale = edge_size * 2.0 / maximum_edge
# 计算平移参数
cx = trans_distance - scale * center_x
cy = trans_distance - scale * center_y
# 创建仿射矩阵
affine_matrix = [scale, 0, cx, 0, scale, cy]
return affine_matrix
def build_projection_matrix(det):
x1, y1, w, h = map(lambda x: int(round(x, 0)), det[:4])
# 计算边界框中心坐标
center_x = x1 + w / 2.0
center_y = y1 + h / 2.0
# 定义后部(rear)和前部(front)的尺寸和深度
rear_width = 0.5 * w
rear_height = 0.5 * h
rear_depth = 0
factor = np.sqrt(2.0)
front_width = factor * rear_width
front_height = factor * rear_height
front_depth = factor * rear_width # 使用宽度来计算深度,也可以使用高度,取决于需求
# 定义立方体的顶点坐标
temp = [
[-rear_width, -rear_height, rear_depth],
[-rear_width, rear_height, rear_depth],
[rear_width, rear_height, rear_depth],
[rear_width, -rear_height, rear_depth],
[-front_width, -front_height, front_depth],
[-front_width, front_height, front_depth],
[front_width, front_height, front_depth],
[front_width, -front_height, front_depth]
]
projections = np.array(temp)
# 返回投影矩阵和中心坐标
return projections, (center_x, center_y)
def rotation_matrix_to_euler_angles(R):
# 将旋转矩阵(3x3 矩阵)转换为欧拉角(pitch、yaw、roll)
# 计算 sin(yaw)
sy = np.sqrt(R[0, 0] ** 2 + R[1, 0] ** 2)
if sy < 1e-6:
# 若 sin(yaw) 过小,说明 pitch 接近 ±90 度
pitch = np.arctan2(-R[1, 2], R[1, 1]) * 180 / np.pi
yaw = np.arctan2(-R[2, 0], sy) * 180 / np.pi
roll = 0
else:
# 计算 pitch、yaw、roll 的角度
pitch = np.arctan2(R[2, 1], R[2, 2]) * 180 / np.pi
yaw = np.arctan2(-R[2, 0], sy) * 180 / np.pi
roll = np.arctan2(R[1, 0], R[0, 0]) * 180 / np.pi
return [pitch,yaw,roll]
def get_euler(data):
# 获取旋转矩阵和欧拉角
R = data[:3, :3].copy()
eular = rotation_matrix_to_euler_angles(R)
return R,eular
def fp_ai2d_init():
# 人脸姿态估计ai2d初始化
with ScopedTiming("fp_ai2d_init",debug_mode > 0):
# (1)创建人脸姿态估计ai2d对象
global fp_ai2d
fp_ai2d = nn.ai2d()
# (2)创建人脸姿态估计ai2d_output_tensor对象
global fp_ai2d_output_tensor
data = np.ones(fp_kmodel_input_shape, dtype=np.uint8)
fp_ai2d_output_tensor = nn.from_numpy(data)
def fp_ai2d_run(rgb888p_img,det):
# 人脸姿态估计ai2d推理
with ScopedTiming("fp_ai2d_run",debug_mode > 0):
global fp_ai2d,fp_ai2d_input_tensor,fp_ai2d_output_tensor
#(1)根据原图构建人脸姿态估计ai2d_input_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
fp_ai2d_input_tensor = nn.from_numpy(ai2d_input)
#(2)设置人脸姿态估计ai2d参数
fp_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global matrix_dst
matrix_dst = get_affine_matrix(det)
fp_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,matrix_dst)
# (3)构建人脸姿态估计ai2d_builder
global fp_ai2d_builder
fp_ai2d_builder = fp_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fp_kmodel_input_shape)
# (4)推理人脸姿态估计ai2d,将结果保存到ai2d_output_tensor
fp_ai2d_builder.run(fp_ai2d_input_tensor, fp_ai2d_output_tensor)
def fp_ai2d_release():
# 释放部分人脸姿态估计ai2d资源
with ScopedTiming("fp_ai2d_release",debug_mode > 0):
global fp_ai2d_input_tensor,fp_ai2d_builder
del fp_ai2d_input_tensor
del fp_ai2d_builder
def fp_kpu_init(kmodel_file):
# 初始化人脸姿态估计kpu及ai2d
with ScopedTiming("fp_kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
fp_ai2d_init()
return kpu_obj
def fp_kpu_pre_process(rgb888p_img,det):
# 人脸姿态估计kpu预处理
fp_ai2d_run(rgb888p_img,det)
with ScopedTiming("fp_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fp_ai2d_output_tensor
current_kmodel_obj.set_input_tensor(0, fp_ai2d_output_tensor)
#ai2d_out_data = _ai2d_output_tensor.to_numpy()
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
def fp_kpu_get_output():
# 获取人脸姿态估计kpu输出
with ScopedTiming("fp_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
result = result[0]
del data
return result
def fp_kpu_post_process(pred):
# 人脸姿态估计kpu推理结果后处理
R,eular = get_euler(pred)
return R,eular
def fp_kpu_run(kpu_obj,rgb888p_img,det):
# 人脸姿态估计kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)根据人脸检测框进行人脸姿态估计kpu预处理
fp_kpu_pre_process(rgb888p_img,det)
# (2)人脸姿态估计kpu推理
with ScopedTiming("fp_kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸姿态估计ai2d资源
fp_ai2d_release()
# (4)释放人脸姿态估计kpu推理输出
result = fp_kpu_get_output()
# (5)释放人脸姿态估计后处理
R,eular = fp_kpu_post_process(result)
return R,eular
def fp_kpu_deinit(kpu_obj):
# 释放人脸姿态估计kpu及ai2d资源
with ScopedTiming("fp_kpu_deinit",debug_mode > 0):
global fp_ai2d,fp_ai2d_output_tensor
del kpu_obj
del fp_ai2d
del fp_ai2d_output_tensor
#********************for media_utils.py********************
global draw_img_ulab,draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# for display,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.2
def display_init():
# 设置使用hdmi进行显示
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets,pose_results):
# 在显示器画人脸轮廓
with ScopedTiming("display_draw",debug_mode >0):
global draw_img_ulab,draw_img,osd_img
if dets:
draw_img.clear()
line_color = np.array([255, 0, 0 ,255],dtype = np.uint8) #bgra
for i,det in enumerate(dets):
# (1)获取人脸姿态矩阵和欧拉角
projections,center_point = build_projection_matrix(det)
R,euler = pose_results[i]
# (2)遍历人脸投影矩阵的关键点,进行投影,并将结果画在图像上
first_points = []
second_points = []
for pp in range(8):
sum_x, sum_y = 0.0, 0.0
for cc in range(3):
sum_x += projections[pp][cc] * R[cc][0]
sum_y += projections[pp][cc] * (-R[cc][1])
center_x,center_y = center_point[0],center_point[1]
x = (sum_x + center_x) / OUT_RGB888P_WIDTH * DISPLAY_WIDTH
y = (sum_y + center_y) / OUT_RGB888P_HEIGH * DISPLAY_HEIGHT
x = max(0, min(x, DISPLAY_WIDTH))
y = max(0, min(y, DISPLAY_HEIGHT))
if pp < 4:
first_points.append((x, y))
else:
second_points.append((x, y))
first_points = np.array(first_points,dtype=np.float)
aidemo.polylines(draw_img_ulab,first_points,True,line_color,2,8,0)
second_points = np.array(second_points,dtype=np.float)
aidemo.polylines(draw_img_ulab,second_points,True,line_color,2,8,0)
for ll in range(4):
x0, y0 = int(first_points[ll][0]),int(first_points[ll][1])
x1, y1 = int(second_points[ll][0]),int(second_points[ll][1])
draw_img.draw_line(x0, y0, x1, y1, color = (255, 0, 0 ,255), thickness = 2)
# (3)将绘制好的图像拷贝到显示缓冲区,并在显示器上展示
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# (1)清空用来画框的图像
draw_img.clear()
# (2)清空osd
draw_img.copy_to(osd_img)
# (3)显示透明图层
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img_ulab,draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框,draw_img->draw_img_ulab(两者指向同一块内存)
draw_img_ulab = np.zeros((DISPLAY_HEIGHT,DISPLAY_WIDTH,4),dtype=np.uint8)
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_REF,data = draw_img_ulab)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_pose_inference():
print("face_pose_test start")
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸姿态估计kpu初始化
kpu_face_pose = fp_kpu_init(fp_kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
gc_count = 0
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取人脸检测结果
dets = fd_kpu_run(kpu_face_detect,rgb888p_img)
# (3.2)针对每个人脸框,推理得到对应人脸旋转矩阵、欧拉角
pose_results = []
for det in dets:
R,eular = fp_kpu_run(kpu_face_pose,rgb888p_img,det)
pose_results.append((R,eular))
# (3.3)将人脸姿态估计结果画到显示器上
display_draw(dets,pose_results)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fp_kpu_deinit(kpu_face_pose)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_pose_test, buffer_deinit failed")
return ret
print("face_pose_test end")
return 0
if __name__ == '__main__':
face_pose_inference()
4. 人脸解析#
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * # 摄像头模块
from media.display import * # 显示模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo # aidemo模块,封装ai demo相关后处理、画图操作
import image # 图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time # 时间统计
import gc # 垃圾回收模块
import os # 操作系统接口模块
import math # 数学模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel参数设置
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸解析kmodel输入shape
fp_kmodel_input_shape = (1,3,320,320)
# ai原图padding
rgb_mean = [104,117,123]
#人脸检测kmodel其它参数设置
confidence_threshold = 0.5 # 人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
# 文件配置
# 人脸检测kmodel
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸解析kmodel
fp_kmodel_file = root_dir + 'kmodel/face_parse.kmodel'
# anchor文件
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu对象
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fld_ai2d: 人脸解析ai2d实例
# fld_ai2d_input_tensor: 人脸解析ai2d输入
# fld_ai2d_output_tensor:人脸解析ai2d输入
# fld_ai2d_builder: 根据人脸解析ai2d参数,构建的人脸解析ai2d_builder对象
global fp_ai2d,fp_ai2d_input_tensor,fp_ai2d_output_tensor,fp_ai2d_builder
global matrix_dst #人脸仿射变换矩阵
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
# (2)设置人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
#(3)人脸检测ai2d_builder,根据人脸检测ai2d参数、输入输出大小创建ai2d_builder对象
global fd_ai2d_builder
fd_ai2d_builder = fd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fd_kmodel_input_shape)
#(4)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fd_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d_input_tensor
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor
del fd_ai2d_input_tensor
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
def get_affine_matrix(bbox):
# 获取仿射矩阵,用于将边界框映射到模型输入空间
with ScopedTiming("get_affine_matrix", debug_mode > 1):
# 设置缩放因子
factor = 2.7
# 从边界框提取坐标和尺寸
x1, y1, w, h = map(lambda x: int(round(x, 0)), bbox[:4])
# 模型输入大小
edge_size = fp_kmodel_input_shape[2]
# 平移距离,使得模型输入空间的中心对准原点
trans_distance = edge_size / 2.0
# 计算边界框中心点的坐标
center_x = x1 + w / 2.0
center_y = y1 + h / 2.0
# 计算最大边长
maximum_edge = factor * (h if h > w else w)
# 计算缩放比例
scale = edge_size * 2.0 / maximum_edge
# 计算平移参数
cx = trans_distance - scale * center_x
cy = trans_distance - scale * center_y
# 创建仿射矩阵
affine_matrix = [scale, 0, cx, 0, scale, cy]
return affine_matrix
def fp_ai2d_init():
# 人脸解析ai2d初始化
with ScopedTiming("fp_ai2d_init",debug_mode > 0):
# (1)创建人脸解析ai2d对象
global fp_ai2d
fp_ai2d = nn.ai2d()
# (2)创建人脸解析ai2d_output_tensor对象
global fp_ai2d_output_tensor
data = np.ones(fp_kmodel_input_shape, dtype=np.uint8)
fp_ai2d_output_tensor = nn.from_numpy(data)
def fp_ai2d_run(rgb888p_img,det):
# 人脸解析ai2d推理
with ScopedTiming("fp_ai2d_run",debug_mode > 0):
global fp_ai2d,fp_ai2d_input_tensor,fp_ai2d_output_tensor
#(1)根据原图构建人脸解析ai2d_input_tensor
ai2d_input = rgb888p_img.to_numpy_ref()
fp_ai2d_input_tensor = nn.from_numpy(ai2d_input)
#(2)设置人脸解析ai2d参数
fp_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global matrix_dst
matrix_dst = get_affine_matrix(det)
fp_ai2d.set_affine_param(True,nn.interp_method.cv2_bilinear,0, 0, 127, 1,matrix_dst)
# (3)构建人脸解析ai2d_builder
global fp_ai2d_builder
fp_ai2d_builder = fp_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fp_kmodel_input_shape)
# (4)推理人脸解析ai2d,将结果保存到ai2d_output_tensor
fp_ai2d_builder.run(fp_ai2d_input_tensor, fp_ai2d_output_tensor)
def fp_ai2d_release():
# 释放部分人脸解析ai2d资源
with ScopedTiming("fp_ai2d_release",debug_mode > 0):
global fp_ai2d_input_tensor,fp_ai2d_builder
del fp_ai2d_input_tensor
del fp_ai2d_builder
def fp_kpu_init(kmodel_file):
# 初始化人脸解析kpu及ai2d
with ScopedTiming("fp_kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
fp_ai2d_init()
return kpu_obj
def fp_kpu_pre_process(rgb888p_img,det):
# 人脸解析kpu预处理
fp_ai2d_run(rgb888p_img,det)
with ScopedTiming("fp_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fp_ai2d_output_tensor
current_kmodel_obj.set_input_tensor(0, fp_ai2d_output_tensor)
#ai2d_out_data = fp_ai2d_output_tensor.to_numpy()
#with open("/sdcard/app/ai2d_out.bin", "wb") as file:
#file.write(ai2d_out_data.tobytes())
def fp_kpu_get_output():
# 获取人脸解析kpu输出
with ScopedTiming("fp_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result
def fp_kpu_run(kpu_obj,rgb888p_img,det):
# 人脸解析kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)根据人脸检测框进行人脸解析kpu预处理
fp_kpu_pre_process(rgb888p_img,det)
# (2)人脸解析kpu推理
with ScopedTiming("fp_kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸解析ai2d资源
fp_ai2d_release()
# (4)释放人脸解析kpu输出
result = fp_kpu_get_output()
return result
def fp_kpu_deinit(kpu_obj):
# 释放人脸解析kpu和ai2d资源
with ScopedTiming("fp_kpu_deinit",debug_mode > 0):
global fp_ai2d,fp_ai2d_output_tensor
del kpu_obj
del fp_ai2d
del fp_ai2d_output_tensor
#********************for media_utils.py********************
global draw_img_ulab,draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
#for display
def display_init():
# 设置使用hdmi进行显示
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets,parse_results):
# 在显示器画出人脸解析结果
with ScopedTiming("display_draw",debug_mode >0):
global draw_img_ulab,draw_img,osd_img
if dets:
draw_img.clear()
for i,det in enumerate(dets):
# (1)将人脸检测框画到draw_img
x, y, w, h = map(lambda x: int(round(x, 0)), det[:4])
x = x * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
y = y * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
w = w * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = h * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
draw_img.draw_rectangle(x,y, w, h, color=(255, 255, 0, 255))
# (2)将人脸解析结果画到draw_img(draw_img_ulab和draw_img指同一内存)
aidemo.face_parse_post_process(draw_img_ulab,[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],
[DISPLAY_WIDTH,DISPLAY_HEIGHT],fp_kmodel_input_shape[2],det.tolist(),parse_results[i])
# (3)将绘制好的图像拷贝到显示缓冲区,并在显示器上展示
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# (1)清空用来画框的图像
draw_img.clear()
# (2)清空osd
draw_img.copy_to(osd_img)
# (3)显示透明图层
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img_ulab,draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框,draw_img->draw_img_ulab(两者指向同一块内存)
draw_img_ulab = np.zeros((DISPLAY_HEIGHT,DISPLAY_WIDTH,4),dtype=np.uint8)
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_REF,data = draw_img_ulab)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_parse_inference():
print("face_parse_test start")
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸解析kpu初始化
kpu_face_parse = fp_kpu_init(fp_kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
gc_count = 0
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取人脸检测结果
dets = fd_kpu_run(kpu_face_detect,rgb888p_img)
# (3.2)针对每个人脸框,推理得到对应人脸解析结果
parse_results = []
for det in dets:
parse_ret = fp_kpu_run(kpu_face_parse,rgb888p_img,det)
parse_results.append(parse_ret)
# (3.3)将人脸解析结果画到显示器上
display_draw(dets,parse_results)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fp_kpu_deinit(kpu_face_parse)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_parse_test, buffer_deinit failed")
return ret
print("face_parse_test end")
return 0
if __name__ == '__main__':
face_parse_inference()
5. 车牌识别#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aidemo #aidemo模块,封装ai demo相关后处理、画图操作
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(640, 16)
OUT_RGB888P_HEIGHT = 480
#车牌检测 和 车牌识别 kmodel输入shape
det_kmodel_input_shape = (1,3,640,640)
rec_kmodel_input_shape = (1,1,32,220)
#车牌检测 相关参数设置
obj_thresh = 0.2 #车牌检测分数阈值
nms_thresh = 0.2 #检测框 非极大值抑制 阈值
#文件配置
root_dir = '/sdcard/app/tests/'
det_kmodel_file = root_dir + 'kmodel/LPD_640.kmodel' # 车牌检测 kmodel 文件路径
rec_kmodel_file = root_dir + 'kmodel/licence_reco.kmodel' # 车牌识别 kmodel 文件路径
#dict_rec = ["挂", "使", "领", "澳", "港", "皖", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "京", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂", "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "警", "学", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_", "-"]
dict_rec = ["gua","shi","ling","ao","gang","wan","hu","jin","yu","ji","jin","meng","liao","ji","hei","su","zhe","jing","min","gan","lu","yu","e","xiang","yue","gui","qiong","chuan","gui","yun","zang","shan","gan","qing","ning","xin","jing","xue","0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_", "-"]
dict_size = len(dict_rec)
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global det_current_kmodel_obj,rec_current_kmodel_obj # 定义全局的 kpu 对象
global det_ai2d,det_ai2d_input_tensor,det_ai2d_output_tensor,det_ai2d_builder # 定义车牌检测 ai2d 对象 ,并且定义 ai2d 的输入、输出 以及 builder
global rec_ai2d,rec_ai2d_input_tensor,rec_ai2d_output_tensor,rec_ai2d_builder # 定义车牌识别 ai2d 对象 ,并且定义 ai2d 的输入、输出 以及 builder
# 车牌检测 接收kmodel输出的后处理方法
def det_kpu_post_process(output_data):
with ScopedTiming("det_kpu_post_process", debug_mode > 0):
results = aidemo.licence_det_postprocess(output_data,[OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH],[det_kmodel_input_shape[2],det_kmodel_input_shape[3]],obj_thresh,nms_thresh)
return results
# 车牌识别 接收kmodel输出的后处理方法
def rec_kpu_post_process(output_data):
with ScopedTiming("rec_kpu_post_process", debug_mode > 0):
size = rec_kmodel_input_shape[3] / 4
result = []
for i in range(size):
maxs = float("-inf")
index = -1
for j in range(dict_size):
if (maxs < float(output_data[i * dict_size +j])):
index = j
maxs = output_data[i * dict_size +j]
result.append(index)
result_str = ""
for i in range(size):
if (result[i] >= 0 and result[i] != 0 and not(i > 0 and result[i-1] == result[i])):
result_str += dict_rec[result[i]-1]
return result_str
# 车牌检测 ai2d 初始化
def det_ai2d_init():
with ScopedTiming("det_ai2d_init",debug_mode > 0):
global det_ai2d
det_ai2d = nn.ai2d()
det_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
det_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global det_ai2d_out_tensor
data = np.ones(det_kmodel_input_shape, dtype=np.uint8)
det_ai2d_out_tensor = nn.from_numpy(data)
global det_ai2d_builder
det_ai2d_builder = det_ai2d.build([1,3,OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH], det_kmodel_input_shape)
# 车牌识别 ai2d 初始化
def rec_ai2d_init():
with ScopedTiming("rec_ai2d_init",debug_mode > 0):
global rec_ai2d
rec_ai2d = nn.ai2d()
rec_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global rec_ai2d_out_tensor
data = np.ones(rec_kmodel_input_shape, dtype=np.uint8)
rec_ai2d_out_tensor = nn.from_numpy(data)
rec_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# 车牌检测 ai2d 运行
def det_ai2d_run(rgb888p_img):
with ScopedTiming("det_ai2d_run",debug_mode > 0):
global det_ai2d_input_tensor,det_ai2d_out_tensor,det_ai2d_builder
det_ai2d_input = rgb888p_img.to_numpy_ref()
det_ai2d_input_tensor = nn.from_numpy(det_ai2d_input)
det_ai2d_builder.run(det_ai2d_input_tensor, det_ai2d_out_tensor)
# 车牌识别 ai2d 运行
def rec_ai2d_run(img_array):
with ScopedTiming("rec_ai2d_run",debug_mode > 0):
global rec_ai2d_input_tensor,rec_ai2d_out_tensor,rec_ai2d_builder
rec_ai2d_builder = rec_ai2d.build([1,1,img_array.shape[2],img_array.shape[3]], rec_kmodel_input_shape)
rec_ai2d_input_tensor = nn.from_numpy(img_array)
rec_ai2d_builder.run(rec_ai2d_input_tensor, rec_ai2d_out_tensor)
# 车牌检测 ai2d 释放内存
def det_ai2d_release():
with ScopedTiming("det_ai2d_release",debug_mode > 0):
global det_ai2d_input_tensor
del det_ai2d_input_tensor
# 车牌识别 ai2d 释放内存
def rec_ai2d_release():
with ScopedTiming("rec_ai2d_release",debug_mode > 0):
global rec_ai2d_input_tensor, rec_ai2d_builder
del rec_ai2d_input_tensor
del rec_ai2d_builder
# 车牌检测 kpu 初始化
def det_kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("det_kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
det_ai2d_init()
return kpu_obj
# 车牌识别 kpu 初始化
def rec_kpu_init(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("rec_kpu_init",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
rec_ai2d_init()
return kpu_obj
# 车牌检测 kpu 输入预处理
def det_kpu_pre_process(rgb888p_img):
det_ai2d_run(rgb888p_img)
with ScopedTiming("det_kpu_pre_process",debug_mode > 0):
global det_current_kmodel_obj,det_ai2d_out_tensor
# set kpu input
det_current_kmodel_obj.set_input_tensor(0, det_ai2d_out_tensor)
# 车牌识别 kpu 输入预处理
def rec_kpu_pre_process(img_array):
rec_ai2d_run(img_array)
with ScopedTiming("rec_kpu_pre_process",debug_mode > 0):
global rec_current_kmodel_obj,rec_ai2d_out_tensor
# set kpu input
rec_current_kmodel_obj.set_input_tensor(0, rec_ai2d_out_tensor)
# 车牌识别 抠图
def rec_array_pre_process(rgb888p_img,dets):
with ScopedTiming("rec_array_pre_process",debug_mode > 0):
isp_image = rgb888p_img.to_numpy_ref()
imgs_array_boxes = aidemo.ocr_rec_preprocess(isp_image,[OUT_RGB888P_HEIGHT,OUT_RGB888P_WIDTH],dets)
return imgs_array_boxes
# 车牌检测 获取 kmodel 输出
def det_kpu_get_output():
with ScopedTiming("det_kpu_get_output",debug_mode > 0):
global det_current_kmodel_obj
results = []
for i in range(det_current_kmodel_obj.outputs_size()):
data = det_current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
tmp2 = result.copy()
del data
results.append(tmp2)
return results
# 车牌识别 获取 kmodel 输出
def rec_kpu_get_output():
with ScopedTiming("rec_kpu_get_output",debug_mode > 0):
global rec_current_kmodel_obj
data = rec_current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
result = result.reshape((result.shape[0] * result.shape[1] * result.shape[2]))
tmp = result.copy()
del data
return tmp
# 车牌检测 kpu 运行
def det_kpu_run(kpu_obj,rgb888p_img):
global det_current_kmodel_obj
det_current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
det_kpu_pre_process(rgb888p_img)
# (2) kpu 运行
with ScopedTiming("det_kpu_run",debug_mode > 0):
kpu_obj.run()
# (3) 释放ai2d资源
det_ai2d_release()
# (4) 获取kpu输出
results = det_kpu_get_output()
# (5) kpu结果后处理
dets = det_kpu_post_process(results)
# 返回 车牌检测结果
return dets
# 车牌识别 kpu 运行
def rec_kpu_run(kpu_obj,rgb888p_img,dets):
global rec_current_kmodel_obj
if (len(dets) == 0):
return []
rec_current_kmodel_obj = kpu_obj
# (1) 原始图像抠图,车牌检测结果 points 排序
imgs_array_boxes = rec_array_pre_process(rgb888p_img,dets)
imgs_array = imgs_array_boxes[0]
boxes = imgs_array_boxes[1]
recs = []
for img_array in imgs_array:
# (2) 抠出后的图像 进行预处理,设置模型输入
rec_kpu_pre_process(img_array)
# (3) kpu 运行
with ScopedTiming("rec_kpu_run",debug_mode > 0):
kpu_obj.run()
# (4) 释放ai2d资源
rec_ai2d_release()
# (5) 获取 kpu 输出
result = rec_kpu_get_output()
# (6) kpu 结果后处理
rec = rec_kpu_post_process(result)
recs.append(rec)
# (7) 返回 车牌检测 和 识别结果
return [boxes,recs]
# 车牌检测 kpu 释放内存
def det_kpu_deinit(kpu_obj):
with ScopedTiming("det_kpu_deinit",debug_mode > 0):
global det_ai2d, det_ai2d_builder, det_ai2d_out_tensor
del kpu_obj
del det_ai2d
del det_ai2d_builder
del det_ai2d_out_tensor
# 车牌识别 kpu 释放内存
def rec_kpu_deinit(kpu_obj):
with ScopedTiming("rec_kpu_deinit",debug_mode > 0):
global rec_ai2d, rec_ai2d_out_tensor
del kpu_obj
del rec_ai2d
del rec_ai2d_out_tensor
#media_utils.py
global draw_img,osd_img #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 将所有车牌检测框 和 识别结果绘制到屏幕
def display_draw(dets_recs):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if dets_recs:
dets = dets_recs[0]
recs = dets_recs[1]
draw_img.clear()
point_8 = np.zeros((8),dtype=np.int16)
for det_index in range(len(dets)):
for i in range(4):
x = dets[det_index][i * 2 + 0]/OUT_RGB888P_WIDTH*DISPLAY_WIDTH
y = dets[det_index][i * 2 + 1]/OUT_RGB888P_HEIGHT*DISPLAY_HEIGHT
point_8[i * 2 + 0] = int(x)
point_8[i * 2 + 1] = int(y)
for i in range(4):
draw_img.draw_line(point_8[i * 2 + 0],point_8[i * 2 + 1],point_8[(i+1) % 4 * 2 + 0],point_8[(i+1) % 4 * 2 + 1],color=(255, 0, 255, 0),thickness=2)
draw_img.draw_string( point_8[6], point_8[7] + 20, recs[det_index] , color=(255,255,153,18) , scale=4)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_BGR_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for licence_det_rec.py**********
def licence_det_inference():
print("licence_det start")
kpu_licence_det = det_kpu_init(det_kmodel_file) # 创建车牌检测的 kpu 对象
kpu_licence_rec = rec_kpu_init(rec_kmodel_file) # 创建车牌识别的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("licence_det, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("licence_det, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = det_kpu_run(kpu_licence_det,rgb888p_img) # 执行车牌检测 kpu 运行 以及 后处理过程
dets_recs = rec_kpu_run(kpu_licence_rec,rgb888p_img,dets) # 执行车牌识别 kpu 运行 以及 后处理过程
display_draw(dets_recs) # 将得到的检测结果和识别结果 绘制到display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
det_kpu_deinit(kpu_licence_det) # 释放 车牌检测 kpu
rec_kpu_deinit(kpu_licence_rec) # 释放 车牌识别 kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放 整个media
if ret:
print("licence_det, buffer_deinit failed")
return ret
print("licence_det end")
return 0
if __name__ == '__main__':
licence_det_inference()
6. 石头剪刀布#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
from random import randint #随机整数生成
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aicube #aicube模块,封装ai cube 相关后处理
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
#ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGH = 1080
#手掌检测 和 手掌关键点检测 kmodel输入shape
hd_kmodel_input_shape = (1,3,512,512)
hk_kmodel_input_shape = (1,3,256,256)
#手掌检测 相关参数设置
confidence_threshold = 0.2 #手掌检测 分数阈值
nms_threshold = 0.5 #非极大值抑制 阈值
hd_kmodel_frame_size = [512,512] #手掌检测kmodel输入 w h
hd_frame_size = [OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH] #手掌检测原始输入图像 w h
strides = [8,16,32] #手掌检测模型 下采样输出倍数
num_classes = 1 #检测类别数, 及手掌一种
nms_option = False #控制最大值抑制的方式 False 类内 True 类间
labels = ["hand"] #标签名称
anchors = [26,27,53,52,75,71,80,99,106,82,99,134,140,113,161,172,245,276] #手掌检测模型 锚框
#手掌关键点检测 相关参数
hk_kmodel_frame_size = [256,256] #手掌关键点检测 kmodel 输入 w h
# kmodel 路径
root_dir = '/sdcard/app/tests/'
hd_kmodel_file = root_dir + 'kmodel/hand_det.kmodel' #手掌检测kmodel路径
hk_kmodel_file = root_dir + 'kmodel/handkp_det.kmodel' #手掌关键点kmodel路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
# 猜拳模式 0 玩家稳赢 , 1 玩家必输 , n > 2 多局多胜
guess_mode = 3
# 读取石头剪刀布的bin文件方法
def read_file(file_name):
image_arr = np.fromfile(file_name,dtype=np.uint8)
image_arr = image_arr.reshape((400,400,4))
return image_arr
# 石头剪刀布的 array
five_image = read_file(root_dir + "utils/five.bin")
fist_image = read_file(root_dir + "utils/fist.bin")
shear_image = read_file(root_dir + "utils/shear.bin")
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global hd_ai2d,hd_ai2d_input_tensor,hd_ai2d_output_tensor,hd_ai2d_builder # 定义手掌检测 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
global hk_ai2d,hk_ai2d_input_tensor,hk_ai2d_output_tensor,hk_ai2d_builder # 定义手掌关键点检测 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
global counts_guess, player_win, k230_win, sleep_end, set_stop_id # 定义猜拳游戏的参数:猜拳次数、玩家赢次、k230赢次、是否停顿、是狗暂停
# 手掌检测 ai2d 初始化
def hd_ai2d_init():
with ScopedTiming("hd_ai2d_init",debug_mode > 0):
global hd_ai2d
global hd_ai2d_builder
# 计算padding值
ori_w = OUT_RGB888P_WIDTH
ori_h = OUT_RGB888P_HEIGH
width = hd_kmodel_frame_size[0]
height = hd_kmodel_frame_size[1]
ratiow = float(width) / ori_w
ratioh = float(height) / ori_h
if ratiow < ratioh:
ratio = ratiow
else:
ratio = ratioh
new_w = int(ratio * ori_w)
new_h = int(ratio * ori_h)
dw = float(width - new_w) / 2
dh = float(height - new_h) / 2
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw - 0.1))
# init kpu and load kmodel
hd_ai2d = nn.ai2d()
hd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
hd_ai2d.set_pad_param(True, [0,0,0,0,top,bottom,left,right], 0, [114,114,114])
hd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
hd_ai2d_builder = hd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,height,width])
global hd_ai2d_output_tensor
data = np.ones(hd_kmodel_input_shape, dtype=np.uint8)
hd_ai2d_output_tensor = nn.from_numpy(data)
# 手掌检测 ai2d 运行
def hd_ai2d_run(rgb888p_img):
with ScopedTiming("hd_ai2d_run",debug_mode > 0):
global hd_ai2d_input_tensor,hd_ai2d_output_tensor
hd_ai2d_input = rgb888p_img.to_numpy_ref()
hd_ai2d_input_tensor = nn.from_numpy(hd_ai2d_input)
hd_ai2d_builder.run(hd_ai2d_input_tensor, hd_ai2d_output_tensor)
# 手掌检测 ai2d 释放内存
def hd_ai2d_release():
with ScopedTiming("hd_ai2d_release",debug_mode > 0):
global hd_ai2d_input_tensor
del hd_ai2d_input_tensor
# 手掌检测 kpu 初始化
def hd_kpu_init(hd_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hd_kpu_init",debug_mode > 0):
hd_kpu_obj = nn.kpu()
hd_kpu_obj.load_kmodel(hd_kmodel_file)
hd_ai2d_init()
return hd_kpu_obj
# 手掌检测 kpu 输入预处理
def hd_kpu_pre_process(rgb888p_img):
hd_ai2d_run(rgb888p_img)
with ScopedTiming("hd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hd_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hd_ai2d_output_tensor)
# 手掌检测 kpu 获取 kmodel 输出
def hd_kpu_get_output():
with ScopedTiming("hd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]*result.shape[2]*result.shape[3]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# 手掌检测 kpu 运行
def hd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原始图像预处理,并设置模型输入
hd_kpu_pre_process(rgb888p_img)
# (2) kpu 运行
with ScopedTiming("hd_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3) 释放ai2d资源
hd_ai2d_release()
# (4) 获取kpu输出
results = hd_kpu_get_output()
# (5) kpu结果后处理
dets = aicube.anchorbasedet_post_process( results[0], results[1], results[2], hd_kmodel_frame_size, hd_frame_size, strides, num_classes, confidence_threshold, nms_threshold, anchors, nms_option)
# (6) 返回 手掌检测 结果
return dets
# 手掌检测 kpu 释放内存
def hd_kpu_deinit(kpu_obj):
with ScopedTiming("hd_kpu_deinit",debug_mode > 0):
global hd_ai2d,hd_ai2d_output_tensor
del kpu_obj
del hd_ai2d
del hd_ai2d_output_tensor
# 手掌关键点检测 ai2d 初始化
def hk_ai2d_init():
with ScopedTiming("hk_ai2d_init",debug_mode > 0):
global hk_ai2d
hk_ai2d = nn.ai2d()
hk_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global hk_ai2d_output_tensor
data = np.ones(hk_kmodel_input_shape, dtype=np.uint8)
hk_ai2d_output_tensor = nn.from_numpy(data)
# 手掌关键点检测 ai2d 运行
def hk_ai2d_run(rgb888p_img, x, y, w, h):
with ScopedTiming("hk_ai2d_run",debug_mode > 0):
global hk_ai2d,hk_ai2d_input_tensor,hk_ai2d_output_tensor
hk_ai2d_input = rgb888p_img.to_numpy_ref()
hk_ai2d_input_tensor = nn.from_numpy(hk_ai2d_input)
hk_ai2d.set_crop_param(True, x, y, w, h)
hk_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
global hk_ai2d_builder
hk_ai2d_builder = hk_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,hk_kmodel_frame_size[1],hk_kmodel_frame_size[0]])
hk_ai2d_builder.run(hk_ai2d_input_tensor, hk_ai2d_output_tensor)
# 手掌关键点检测 ai2d 释放内存
def hk_ai2d_release():
with ScopedTiming("hk_ai2d_release",debug_mode > 0):
global hk_ai2d_input_tensor
del hk_ai2d_input_tensor
# 手掌关键点检测 kpu 初始化
def hk_kpu_init(hk_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hk_kpu_init",debug_mode > 0):
hk_kpu_obj = nn.kpu()
hk_kpu_obj.load_kmodel(hk_kmodel_file)
hk_ai2d_init()
return hk_kpu_obj
# 手掌关键点检测 kpu 输入预处理
def hk_kpu_pre_process(rgb888p_img, x, y, w, h):
hk_ai2d_run(rgb888p_img, x, y, w, h)
with ScopedTiming("hk_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hk_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hk_ai2d_output_tensor)
# 手掌关键点检测 kpu 获得 kmodel 输出
def hk_kpu_get_output():
with ScopedTiming("hk_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# 手掌关键点检测 接收kmodel结果的后处理
def hk_kpu_post_process(results, x, y, w, h):
results_show = np.zeros(results.shape,dtype=np.int16)
# results_show = np.zeros(len(results),dtype=np.int16)
results_show[0::2] = results[0::2] * w + x
results_show[1::2] = results[1::2] * h + y
return results_show
# 手掌关键点检测 kpu 运行
def hk_kpu_run(kpu_obj,rgb888p_img, x, y, w, h):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1) 原图预处理,并设置模型输入
hk_kpu_pre_process(rgb888p_img, x, y, w, h)
# (2) kpu 运行
with ScopedTiming("hk_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3) 释放ai2d资源
hk_ai2d_release()
# (4) 获取kpu输出
results = hk_kpu_get_output()
# (5) kpu结果后处理
result = hk_kpu_post_process(results[0],x,y,w,h)
# (6) 返回 关键点检测 结果
return result
# 手掌关键点检测 kpu 释放内存
def hk_kpu_deinit(kpu_obj):
with ScopedTiming("hk_kpu_deinit",debug_mode > 0):
global hk_ai2d,hk_ai2d_output_tensor
del kpu_obj
del hk_ai2d
del hk_ai2d_output_tensor
# 手掌关键点检测 计算角度
def hk_vector_2d_angle(v1,v2):
v1_x = v1[0]
v1_y = v1[1]
v2_x = v2[0]
v2_y = v2[1]
v1_norm = np.sqrt(v1_x * v1_x+ v1_y * v1_y)
v2_norm = np.sqrt(v2_x * v2_x + v2_y * v2_y)
dot_product = v1_x * v2_x + v1_y * v2_y
cos_angle = dot_product/(v1_norm*v2_norm)
angle = np.acos(cos_angle)*180/np.pi
# if (angle>180):
# return 65536
return angle
# 利用手掌关键点检测的结果 判断手掌手势
def hk_gesture(kpu_hand_keypoint_detect,rgb888p_img,det_box):
x1, y1, x2, y2 = det_box[2],det_box[3],det_box[4],det_box[5]
w = int(float(x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
h = int(float(y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x1 = int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y1 = int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x2 = int(x2 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y2 = int(y2 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
if (h<(0.1*DISPLAY_HEIGHT)):
return
if (w<(0.25*DISPLAY_WIDTH) and ((x1<(0.03*DISPLAY_WIDTH)) or (x2>(0.97*DISPLAY_WIDTH)))):
return
if (w<(0.15*DISPLAY_WIDTH) and ((x1<(0.01*DISPLAY_WIDTH)) or (x2>(0.99*DISPLAY_WIDTH)))):
return
length = max(w,h)/2
cx = (x1+x2)/2
cy = (y1+y2)/2
ratio_num = 1.26*length
x1_kp = int(max(0,cx-ratio_num))
y1_kp = int(max(0,cy-ratio_num))
x2_kp = int(min(DISPLAY_WIDTH-1, cx+ratio_num))
y2_kp = int(min(DISPLAY_HEIGHT-1, cy+ratio_num))
w_kp = int(x2_kp - x1_kp + 1)
h_kp = int(y2_kp - y1_kp + 1)
results = hk_kpu_run(kpu_hand_keypoint_detect,rgb888p_img, x1_kp, y1_kp, w_kp, h_kp)
angle_list = []
for i in range(5):
angle = hk_vector_2d_angle([(results[0]-results[i*8+4]), (results[1]-results[i*8+5])],[(results[i*8+6]-results[i*8+8]),(results[i*8+7]-results[i*8+9])])
angle_list.append(angle)
thr_angle = 65.
thr_angle_thumb = 53.
thr_angle_s = 49.
gesture_str = None
if 65535. not in angle_list:
if (angle_list[0]>thr_angle_thumb) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
gesture_str = "fist"
elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]<thr_angle_s):
gesture_str = "five"
elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
gesture_str = "gun"
elif (angle_list[0]<thr_angle_s) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s):
gesture_str = "love"
elif (angle_list[0]>5) and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
gesture_str = "one"
elif (angle_list[0]<thr_angle_s) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s):
gesture_str = "six"
elif (angle_list[0]>thr_angle_thumb) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]>thr_angle):
gesture_str = "three"
elif (angle_list[0]<thr_angle_s) and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
gesture_str = "thumbUp"
elif (angle_list[0]>thr_angle_thumb) and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
gesture_str = "yeah"
return gesture_str
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img, masks
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
masks = np.zeros((OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH,4),dtype=np.uint8)
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888,alloc=image.ALLOC_REF,data=masks)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for finger_guessing.py**********
def finger_guessing_inference():
print("finger_guessing_test start")
kpu_hand_detect = hd_kpu_init(hd_kmodel_file) # 创建手掌检测的 kpu 对象
kpu_hand_keypoint_detect = hk_kpu_init(hk_kmodel_file) # 创建手掌关键点检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("finger_guessing, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0) # 开启 camera
counts_guess = -1 # 猜拳次数 计数
player_win = 0 # 玩家 赢次计数
k230_win = 0 # k230 赢次计数
sleep_end = False # 是否 停顿
set_stop_id = True # 是否 暂停猜拳
LIBRARY = ["fist","yeah","five"] # 猜拳 石头剪刀布 三种方案的dict
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图像
if rgb888p_img == -1:
print("finger_guessing, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
with ScopedTiming("trigger time", debug_mode > 0):
dets = hd_kpu_run(kpu_hand_detect,rgb888p_img) # 执行手掌检测 kpu 运行 以及 后处理过程
gesture = ""
draw_img.clear()
for det_box in dets:
gesture = hk_gesture(kpu_hand_keypoint_detect,rgb888p_img,det_box) # 执行手掌关键点检测 kpu 运行 以及 后处理过程 得到手势类型
if (len(dets) >= 2):
draw_img.draw_string( 300 , 500, "Must have one hand !", color=(255,255,0,0), scale=7)
draw_img.copy_to(osd_img)
elif (guess_mode == 0):
if (gesture == "fist"):
masks[:400,:400,:] = shear_image
elif (gesture == "five"):
masks[:400,:400,:] = fist_image
elif (gesture == "yeah"):
masks[:400,:400,:] = five_image
draw_img.copy_to(osd_img)
elif (guess_mode == 1):
if (gesture == "fist"):
masks[:400,:400,:] = five_image
elif (gesture == "five"):
masks[:400,:400,:] = shear_image
elif (gesture == "yeah"):
masks[:400,:400,:] = fist_image
draw_img.copy_to(osd_img)
else:
if (sleep_end):
time.sleep_ms(2000)
sleep_end = False
if (len(dets) == 0):
set_stop_id = True
if (counts_guess == -1 and gesture != "fist" and gesture != "yeah" and gesture != "five"):
draw_img.draw_string( 400 , 450, "G A M E S T A R T", color=(255,255,0,0), scale=7)
draw_img.draw_string( 400 , 550, " 1 S E T ", color=(255,255,0,0), scale=7)
draw_img.copy_to(osd_img)
elif (counts_guess == guess_mode):
draw_img.clear()
if (k230_win > player_win):
draw_img.draw_string( 400 , 450, "Y O U L O S E", color=(255,255,0,0), scale=7)
elif (k230_win < player_win):
draw_img.draw_string( 400 , 450, "Y O U W I N", color=(255,255,0,0), scale=7)
else:
draw_img.draw_string( 400 , 450, "T I E G A M E", color=(255,255,0,0), scale=7)
draw_img.copy_to(osd_img)
counts_guess = -1
player_win = 0
k230_win = 0
sleep_end = True
else:
if (set_stop_id):
if (counts_guess == -1 and (gesture == "fist" or gesture == "yeah" or gesture == "five")):
counts_guess = 0
if (counts_guess != -1 and (gesture == "fist" or gesture == "yeah" or gesture == "five")):
k230_guess = randint(1,10000) % 3
if (gesture == "fist" and LIBRARY[k230_guess] == "yeah"):
player_win += 1
elif (gesture == "fist" and LIBRARY[k230_guess] == "five"):
k230_win += 1
if (gesture == "yeah" and LIBRARY[k230_guess] == "fist"):
k230_win += 1
elif (gesture == "yeah" and LIBRARY[k230_guess] == "five"):
player_win += 1
if (gesture == "five" and LIBRARY[k230_guess] == "fist"):
player_win += 1
elif (gesture == "five" and LIBRARY[k230_guess] == "yeah"):
k230_win += 1
if (LIBRARY[k230_guess] == "fist"):
masks[:400,:400,:] = fist_image
elif (LIBRARY[k230_guess] == "five"):
masks[:400,:400,:] = five_image
elif (LIBRARY[k230_guess] == "yeah"):
masks[:400,:400,:] = shear_image
counts_guess += 1;
draw_img.draw_string( 400 , 450, " " + str(counts_guess) + " S E T ", color=(255,255,0,0), scale=7)
draw_img.copy_to(osd_img)
set_stop_id = False
sleep_end = True
else:
draw_img.draw_string( 400 , 450, " " + str(counts_guess+1) + " S E T ", color=(255,255,0,0), scale=7)
draw_img.copy_to(osd_img)
else:
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3) # 将得到的图像 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图形
rgb888p_img = None
gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 停止 display
hd_kpu_deinit(kpu_hand_detect) # 释放手掌检测 kpu
hk_kpu_deinit(kpu_hand_keypoint_detect) # 释放手掌关键点检测 kpu
gc.collect()
ret = media_deinit() # 释放 整个 media
if ret:
print("finger_guessing, buffer_deinit failed")
return ret
print("finger_guessing_test end")
return 0
if __name__ == '__main__':
finger_guessing_inference()
7. OCR识别#
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time #时间统计
import gc #垃圾回收模块
import aicube #aicube模块,封装检测分割等任务相关后处理
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
# ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(640, 16)
OUT_RGB888P_HEIGH = 360
#kmodel输入参数设置
kmodel_input_shape_det = (1,3,640,640) # OCR检测模型的kmodel输入分辨率
kmodel_input_shape_rec = (1,3,32,512) # OCR识别模型的kmodel输入分辨率
rgb_mean = [0,0,0] # ai2d padding的值
#检测步骤kmodel相关参数设置
mask_threshold = 0.25 # 二值化mask阈值
box_threshold = 0.3 # 检测框分数阈值
#文件配置
root_dir = '/sdcard/app/tests/'
kmodel_file_det = root_dir + 'kmodel/ocr_det_int16.kmodel' # 检测模型路径
kmodel_file_rec = root_dir + "kmodel/ocr_rec_int16.kmodel" # 识别模型路径
dict_path = root_dir + 'utils/dict.txt' # 调试模式 大于0(调试)、 反之 (不调试)
debug_mode = 0
# OCR字典读取
with open(dict_path, 'r') as file:
line_one = file.read(100000)
line_list = line_one.split("\r\n")
DICT = {num: char.replace("\r", "").replace("\n", "") for num, char in enumerate(line_list)}
# scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
# utils 设定全局变量
global current_kmodel_obj # 设置全局kpu对象
global ai2d_det,ai2d_input_tensor_det,ai2d_output_tensor_det,ai2d_builder_det,ai2d_input_det # 设置检测模型的ai2d对象,并定义ai2d的输入、输出和builder
global ai2d_rec,ai2d_input_tensor_rec,ai2d_output_tensor_rec,ai2d_builder_rec # 设置识别模型的ai2d对象,并定义ai2d的输入、输出和builder
# padding方法,一边padding,右padding或者下padding
def get_pad_one_side_param(out_img_size,input_img_size):
dst_w = out_img_size[0]
dst_h = out_img_size[1]
input_width = input_img_size[0]
input_high = input_img_size[1]
ratio_w = dst_w / input_width
ratio_h = dst_h / input_high
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
new_w = (int)(ratio * input_width)
new_h = (int)(ratio * input_high)
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
# 检测步骤ai2d初始化
def ai2d_init_det():
with ScopedTiming("ai2d_init_det",debug_mode > 0):
global ai2d_det
ai2d_det = nn.ai2d()
ai2d_det.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d_det.set_pad_param(True, get_pad_one_side_param([kmodel_input_shape_det[3],kmodel_input_shape_det[2]], [OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH]), 0, [0, 0, 0])
ai2d_det.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
global ai2d_output_tensor_det
data = np.ones(kmodel_input_shape_det, dtype=np.uint8)
ai2d_output_tensor_det = nn.from_numpy(data)
global ai2d_builder_det
ai2d_builder_det = ai2d_det.build([1, 3, OUT_RGB888P_HEIGH, OUT_RGB888P_WIDTH], [1, 3, kmodel_input_shape_det[2], kmodel_input_shape_det[3]])
# 检测步骤的ai2d 运行,完成ai2d_init_det预设的预处理
def ai2d_run_det(rgb888p_img):
with ScopedTiming("ai2d_run_det",debug_mode > 0):
global ai2d_input_tensor_det,ai2d_builder_det,ai2d_input_det
ai2d_input_det = rgb888p_img.to_numpy_ref()
ai2d_input_tensor_det = nn.from_numpy(ai2d_input_det)
global ai2d_output_tensor_det
ai2d_builder_det.run(ai2d_input_tensor_det, ai2d_output_tensor_det)
# 识别步骤ai2d初始化
def ai2d_init_rec():
with ScopedTiming("ai2d_init_res",debug_mode > 0):
global ai2d_rec,ai2d_output_tensor_rec
ai2d_rec = nn.ai2d()
ai2d_rec.set_dtype(nn.ai2d_format.RGB_packed,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
ai2d_out_data = np.ones((1, 3, kmodel_input_shape_rec[2], kmodel_input_shape_rec[3]), dtype=np.uint8)
ai2d_output_tensor_rec = nn.from_numpy(ai2d_out_data)
# 识别步骤ai2d运行
def ai2d_run_rec(rgb888p_img):
with ScopedTiming("ai2d_run_rec",debug_mode > 0):
global ai2d_rec,ai2d_builder_rec,ai2d_input_tensor_rec,ai2d_output_tensor_rec
ai2d_rec.set_pad_param(True, get_pad_one_side_param([kmodel_input_shape_rec[3],kmodel_input_shape_rec[2]],[rgb888p_img.shape[2],rgb888p_img.shape[1]]), 0, [0, 0, 0])
ai2d_rec.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
ai2d_builder_rec = ai2d_rec.build([rgb888p_img.shape[0], rgb888p_img.shape[1], rgb888p_img.shape[2],rgb888p_img.shape[3]],
[1, 3, kmodel_input_shape_rec[2], kmodel_input_shape_rec[3]])
ai2d_input_tensor_rec = nn.from_numpy(rgb888p_img)
ai2d_builder_rec.run(ai2d_input_tensor_rec, ai2d_output_tensor_rec)
# 检测步骤ai2d释放内存
def ai2d_release_det():
with ScopedTiming("ai2d_release_det",debug_mode > 0):
global ai2d_input_tensor_det
del ai2d_input_tensor_det
# 识别步骤ai2d释放内存
def ai2d_release_rec():
with ScopedTiming("ai2d_release_rec",debug_mode > 0):
global ai2d_input_tensor_rec
del ai2d_input_tensor_rec
# 检测步骤kpu初始化
def kpu_init_det(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init_det",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init_det()
return kpu_obj
# 识别步骤kpu初始化
def kpu_init_rec(kmodel_file):
# init kpu and load kmodel
with ScopedTiming("kpu_init_rec",debug_mode > 0):
kpu_obj = nn.kpu()
kpu_obj.load_kmodel(kmodel_file)
ai2d_init_rec()
return kpu_obj
# 检测步骤预处理,调用ai2d_run_det实现,并将ai2d的输出设置为kmodel的输入
def kpu_pre_process_det(rgb888p_img):
ai2d_run_det(rgb888p_img)
with ScopedTiming("kpu_pre_process_det",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor_det
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor_det)
# 识别步骤预处理,调用ai2d_init_run_rec实现,并将ai2d的输出设置为kmodel的输入
def kpu_pre_process_rec(rgb888p_img):
ai2d_run_rec(rgb888p_img)
with ScopedTiming("kpu_pre_process_rec",debug_mode > 0):
global current_kmodel_obj,ai2d_output_tensor_rec
# set kpu input
current_kmodel_obj.set_input_tensor(0, ai2d_output_tensor_rec)
# 获取kmodel的输出
def kpu_get_output():
with ScopedTiming("kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
# 检测步骤kpu运行
def kpu_run_det(kpu_obj,rgb888p_img):
# kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
#(1)原图像预处理并设置模型输入
kpu_pre_process_det(rgb888p_img)
#(2)kpu推理
with ScopedTiming("kpu_run_det",debug_mode > 0):
# 检测运行
kpu_obj.run()
#(3)检测释放ai2d资源
ai2d_release_det()
#(4)获取检测kpu输出
results = kpu_get_output()
#(5)CHW转HWC
global ai2d_input_det
tmp = (ai2d_input_det.shape[0], ai2d_input_det.shape[1], ai2d_input_det.shape[2])
ai2d_input_det = ai2d_input_det.reshape((ai2d_input_det.shape[0], ai2d_input_det.shape[1] * ai2d_input_det.shape[2]))
ai2d_input_det = ai2d_input_det.transpose()
tmp2 = ai2d_input_det.copy()
tmp2 = tmp2.reshape((tmp[1], tmp[2], tmp[0]))
#(6)后处理,aicube.ocr_post_process接口说明:
# 接口:aicube.ocr_post_process(threshold_map,ai_isp,kmodel_input_shape,isp_shape,mask_threshold,box_threshold);
# 参数说明:
# threshold_map: DBNet模型的输出为(N,kmodel_input_shape_det[2],kmodel_input_shape_det[3],2),两个通道分别为threshold map和segmentation map
# 后处理过程只使用threshold map,因此将results[0][:,:,:,0] reshape成一维传给接口使用。
# ai_isp:后处理还会返回基于原图的检测框裁剪数据,因此要将原图数据reshape为一维传给接口处理。
# kmodel_input_shape:kmodel输入分辨率。
# isp_shape:AI原图分辨率。要将kmodel输出分辨率的检测框坐标映射到原图分辨率上,需要使用这两个分辨率的值。
# mask_threshold:用于二值化图像获得文本区域。
# box_threshold:检测框分数阈值,低于该阈值的检测框不计入结果。
with ScopedTiming("kpu_post",debug_mode > 0):
# 调用aicube模块的ocr_post_process完成ocr检测的后处理
# det_results结构为[[crop_array_nhwc,[p1_x,p1_y,p2_x,p2_y,p3_x,p3_y,p4_x,p4_y]],...]
det_results = aicube.ocr_post_process(results[0][:, :, :, 0].reshape(-1), tmp2.reshape(-1),
[kmodel_input_shape_det[3], kmodel_input_shape_det[2]],
[OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH], mask_threshold, box_threshold)
return det_results
# 识别步骤后处理
def kpu_run_rec(kpu_obj,rgb888p_img):
# kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
#(1)识别预处理并设置模型输入
kpu_pre_process_rec(rgb888p_img)
#(2)kpu推理
with ScopedTiming("kpu_run_rec",debug_mode > 0):
# 识别运行
kpu_obj.run()
#(3)识别释放ai2d资源
ai2d_release_rec()
#(4)获取识别kpu输出
results = kpu_get_output()
#(5)识别后处理,results结构为[(N,MAX_LENGTH,DICT_LENGTH),...],在axis=2维度上取argmax获取当前识别字符在字典中的索引
preds = np.argmax(results[0], axis=2).reshape((-1))
output_txt = ""
for i in range(len(preds)):
# 当前识别字符不是字典的最后一个字符并且和前一个字符不重复(去重),加入识别结果字符串
if preds[i] != (len(DICT) - 1) and (not (i > 0 and preds[i - 1] == preds[i])):
output_txt = output_txt + DICT[preds[i]]
return output_txt
# 释放检测步骤kpu、ai2d以及ai2d相关的tensor
def kpu_deinit_det(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d_det,ai2d_output_tensor_det
del kpu_obj
del ai2d_det
del ai2d_output_tensor_det
# 释放识别步骤kpu
def kpu_deinit_rec(kpu_obj):
with ScopedTiming("kpu_deinit",debug_mode > 0):
global ai2d_rec,ai2d_output_tensor_rec
del kpu_obj
del ai2d_rec
del ai2d_output_tensor_rec
#********************for media_utils.py********************
global draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# display初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# 释放display
def display_deinit():
display.deinit()
# display显示检测识别框
def display_draw(det_results):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if det_results:
draw_img.clear()
# 循环绘制所有检测到的框
for j in det_results:
# 将原图的坐标点转换成显示的坐标点,循环绘制四条直线,得到一个矩形框
for i in range(4):
x1 = j[1][(i * 2)] / OUT_RGB888P_WIDTH * DISPLAY_WIDTH
y1 = j[1][(i * 2 + 1)] / OUT_RGB888P_HEIGH * DISPLAY_HEIGHT
x2 = j[1][((i + 1) * 2) % 8] / OUT_RGB888P_WIDTH * DISPLAY_WIDTH
y2 = j[1][((i + 1) * 2 + 1) % 8] / OUT_RGB888P_HEIGH * DISPLAY_HEIGHT
draw_img.draw_line((int(x1), int(y1), int(x2), int(y2)), color=(255, 0, 0, 255),
thickness=5)
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
draw_img.clear()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
# camera初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# camera获取的通道0图像送display显示
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# camera获取的通道2图像送ai处理
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 启动视频流
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 捕获一帧图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 释放内存
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 停止视频流
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放buffer,销毁link
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
def ocr_rec_inference():
print("ocr_rec_test start")
kpu_ocr_det = kpu_init_det(kmodel_file_det) # 创建OCR检测kpu对象
kpu_ocr_rec = kpu_init_rec(kmodel_file_rec) # 创建OCR识别kpu对象
camera_init(CAM_DEV_ID_0) # camera初始化
display_init() # display初始化
rgb888p_img = None
try:
ret = media_init()
if ret:
print("ocr_rec_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图像
if rgb888p_img == -1:
print("ocr_rec_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
det_results = kpu_run_det(kpu_ocr_det,rgb888p_img) # kpu运行获取OCR检测kmodel的推理输出
ocr_results=""
if det_results:
for j in det_results:
ocr_result = kpu_run_rec(kpu_ocr_rec,j[0]) # j[0]为检测框的裁剪部分,kpu运行获取OCR识别kmodel的推理输出
ocr_results = ocr_results+" ["+ocr_result+"] "
print("\n"+ocr_results)
display_draw(det_results)
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
# gc.collect()
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止camera
display_deinit() # 释放display
kpu_deinit_det(kpu_ocr_det) # 释放OCR检测步骤kpu
kpu_deinit_rec(kpu_ocr_rec) # 释放OCR识别步骤kpu
gc.collect()
time.sleep(1)
ret = media_deinit() # 释放整个media
if ret:
print("ocr_rec_test, buffer_deinit failed")
return ret
print("ocr_rec_test end")
return 0
if __name__ == '__main__':
ocr_rec_inference()
8. 手掌关键点检测#
import aicube #aicube模块,封装检测分割等任务相关后处理
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import time #时间统计
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import gc #垃圾回收模块
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
##ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGH = 1080
#--------for hand detection----------
#kmodel输入shape
hd_kmodel_input_shape = (1,3,512,512) # 手掌检测kmodel输入分辨率
#kmodel相关参数设置
confidence_threshold = 0.2 # 手掌检测阈值,用于过滤roi
nms_threshold = 0.5 # 手掌检测框阈值,用于过滤重复roi
hd_kmodel_frame_size = [512,512] # 手掌检测输入图片尺寸
hd_frame_size = [OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH] # 手掌检测直接输入图片尺寸
strides = [8,16,32] # 输出特征图的尺寸与输入图片尺寸的比
num_classes = 1 # 手掌检测模型输出类别数
nms_option = False # 是否所有检测框一起做NMS,False则按照不同的类分别应用NMS
root_dir = '/sdcard/app/tests/'
hd_kmodel_file = root_dir + "kmodel/hand_det.kmodel" # 手掌检测kmodel文件的路径
anchors = [26,27, 53,52, 75,71, 80,99, 106,82, 99,134, 140,113, 161,172, 245,276] #anchor设置
#--------for hand keypoint detection----------
#kmodel输入shape
hk_kmodel_input_shape = (1,3,256,256) # 手掌关键点检测kmodel输入分辨率
#kmodel相关参数设置
hk_kmodel_frame_size = [256,256] # 手掌关键点检测输入图片尺寸
hk_kmodel_file = root_dir + 'kmodel/handkp_det.kmodel' # 手掌关键点检测kmodel文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global hd_ai2d,hd_ai2d_input_tensor,hd_ai2d_output_tensor,hd_ai2d_builder # 定义手掌检测全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
global hk_ai2d,hk_ai2d_input_tensor,hk_ai2d_output_tensor,hk_ai2d_builder # 定义手掌关键点检测全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
#-------hand detect--------:
# 手掌检测ai2d 初始化
def hd_ai2d_init():
with ScopedTiming("hd_ai2d_init",debug_mode > 0):
global hd_ai2d
global hd_ai2d_builder
global hd_ai2d_output_tensor
# 计算padding值
ori_w = OUT_RGB888P_WIDTH
ori_h = OUT_RGB888P_HEIGH
width = hd_kmodel_frame_size[0]
height = hd_kmodel_frame_size[1]
ratiow = float(width) / ori_w
ratioh = float(height) / ori_h
if ratiow < ratioh:
ratio = ratiow
else:
ratio = ratioh
new_w = int(ratio * ori_w)
new_h = int(ratio * ori_h)
dw = float(width - new_w) / 2
dh = float(height - new_h) / 2
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw - 0.1))
# init kpu and load kmodel
hd_ai2d = nn.ai2d()
hd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
hd_ai2d.set_pad_param(True, [0,0,0,0,top,bottom,left,right], 0, [114,114,114])
hd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
hd_ai2d_builder = hd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,height,width])
data = np.ones(hd_kmodel_input_shape, dtype=np.uint8)
hd_ai2d_output_tensor = nn.from_numpy(data)
# 手掌检测 ai2d 运行
def hd_ai2d_run(rgb888p_img):
with ScopedTiming("hd_ai2d_run",debug_mode > 0):
global hd_ai2d_input_tensor,hd_ai2d_output_tensor
hd_ai2d_input = rgb888p_img.to_numpy_ref()
hd_ai2d_input_tensor = nn.from_numpy(hd_ai2d_input)
hd_ai2d_builder.run(hd_ai2d_input_tensor, hd_ai2d_output_tensor)
# 手掌检测 ai2d 释放内存
def hd_ai2d_release():
with ScopedTiming("hd_ai2d_release",debug_mode > 0):
global hd_ai2d_input_tensor
del hd_ai2d_input_tensor
# 手掌检测 kpu 初始化
def hd_kpu_init(hd_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hd_kpu_init",debug_mode > 0):
hd_kpu_obj = nn.kpu()
hd_kpu_obj.load_kmodel(hd_kmodel_file)
hd_ai2d_init()
return hd_kpu_obj
# 手掌检测 kpu 输入预处理
def hd_kpu_pre_process(rgb888p_img):
hd_ai2d_run(rgb888p_img)
with ScopedTiming("hd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hd_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hd_ai2d_output_tensor)
# 手掌检测 kpu 获得 kmodel 输出
def hd_kpu_get_output():
with ScopedTiming("hd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]*result.shape[2]*result.shape[3]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# 手掌检测 kpu 运行
def hd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
hd_kpu_pre_process(rgb888p_img)
# (2)手掌检测 kpu 运行
with ScopedTiming("hd_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放手掌检测 ai2d 资源
hd_ai2d_release()
# (4)获取手掌检测 kpu 输出
results = hd_kpu_get_output()
# (5)手掌检测 kpu 结果后处理
dets = aicube.anchorbasedet_post_process( results[0], results[1], results[2], hd_kmodel_frame_size, hd_frame_size, strides, num_classes, confidence_threshold, nms_threshold, anchors, nms_option)
# (6)返回手掌检测结果
return dets
# 手掌检测 kpu 释放内存
def hd_kpu_deinit(kpu_obj):
with ScopedTiming("hd_kpu_deinit",debug_mode > 0):
global hd_ai2d, hd_ai2d_output_tensor
del kpu_obj
del hd_ai2d
del hd_ai2d_output_tensor
#-------hand keypoint detection------:
# 手掌关键点检测 ai2d 初始化
def hk_ai2d_init():
with ScopedTiming("hk_ai2d_init",debug_mode > 0):
global hk_ai2d, hk_ai2d_output_tensor
hk_ai2d = nn.ai2d()
hk_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
data = np.ones(hk_kmodel_input_shape, dtype=np.uint8)
hk_ai2d_output_tensor = nn.from_numpy(data)
# 手掌关键点检测 ai2d 运行
def hk_ai2d_run(rgb888p_img, x, y, w, h):
with ScopedTiming("hk_ai2d_run",debug_mode > 0):
global hk_ai2d,hk_ai2d_input_tensor,hk_ai2d_output_tensor
hk_ai2d_input = rgb888p_img.to_numpy_ref()
hk_ai2d_input_tensor = nn.from_numpy(hk_ai2d_input)
hk_ai2d.set_crop_param(True, x, y, w, h)
hk_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
global hk_ai2d_builder
hk_ai2d_builder = hk_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,hk_kmodel_frame_size[1],hk_kmodel_frame_size[0]])
hk_ai2d_builder.run(hk_ai2d_input_tensor, hk_ai2d_output_tensor)
# 手掌关键点检测 ai2d 释放内存
def hk_ai2d_release():
with ScopedTiming("hk_ai2d_release",debug_mode > 0):
global hk_ai2d_input_tensor
del hk_ai2d_input_tensor
# 手掌关键点检测 kpu 初始化
def hk_kpu_init(hk_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hk_kpu_init",debug_mode > 0):
hk_kpu_obj = nn.kpu()
hk_kpu_obj.load_kmodel(hk_kmodel_file)
hk_ai2d_init()
return hk_kpu_obj
# 手掌关键点检测 kpu 输入预处理
def hk_kpu_pre_process(rgb888p_img, x, y, w, h):
hk_ai2d_run(rgb888p_img, x, y, w, h)
with ScopedTiming("hk_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hk_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hk_ai2d_output_tensor)
# 手掌关键点检测 kpu 获得 kmodel 输出
def hk_kpu_get_output():
with ScopedTiming("hk_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# 手掌关键点检测 kpu 运行
def hk_kpu_run(kpu_obj,rgb888p_img, x, y, w, h):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
hk_kpu_pre_process(rgb888p_img, x, y, w, h)
# (2)手掌关键点检测 kpu 运行
with ScopedTiming("hk_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放手掌关键点检测 ai2d 资源
hk_ai2d_release()
# (4)获取手掌关键点检测 kpu 输出
results = hk_kpu_get_output()
# (5)返回手掌关键点检测结果
return results
# 手掌关键点检测 kpu 释放内存
def hk_kpu_deinit(kpu_obj):
with ScopedTiming("hk_kpu_deinit",debug_mode > 0):
global hk_ai2d, hk_ai2d_output_tensor
del kpu_obj
del hk_ai2d
del hk_ai2d_output_tensor
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
# display 作图过程 标出检测到的21个关键点并用不同颜色的线段连接
def display_draw(results, x, y, w, h):
with ScopedTiming("display_draw",debug_mode >0):
global draw_img,osd_img
if results:
results_show = np.zeros(results.shape,dtype=np.int16)
results_show[0::2] = results[0::2] * w + x
results_show[1::2] = results[1::2] * h + y
for i in range(len(results_show)/2):
draw_img.draw_circle(results_show[i*2], results_show[i*2+1], 1, color=(255, 0, 255, 0),fill=False)
for i in range(5):
j = i*8
if i==0:
R = 255; G = 0; B = 0
if i==1:
R = 255; G = 0; B = 255
if i==2:
R = 255; G = 255; B = 0
if i==3:
R = 0; G = 255; B = 0
if i==4:
R = 0; G = 0; B = 255
draw_img.draw_line(results_show[0], results_show[1],results_show[j+2], results_show[j+3],color=(255,R,G,B))
draw_img.draw_line(results_show[j+2], results_show[j+3],results_show[j+4], results_show[j+5],color=(255,R,G,B))
draw_img.draw_line(results_show[j+4], results_show[j+5],results_show[j+6], results_show[j+7],color=(255,R,G,B))
draw_img.draw_line(results_show[j+6], results_show[j+7],results_show[j+8], results_show[j+9],color=(255,R,G,B))
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for hand_keypoint_detect.py**********
def hand_keypoint_detect_inference():
print("hand_keypoint_detect_test start")
kpu_hand_detect = hd_kpu_init(hd_kmodel_file) # 创建手掌检测的 kpu 对象
kpu_hand_keypoint_detect = hk_kpu_init(hk_kmodel_file) # 创建手掌关键点检测的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("hand_detect_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("hand_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = hd_kpu_run(kpu_hand_detect,rgb888p_img) # 执行手掌检测 kpu 运行 以及 后处理过程
draw_img.clear()
for det_box in dets:
x1, y1, x2, y2 = det_box[2],det_box[3],det_box[4],det_box[5]
w = int(float(x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
h = int(float(y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x1 = int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y1 = int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x2 = int(x2 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y2 = int(y2 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
if (h<(0.1*DISPLAY_HEIGHT)):
continue
if (w<(0.25*DISPLAY_WIDTH) and ((x1<(0.03*DISPLAY_WIDTH)) or (x2>(0.97*DISPLAY_WIDTH)))):
continue
if (w<(0.15*DISPLAY_WIDTH) and ((x1<(0.01*DISPLAY_WIDTH)) or (x2>(0.99*DISPLAY_WIDTH)))):
continue
length = max(w,h)/2
cx = (x1+x2)/2
cy = (y1+y2)/2
ratio_num = 1.26*length
x1_kp = int(max(0,cx-ratio_num))
y1_kp = int(max(0,cy-ratio_num))
x2_kp = int(min(DISPLAY_WIDTH-1, cx+ratio_num))
y2_kp = int(min(DISPLAY_HEIGHT-1, cy+ratio_num))
w_kp = int(x2_kp - x1_kp + 1)
h_kp = int(y2_kp - y1_kp + 1)
hk_results = hk_kpu_run(kpu_hand_keypoint_detect,rgb888p_img, x1_kp, y1_kp, w_kp, h_kp) # 执行手掌关键点检测 kpu 运行 以及 后处理过程
draw_img.draw_rectangle(x1, y1, w, h, color=(255, 0, 255, 0)) # 将得到的手掌检测结果 绘制到 display
display_draw(hk_results[0], x1_kp, y1_kp, w_kp, h_kp) # 将得到的手掌关键点检测结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
#gc.collect()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
hd_kpu_deinit(kpu_hand_detect) # 释放手掌检测 kpu
hk_kpu_deinit(kpu_hand_keypoint_detect) # 释放手掌关键点检测 kpu
gc.collect()
ret = media_deinit() # 释放 整个media
if ret:
print("hand_detect_test, buffer_deinit failed")
return ret
print("hand_detect_test end")
return 0
if __name__ == '__main__':
hand_keypoint_detect_inference()
9. 静态手势识别#
import aicube #aicube模块,封装检测分割等任务相关后处理
from media.camera import * #摄像头模块
from media.display import * #显示模块
from media.media import * #软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import nncase_runtime as nn #nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
import ulab.numpy as np #类似python numpy操作,但也会有一些接口不同
import time #时间统计
import image #图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import gc #垃圾回收模块
##config.py
#display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16)
DISPLAY_HEIGHT = 1080
##ai原图分辨率输入
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16)
OUT_RGB888P_HEIGH = 1080
#--------for hand detection----------
#kmodel输入shape
hd_kmodel_input_shape = (1,3,512,512) # 手掌检测kmodel输入分辨率
#kmodel相关参数设置
confidence_threshold = 0.2 # 手掌检测阈值,用于过滤roi
nms_threshold = 0.5 # 手掌检测框阈值,用于过滤重复roi
hd_kmodel_frame_size = [512,512] # 手掌检测输入图片尺寸
hd_frame_size = [OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH] # 手掌检测直接输入图片尺寸
strides = [8,16,32] # 输出特征图的尺寸与输入图片尺寸的比
num_classes = 1 # 手掌检测模型输出类别数
nms_option = False # 是否所有检测框一起做NMS,False则按照不同的类分别应用NMS
root_dir = '/sdcard/app/tests/'
hd_kmodel_file = root_dir + 'kmodel/hand_det.kmodel' # 手掌检测kmodel文件的路径
anchors = [26,27, 53,52, 75,71, 80,99, 106,82, 99,134, 140,113, 161,172, 245,276] #anchor设置
#--------for hand recognition----------
#kmodel输入shape
hr_kmodel_input_shape = (1,3,224,224) # 手势识别kmodel输入分辨率
#kmodel相关参数设置
hr_kmodel_frame_size = [224,224] # 手势识别输入图片尺寸
labels = ["gun","other","yeah","five"] # 模型输出类别名称
hr_kmodel_file = root_dir + "kmodel/hand_reco.kmodel" # 手势识别kmodel文件的路径
debug_mode = 0 # debug模式 大于0(调试)、 反之 (不调试)
#scoped_timing.py 用于debug模式输出程序块运行时间
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#ai_utils.py
global current_kmodel_obj # 定义全局的 kpu 对象
global hd_ai2d,hd_ai2d_input_tensor,hd_ai2d_output_tensor,hd_ai2d_builder # 定义手掌检测全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
global hr_ai2d,hr_ai2d_input_tensor,hr_ai2d_output_tensor,hr_ai2d_builder # 定义手势识别全局 ai2d 对象,并且定义 ai2d 的输入、输出 以及 builder
#-------hand detect--------:
# 手掌检测 ai2d 初始化
def hd_ai2d_init():
with ScopedTiming("hd_ai2d_init",debug_mode > 0):
global hd_ai2d
global hd_ai2d_builder
global hd_ai2d_output_tensor
# 计算padding值
ori_w = OUT_RGB888P_WIDTH
ori_h = OUT_RGB888P_HEIGH
width = hd_kmodel_frame_size[0]
height = hd_kmodel_frame_size[1]
ratiow = float(width) / ori_w
ratioh = float(height) / ori_h
if ratiow < ratioh:
ratio = ratiow
else:
ratio = ratioh
new_w = int(ratio * ori_w)
new_h = int(ratio * ori_h)
dw = float(width - new_w) / 2
dh = float(height - new_h) / 2
top = int(round(dh - 0.1))
bottom = int(round(dh + 0.1))
left = int(round(dw - 0.1))
right = int(round(dw - 0.1))
# init kpu and load kmodel
hd_ai2d = nn.ai2d()
hd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
hd_ai2d.set_pad_param(True, [0,0,0,0,top,bottom,left,right], 0, [114,114,114])
hd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
hd_ai2d_builder = hd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,height,width])
data = np.ones(hd_kmodel_input_shape, dtype=np.uint8)
hd_ai2d_output_tensor = nn.from_numpy(data)
# 手掌检测 ai2d 运行
def hd_ai2d_run(rgb888p_img):
with ScopedTiming("hd_ai2d_run",debug_mode > 0):
global hd_ai2d_input_tensor,hd_ai2d_output_tensor
hd_ai2d_input = rgb888p_img.to_numpy_ref()
hd_ai2d_input_tensor = nn.from_numpy(hd_ai2d_input)
hd_ai2d_builder.run(hd_ai2d_input_tensor, hd_ai2d_output_tensor)
# 手掌检测 ai2d 释放内存
def hd_ai2d_release():
with ScopedTiming("hd_ai2d_release",debug_mode > 0):
global hd_ai2d_input_tensor
del hd_ai2d_input_tensor
# 手掌检测 kpu 初始化
def hd_kpu_init(hd_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hd_kpu_init",debug_mode > 0):
hd_kpu_obj = nn.kpu()
hd_kpu_obj.load_kmodel(hd_kmodel_file)
hd_ai2d_init()
return hd_kpu_obj
# 手掌检测 kpu 输入预处理
def hd_kpu_pre_process(rgb888p_img):
hd_ai2d_run(rgb888p_img)
with ScopedTiming("hd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hd_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hd_ai2d_output_tensor)
# 手掌检测 kpu 获得 kmodel 输出
def hd_kpu_get_output():
with ScopedTiming("hd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]*result.shape[2]*result.shape[3]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# 手掌检测 kpu 运行
def hd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
hd_kpu_pre_process(rgb888p_img)
# (2)手掌检测 kpu 运行
with ScopedTiming("hd_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放手掌检测 ai2d 资源
hd_ai2d_release()
# (4)获取手掌检测 kpu 输出
results = hd_kpu_get_output()
# (5)手掌检测 kpu 结果后处理
dets = aicube.anchorbasedet_post_process( results[0], results[1], results[2], hd_kmodel_frame_size, hd_frame_size, strides, num_classes, confidence_threshold, nms_threshold, anchors, nms_option)
# (6)返回手掌检测结果
return dets
# 手掌检测 kpu 释放内存
def hd_kpu_deinit(kpu_obj):
with ScopedTiming("hd_kpu_deinit",debug_mode > 0):
global hd_ai2d, hd_ai2d_output_tensor
del kpu_obj
del hd_ai2d
del hd_ai2d_output_tensor
#-------hand recognition--------:
# 手势识别 ai2d 初始化
def hr_ai2d_init():
with ScopedTiming("hr_ai2d_init",debug_mode > 0):
global hr_ai2d, hr_ai2d_output_tensor
hr_ai2d = nn.ai2d()
hr_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
data = np.ones(hr_kmodel_input_shape, dtype=np.uint8)
hr_ai2d_output_tensor = nn.from_numpy(data)
# 手势识别 ai2d 运行
def hr_ai2d_run(rgb888p_img, x, y, w, h):
with ScopedTiming("hr_ai2d_run",debug_mode > 0):
global hr_ai2d,hr_ai2d_input_tensor,hr_ai2d_output_tensor
hr_ai2d_input = rgb888p_img.to_numpy_ref()
hr_ai2d_input_tensor = nn.from_numpy(hr_ai2d_input)
hr_ai2d.set_crop_param(True, x, y, w, h)
hr_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel )
global hr_ai2d_builder
hr_ai2d_builder = hr_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], [1,3,hr_kmodel_frame_size[1],hr_kmodel_frame_size[0]])
hr_ai2d_builder.run(hr_ai2d_input_tensor, hr_ai2d_output_tensor)
# 手势识别 ai2d 释放内存
def hr_ai2d_release():
with ScopedTiming("hr_ai2d_release",debug_mode > 0):
global hr_ai2d_input_tensor
del hr_ai2d_input_tensor
# 手势识别 kpu 初始化
def hr_kpu_init(hr_kmodel_file):
# init kpu and load kmodel
with ScopedTiming("hr_kpu_init",debug_mode > 0):
hr_kpu_obj = nn.kpu()
hr_kpu_obj.load_kmodel(hr_kmodel_file)
hr_ai2d_init()
return hr_kpu_obj
# 手势识别 kpu 输入预处理
def hr_kpu_pre_process(rgb888p_img, x, y, w, h):
hr_ai2d_run(rgb888p_img, x, y, w, h)
with ScopedTiming("hr_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,hr_ai2d_output_tensor
# set kpu input
current_kmodel_obj.set_input_tensor(0, hr_ai2d_output_tensor)
# 手势识别 kpu 获得 kmodel 输出
def hr_kpu_get_output():
with ScopedTiming("hr_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
result = result.reshape((result.shape[0]*result.shape[1]))
tmp2 = result.copy()
del result
results.append(tmp2)
return results
# softmax实现
def softmax(x):
x -= np.max(x)
x = np.exp(x) / np.sum(np.exp(x))
return x
# 手势识别 kpu 输出后处理
def hr_kpu_post_process(results):
x_softmax = softmax(results[0])
result = np.argmax(x_softmax)
text = " " + labels[result] + ": " + str(round(x_softmax[result],2))
return text
# 手势识别 kpu 运行
def hr_kpu_run(kpu_obj,rgb888p_img, x, y, w, h):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
hr_kpu_pre_process(rgb888p_img, x, y, w, h)
# (2)手势识别 kpu 运行
with ScopedTiming("hr_kpu_run",debug_mode > 0):
current_kmodel_obj.run()
# (3)释放手势识别 ai2d 资源
hr_ai2d_release()
# (4)获取手势识别 kpu 输出
results = hr_kpu_get_output()
# (5)手势识别 kpu 结果后处理
result = hr_kpu_post_process(results)
# (6)返回手势识别结果
return result
# 手势识别 kpu 释放内存
def hr_kpu_deinit(kpu_obj):
with ScopedTiming("hr_kpu_deinit",debug_mode > 0):
global hr_ai2d, hr_ai2d_output_tensor
del kpu_obj
del hr_ai2d
del hr_ai2d_output_tensor
#media_utils.py
global draw_img,osd_img,masks #for display 定义全局 作图image对象
global buffer,media_source,media_sink #for media 定义 media 程序中的中间存储对象
#for display 初始化
def display_init():
# use hdmi for display
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
# display 释放内存
def display_deinit():
display.deinit()
#for camera 初始化
def camera_init(dev_id):
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
# camera 开启
def camera_start(dev_id):
camera.start_stream(dev_id)
# camera 读取图像
def camera_read(dev_id):
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
# camera 图像释放
def camera_release_image(dev_id,rgb888p_img):
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
# camera 结束
def camera_stop(dev_id):
camera.stop_stream(dev_id)
#for media 初始化
def media_init():
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 图层1,用于画框
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_MPGC)
# 图层2,用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
# media 释放内存
def media_deinit():
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#**********for hand_recognition.py**********
def hand_recognition_inference():
print("hand_recognition start")
kpu_hand_detect = hd_kpu_init(hd_kmodel_file) # 创建手掌检测的 kpu 对象
kpu_hand_recognition = hr_kpu_init(hr_kmodel_file) # 创建手势识别的 kpu 对象
camera_init(CAM_DEV_ID_0) # 初始化 camera
display_init() # 初始化 display
rgb888p_img = None
try:
ret = media_init()
if ret:
print("hand_recognition_test, buffer init failed")
return ret
camera_start(CAM_DEV_ID_0)
while True:
with ScopedTiming("total",1):
rgb888p_img = camera_read(CAM_DEV_ID_0) # 读取一帧图片
if rgb888p_img == -1:
print("hand_recognition_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# for rgb888planar
if rgb888p_img.format() == image.RGBP888:
dets = hd_kpu_run(kpu_hand_detect,rgb888p_img) # 执行手掌检测 kpu 运行 以及 后处理过程
draw_img.clear()
for det_box in dets:
x1, y1, x2, y2 = det_box[2],det_box[3],det_box[4],det_box[5]
w = int(float(x2 - x1) * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
h = int(float(y2 - y1) * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x1 = int(x1 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y1 = int(y1 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
x2 = int(x2 * DISPLAY_WIDTH // OUT_RGB888P_WIDTH)
y2 = int(y2 * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH)
if (h<(0.1*DISPLAY_HEIGHT)):
continue
if (w<(0.25*DISPLAY_WIDTH) and ((x1<(0.03*DISPLAY_WIDTH)) or (x2>(0.97*DISPLAY_WIDTH)))):
continue
if (w<(0.15*DISPLAY_WIDTH) and ((x1<(0.01*DISPLAY_WIDTH)) or (x2>(0.99*DISPLAY_WIDTH)))):
continue
length = max(w,h)/2
cx = (x1+x2)/2
cy = (y1+y2)/2
ratio_num = 1.1*length
x1_kp = int(max(0,cx-ratio_num))
y1_kp = int(max(0,cy-ratio_num))
x2_kp = int(min(DISPLAY_WIDTH-1, cx+ratio_num))
y2_kp = int(min(DISPLAY_HEIGHT-1, cy+ratio_num))
w_kp = int(x2_kp - x1_kp + 1)
h_kp = int(y2_kp - y1_kp + 1)
hr_results = hr_kpu_run(kpu_hand_recognition,rgb888p_img, x1_kp, y1_kp, w_kp, h_kp) # 执行手势识别 kpu 运行 以及 后处理过程
draw_img.draw_rectangle(x1, y1, w, h, color=(255, 0, 255, 0)) # 将得到的检测结果 绘制到 display
draw_img.draw_string( x1 , y1-50, hr_results, color=(255,0, 255, 0), scale=4) # 将得到的识别结果 绘制到 display
camera_release_image(CAM_DEV_ID_0,rgb888p_img) # camera 释放图像
rgb888p_img = None
#gc.collect()
draw_img.copy_to(osd_img)
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
except Exception as e:
print(f"An error occurred during buffer used: {e}")
finally:
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
camera_stop(CAM_DEV_ID_0) # 停止 camera
display_deinit() # 释放 display
hd_kpu_deinit(kpu_hand_detect) # 释放手掌检测 kpu
hr_kpu_deinit(kpu_hand_recognition) # 释放手势识别 kpu
gc.collect()
ret = media_deinit() # 释放 整个media
if ret:
print("hand_recognition_test, buffer_deinit failed")
return ret
print("hand_recognition_test end")
return 0
if __name__ == '__main__':
hand_recognition_inference()
10.人脸mesh#
import ulab.numpy as np # 类似python numpy操作,但也会有一些接口不同
import nncase_runtime as nn # nncase运行模块,封装了kpu(kmodel推理)和ai2d(图片预处理加速)操作
from media.camera import * # 摄像头模块
from media.display import * # 显示模块
from media.media import * # 软件抽象模块,主要封装媒体数据链路以及媒体缓冲区
import aidemo # aidemo模块,封装ai demo相关后处理、画图操作
import image # 图像模块,主要用于读取、图像绘制元素(框、点等)等操作
import time # 时间统计
import gc # 垃圾回收模块
import os # 操作系统接口模块
import math # 数学模块
#********************for config.py********************
# display分辨率
DISPLAY_WIDTH = ALIGN_UP(1920, 16) # 显示宽度要求16位对齐
DISPLAY_HEIGHT = 1080
# ai原图分辨率,sensor默认出图为16:9,若需不形变原图,最好按照16:9比例设置宽高
OUT_RGB888P_WIDTH = ALIGN_UP(1920, 16) # ai原图宽度要求16位对齐
OUT_RGB888P_HEIGH = 1080
# kmodel参数设置
# 人脸检测kmodel输入shape
fd_kmodel_input_shape = (1,3,320,320)
# 人脸mesh kmodel输入shape
fm_kmodel_input_shape = (1,3,120,120)
fmpost_kmodel_input_shapes = [(3,3),(3,1),(40,1),(10,1)]
# ai原图padding
rgb_mean = [104,117,123]
#人脸检测kmodel其它参数设置
confidence_threshold = 0.5 # 人脸检测阈值
top_k = 5000
nms_threshold = 0.2
keep_top_k = 750
vis_thres = 0.5
variance = [0.1, 0.2]
anchor_len = 4200
score_dim = 2
det_dim = 4
keypoint_dim = 10
# 文件配置
# 人脸检测kmodel
root_dir = '/sdcard/app/tests/'
fd_kmodel_file = root_dir + 'kmodel/face_detection_320.kmodel'
# 人脸mesh kmodel
fm_kmodel_file = root_dir + 'kmodel/face_alignment.kmodel'
# 人脸mesh后处理kmodel
fmpost_kmodel_file = root_dir + 'kmodel/face_alignment_post.kmodel'
# anchor文件
anchors_path = root_dir + 'utils/prior_data_320.bin'
# 人脸mesh参数均值
param_mean = np.array([0.0003492636315058917,2.52790130161884e-07,-6.875197868794203e-07,60.1679573059082,-6.295513230725192e-07,0.0005757200415246189,-5.085391239845194e-05,74.2781982421875,5.400917189035681e-07,6.574138387804851e-05,0.0003442012530285865,-66.67157745361328,-346603.6875,-67468.234375,46822.265625,-15262.046875,4350.5888671875,-54261.453125,-18328.033203125,-1584.328857421875,-84566.34375,3835.960693359375,-20811.361328125,38094.9296875,-19967.85546875,-9241.3701171875,-19600.71484375,13168.08984375,-5259.14404296875,1848.6478271484375,-13030.662109375,-2435.55615234375,-2254.20654296875,-14396.5615234375,-6176.3291015625,-25621.919921875,226.39447021484375,-6326.12353515625,-10867.2509765625,868.465087890625,-5831.14794921875,2705.123779296875,-3629.417724609375,2043.9901123046875,-2446.6162109375,3658.697021484375,-7645.98974609375,-6674.45263671875,116.38838958740234,7185.59716796875,-1429.48681640625,2617.366455078125,-1.2070955038070679,0.6690792441368103,-0.17760828137397766,0.056725528091192245,0.03967815637588501,-0.13586315512657166,-0.09223993122577667,-0.1726071834564209,-0.015804484486579895,-0.1416848599910736],dtype=np.float)
# 人脸mesh参数方差
param_std = np.array([0.00017632152594160289,6.737943476764485e-05,0.00044708489440381527,26.55023193359375,0.0001231376954820007,4.493021697271615e-05,7.923670636955649e-05,6.982563018798828,0.0004350444069132209,0.00012314890045672655,0.00017400001524947584,20.80303955078125,575421.125,277649.0625,258336.84375,255163.125,150994.375,160086.109375,111277.3046875,97311.78125,117198.453125,89317.3671875,88493.5546875,72229.9296875,71080.2109375,50013.953125,55968.58203125,47525.50390625,49515.06640625,38161.48046875,44872.05859375,46273.23828125,38116.76953125,28191.162109375,32191.4375,36006.171875,32559.892578125,25551.1171875,24267.509765625,27521.3984375,23166.53125,21101.576171875,19412.32421875,19452.203125,17454.984375,22537.623046875,16174.28125,14671.640625,15115.6884765625,13870.0732421875,13746.3125,12663.1337890625,1.5870834589004517,1.5077009201049805,0.5881357789039612,0.5889744758605957,0.21327851712703705,0.2630201280117035,0.2796429395675659,0.38030216097831726,0.16162841022014618,0.2559692859649658],dtype=np.float)
# 调试模型,0:不调试,>0:打印对应级别调试信息
debug_mode = 0
#********************for scoped_timing.py********************
# 时间统计类
class ScopedTiming:
def __init__(self, info="", enable_profile=True):
self.info = info
self.enable_profile = enable_profile
def __enter__(self):
if self.enable_profile:
self.start_time = time.time_ns()
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.enable_profile:
elapsed_time = time.time_ns() - self.start_time
print(f"{self.info} took {elapsed_time / 1000000:.2f} ms")
#********************for ai_utils.py********************
global current_kmodel_obj #当前kpu对象
# fd_ai2d: 人脸检测ai2d实例
# fd_ai2d_input_tensor: 人脸检测ai2d输入
# fd_ai2d_output_tensor: 人脸检测ai2d输入
# fd_ai2d_builder: 根据人脸检测ai2d参数,构建的人脸检测ai2d_builder对象
global fd_ai2d,fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# fm_ai2d: 人脸mesh ai2d实例
# fm_ai2d_input_tensor: 人脸mesh ai2d输入
# fm_ai2d_output_tensor:人脸mesh ai2d输入
# fm_ai2d_builder: 根据人脸mesh ai2d参数,构建的人脸mesh ai2d_builder对象
global fm_ai2d,fm_ai2d_input_tensor,fm_ai2d_output_tensor,fm_ai2d_builder
global roi #人脸区域
global vertices #3D关键点
#读取anchor文件,为人脸检测后处理做准备
print('anchors_path:',anchors_path)
prior_data = np.fromfile(anchors_path, dtype=np.float)
prior_data = prior_data.reshape((anchor_len,det_dim))
def get_pad_one_side_param():
# 右padding或下padding,获取padding参数
dst_w = fd_kmodel_input_shape[3] # kmodel输入宽(w)
dst_h = fd_kmodel_input_shape[2] # kmodel输入高(h)
# OUT_RGB888P_WIDTH:原图宽(w)
# OUT_RGB888P_HEIGH:原图高(h)
# 计算最小的缩放比例,等比例缩放
ratio_w = dst_w / OUT_RGB888P_WIDTH
ratio_h = dst_h / OUT_RGB888P_HEIGH
if ratio_w < ratio_h:
ratio = ratio_w
else:
ratio = ratio_h
# 计算经过缩放后的新宽和新高
new_w = (int)(ratio * OUT_RGB888P_WIDTH)
new_h = (int)(ratio * OUT_RGB888P_HEIGH)
# 计算需要添加的padding,以使得kmodel输入的宽高和原图一致
dw = (dst_w - new_w) / 2
dh = (dst_h - new_h) / 2
# 四舍五入,确保padding是整数
top = (int)(round(0))
bottom = (int)(round(dh * 2 + 0.1))
left = (int)(round(0))
right = (int)(round(dw * 2 - 0.1))
return [0, 0, 0, 0, top, bottom, left, right]
def fd_ai2d_init():
# 人脸检测模型ai2d初始化
with ScopedTiming("fd_ai2d_init",debug_mode > 0):
# (1)创建人脸检测ai2d对象
global fd_ai2d
fd_ai2d = nn.ai2d()
# (2)设置人脸检测ai2d参数
fd_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
fd_ai2d.set_pad_param(True, get_pad_one_side_param(), 0, rgb_mean)
fd_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
#(3)人脸检测ai2d_builder,根据人脸检测ai2d参数、输入输出大小创建ai2d_builder对象
global fd_ai2d_builder
fd_ai2d_builder = fd_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fd_kmodel_input_shape)
#(4)创建人脸检测ai2d_output_tensor,用于保存人脸检测ai2d输出
global fd_ai2d_output_tensor
data = np.ones(fd_kmodel_input_shape, dtype=np.uint8)
fd_ai2d_output_tensor = nn.from_numpy(data)
def fd_ai2d_run(rgb888p_img):
# 根据人脸检测ai2d参数,对原图rgb888p_img进行预处理
with ScopedTiming("fd_ai2d_run",debug_mode > 0):
global fd_ai2d_input_tensor,fd_ai2d_output_tensor,fd_ai2d_builder
# (1)根据原图构建ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fd_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)运行人脸检测ai2d_builder,将结果保存到人脸检测ai2d_output_tensor中
fd_ai2d_builder.run(fd_ai2d_input_tensor, fd_ai2d_output_tensor)
def fd_ai2d_release():
# 释放人脸检测ai2d_input_tensor
with ScopedTiming("fd_ai2d_release",debug_mode > 0):
global fd_ai2d_input_tensor
del fd_ai2d_input_tensor
def fd_kpu_init(kmodel_file):
# 初始化人脸检测kpu对象,并加载kmodel
with ScopedTiming("fd_kpu_init",debug_mode > 0):
# 初始化人脸检测kpu对象
kpu_obj = nn.kpu()
# 加载人脸检测kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸检测ai2d
fd_ai2d_init()
return kpu_obj
def fd_kpu_pre_process(rgb888p_img):
# 设置人脸检测kpu输入
# 使用人脸检测ai2d对原图进行预处理(padding,resize)
fd_ai2d_run(rgb888p_img)
with ScopedTiming("fd_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fd_ai2d_output_tensor
# 设置人脸检测kpu输入
current_kmodel_obj.set_input_tensor(0, fd_ai2d_output_tensor)
def fd_kpu_get_output():
# 获取人脸检测kpu输出
with ScopedTiming("fd_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取模型输出,并将结果转换为numpy,以便进行人脸检测后处理
results = []
for i in range(current_kmodel_obj.outputs_size()):
data = current_kmodel_obj.get_output_tensor(i)
result = data.to_numpy()
del data
results.append(result)
return results
def fd_kpu_run(kpu_obj,rgb888p_img):
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)原图预处理,并设置模型输入
fd_kpu_pre_process(rgb888p_img)
# (2)人脸检测kpu推理
with ScopedTiming("fd kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸检测ai2d资源
fd_ai2d_release()
# (4)获取人俩检测kpu输出
results = fd_kpu_get_output()
# (5)人脸检测kpu结果后处理
with ScopedTiming("fd kpu_post",debug_mode > 0):
post_ret = aidemo.face_det_post_process(confidence_threshold,nms_threshold,fd_kmodel_input_shape[2],prior_data,
[OUT_RGB888P_WIDTH,OUT_RGB888P_HEIGH],results)
# (6)返回人脸检测框
if len(post_ret)==0:
return post_ret
else:
return post_ret[0] #0:det,1:landm,2:score
def fd_kpu_deinit(kpu_obj):
# kpu释放
with ScopedTiming("fd_kpu_deinit",debug_mode > 0):
global fd_ai2d, fd_ai2d_output_tensor
del kpu_obj #删除人脸检测kpu_obj变量,释放对它所引用对象的内存引用
del fd_ai2d #删除人脸检测ai2d变量,释放对它所引用对象的内存引用
del fd_ai2d_output_tensor #删除人脸检测ai2d_output_tensor变量,释放对它所引用对象的内存引用
###############for face recognition###############
def parse_roi_box_from_bbox(bbox):
# 获取人脸roi
x1, y1, w, h = map(lambda x: int(round(x, 0)), bbox[:4])
old_size = (w + h) / 2
center_x = x1 + w / 2
center_y = y1 + h / 2 + old_size * 0.14
size = int(old_size * 1.58)
x0 = center_x - float(size) / 2
y0 = center_y - float(size) / 2
x1 = x0 + size
y1 = y0 + size
x0 = max(0, min(x0, OUT_RGB888P_WIDTH))
y0 = max(0, min(y0, OUT_RGB888P_HEIGH))
x1 = max(0, min(x1, OUT_RGB888P_WIDTH))
y1 = max(0, min(y1, OUT_RGB888P_HEIGH))
roi = (x0, y0, x1 - x0, y1 - y0)
return roi
def fm_ai2d_init():
# 人脸mesh ai2d初始化
with ScopedTiming("fm_ai2d_init",debug_mode > 0):
# (1)创建人脸mesh ai2d对象
global fm_ai2d
fm_ai2d = nn.ai2d()
# (2)创建人脸mesh ai2d_output_tensor对象,用于存放ai2d输出
global fm_ai2d_output_tensor
data = np.ones(fm_kmodel_input_shape, dtype=np.uint8)
fm_ai2d_output_tensor = nn.from_numpy(data)
def fm_ai2d_run(rgb888p_img,det):
# 人脸mesh ai2d推理
with ScopedTiming("fm_ai2d_run",debug_mode > 0):
global fm_ai2d,fm_ai2d_input_tensor,fm_ai2d_output_tensor
#(1)根据原图ai2d_input_tensor对象
ai2d_input = rgb888p_img.to_numpy_ref()
fm_ai2d_input_tensor = nn.from_numpy(ai2d_input)
# (2)根据新的det设置新的人脸mesh ai2d参数
fm_ai2d.set_dtype(nn.ai2d_format.NCHW_FMT,
nn.ai2d_format.NCHW_FMT,
np.uint8, np.uint8)
global roi
roi = parse_roi_box_from_bbox(det)
fm_ai2d.set_crop_param(True,int(roi[0]),int(roi[1]),int(roi[2]),int(roi[3]))
fm_ai2d.set_resize_param(True, nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)
# (3)根据新的人脸mesh ai2d参数,构建人脸mesh ai2d_builder
global fm_ai2d_builder
fm_ai2d_builder = fm_ai2d.build([1,3,OUT_RGB888P_HEIGH,OUT_RGB888P_WIDTH], fm_kmodel_input_shape)
# (4)推理人脸mesh ai2d,将预处理的结果保存到fm_ai2d_output_tensor
fm_ai2d_builder.run(fm_ai2d_input_tensor, fm_ai2d_output_tensor)
def fm_ai2d_release():
# 释放人脸mesh ai2d_input_tensor、ai2d_builder
with ScopedTiming("fm_ai2d_release",debug_mode > 0):
global fm_ai2d_input_tensor,fm_ai2d_builder
del fm_ai2d_input_tensor
del fm_ai2d_builder
def fm_kpu_init(kmodel_file):
# 人脸mesh kpu初始化
with ScopedTiming("fm_kpu_init",debug_mode > 0):
# 初始化人脸mesh kpu对象
kpu_obj = nn.kpu()
# 加载人脸mesh kmodel
kpu_obj.load_kmodel(kmodel_file)
# 初始化人脸mesh ai2d
fm_ai2d_init()
return kpu_obj
def fm_kpu_pre_process(rgb888p_img,det):
# 人脸mesh kpu预处理
# 人脸mesh ai2d推理,根据det对原图进行预处理
fm_ai2d_run(rgb888p_img,det)
with ScopedTiming("fm_kpu_pre_process",debug_mode > 0):
global current_kmodel_obj,fm_ai2d_output_tensor
# 将人脸mesh ai2d输出设置为人脸mesh kpu输入
current_kmodel_obj.set_input_tensor(0, fm_ai2d_output_tensor)
def fm_kpu_get_output():
with ScopedTiming("fm_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取人脸mesh kpu输出
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result
def fm_kpu_post_process(param):
# 人脸mesh kpu结果后处理,反标准化
with ScopedTiming("fm_kpu_post_process",debug_mode > 0):
param = param * param_std + param_mean
return param
def fm_kpu_run(kpu_obj,rgb888p_img,det):
# 人脸mesh kpu推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
# (1)人脸mesh kpu预处理,设置kpu输入
fm_kpu_pre_process(rgb888p_img,det)
# (2)人脸mesh kpu推理
with ScopedTiming("fm_kpu_run",debug_mode > 0):
kpu_obj.run()
# (3)释放人脸mesh ai2d
fm_ai2d_release()
# (4)获取人脸mesh kpu输出
param = fm_kpu_get_output()
# (5)人脸mesh 后处理
param = fm_kpu_post_process(param)
return param
def fm_kpu_deinit(kpu_obj):
# 人脸mesh kpu释放
with ScopedTiming("fm_kpu_deinit",debug_mode > 0):
global fm_ai2d,fm_ai2d_output_tensor
del kpu_obj # 删除kpu_obj变量,释放对它所引用对象的内存引用
del fm_ai2d # 删除fm_ai2d变量,释放对它所引用对象的内存引用
del fm_ai2d_output_tensor # 删除fm_ai2d_output_tensor变量,释放对它所引用对象的内存引用
def fmpost_kpu_init(kmodel_file):
# face mesh post模型初始化
with ScopedTiming("fmpost_kpu_init",debug_mode > 0):
# 初始化人脸mesh kpu post对象
kpu_obj = nn.kpu()
# 加载人脸mesh后处理kmodel
kpu_obj.load_kmodel(kmodel_file)
return kpu_obj
def fmpost_kpu_pre_process(param):
# face mesh post模型预处理,param解析
with ScopedTiming("fmpost_kpu_pre_process",debug_mode > 0):
param = param[0]
trans_dim, shape_dim, exp_dim = 12, 40, 10
# reshape前务必进行copy,否则会导致模型输入错误
R_ = param[:trans_dim].copy().reshape((3, -1))
R = R_[:, :3].copy()
offset = R_[:, 3].copy()
offset = offset.reshape((3, 1))
alpha_shp = param[trans_dim:trans_dim + shape_dim].copy().reshape((-1, 1))
alpha_exp = param[trans_dim + shape_dim:].copy().reshape((-1, 1))
R_tensor = nn.from_numpy(R)
current_kmodel_obj.set_input_tensor(0, R_tensor)
del R_tensor
offset_tensor = nn.from_numpy(offset)
current_kmodel_obj.set_input_tensor(1, offset_tensor)
del offset_tensor
alpha_shp_tensor = nn.from_numpy(alpha_shp)
current_kmodel_obj.set_input_tensor(2, alpha_shp_tensor)
del alpha_shp_tensor
alpha_exp_tensor = nn.from_numpy(alpha_exp)
current_kmodel_obj.set_input_tensor(3, alpha_exp_tensor)
del alpha_exp_tensor
return
def fmpost_kpu_get_output():
# 获取face mesh post模型输出
with ScopedTiming("fmpost_kpu_get_output",debug_mode > 0):
global current_kmodel_obj
# 获取人脸mesh kpu输出
data = current_kmodel_obj.get_output_tensor(0)
result = data.to_numpy()
del data
return result
def fmpost_kpu_post_process(roi):
# face mesh post模型推理结果后处理
with ScopedTiming("fmpost_kpu_post_process",debug_mode > 0):
x, y, w, h = map(lambda x: int(round(x, 0)), roi[:4])
x = x * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
y = y * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
w = w * DISPLAY_WIDTH // OUT_RGB888P_WIDTH
h = h * DISPLAY_HEIGHT // OUT_RGB888P_HEIGH
roi_array = np.array([x,y,w,h],dtype=np.float)
global vertices
aidemo.face_mesh_post_process(roi_array,vertices)
return
def fmpost_kpu_run(kpu_obj,param):
# face mesh post模型推理
global current_kmodel_obj
current_kmodel_obj = kpu_obj
fmpost_kpu_pre_process(param)
with ScopedTiming("fmpost_kpu_run",debug_mode > 0):
kpu_obj.run()
global vertices
vertices = fmpost_kpu_get_output()
global roi
fmpost_kpu_post_process(roi)
return
def fmpost_kpu_deinit(kpu_obj):
# face mesh post模型释放
with ScopedTiming("fm_kpu_deinit",debug_mode > 0):
del kpu_obj
#********************for media_utils.py********************
global draw_img_ulab,draw_img,osd_img #for display
global buffer,media_source,media_sink #for media
# for display,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.2
def display_init():
# 设置使用hdmi进行显示
display.init(LT9611_1920X1080_30FPS)
display.set_plane(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, PIXEL_FORMAT_YVU_PLANAR_420, DISPLAY_MIRROR_NONE, DISPLAY_CHN_VIDEO1)
def display_deinit():
# 释放显示资源
display.deinit()
def display_draw(dets,vertices_list):
# 在显示器画人脸轮廓
with ScopedTiming("display_draw",debug_mode >0):
global draw_img_ulab,draw_img,osd_img
if dets:
draw_img.clear()
for vertices in vertices_list:
aidemo.face_draw_mesh(draw_img_ulab, vertices)
# (4)将轮廓结果拷贝到osd
draw_img.copy_to(osd_img)
# (5)将osd显示到屏幕
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
else:
# (1)清空用来画框的图像
draw_img.clear()
# (2)清空osd
draw_img.copy_to(osd_img)
# (3)显示透明图层
display.show_image(osd_img, 0, 0, DISPLAY_CHN_OSD3)
#for camera,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.1
def camera_init(dev_id):
# camera初始化
camera.sensor_init(dev_id, CAM_DEFAULT_SENSOR)
# set chn0 output yuv420sp
camera.set_outsize(dev_id, CAM_CHN_ID_0, DISPLAY_WIDTH, DISPLAY_HEIGHT)
camera.set_outfmt(dev_id, CAM_CHN_ID_0, PIXEL_FORMAT_YUV_SEMIPLANAR_420)
# set chn2 output rgb88planar
camera.set_outsize(dev_id, CAM_CHN_ID_2, OUT_RGB888P_WIDTH, OUT_RGB888P_HEIGH)
camera.set_outfmt(dev_id, CAM_CHN_ID_2, PIXEL_FORMAT_RGB_888_PLANAR)
def camera_start(dev_id):
# camera启动
camera.start_stream(dev_id)
def camera_read(dev_id):
# 读取一帧图像
with ScopedTiming("camera_read",debug_mode >0):
rgb888p_img = camera.capture_image(dev_id, CAM_CHN_ID_2)
return rgb888p_img
def camera_release_image(dev_id,rgb888p_img):
# 释放一帧图像
with ScopedTiming("camera_release_image",debug_mode >0):
camera.release_image(dev_id, CAM_CHN_ID_2, rgb888p_img)
def camera_stop(dev_id):
# 停止camera
camera.stop_stream(dev_id)
#for media,已经封装好,无需自己再实现,直接调用即可,详细解析请查看1.6.3
def media_init():
# meida初始化
config = k_vb_config()
config.max_pool_cnt = 1
config.comm_pool[0].blk_size = 4 * DISPLAY_WIDTH * DISPLAY_HEIGHT
config.comm_pool[0].blk_cnt = 1
config.comm_pool[0].mode = VB_REMAP_MODE_NOCACHE
ret = media.buffer_config(config)
global media_source, media_sink
media_source = media_device(CAMERA_MOD_ID, CAM_DEV_ID_0, CAM_CHN_ID_0)
media_sink = media_device(DISPLAY_MOD_ID, DISPLAY_DEV_ID, DISPLAY_CHN_VIDEO1)
media.create_link(media_source, media_sink)
# 初始化多媒体buffer
ret = media.buffer_init()
if ret:
return ret
global buffer, draw_img_ulab,draw_img, osd_img
buffer = media.request_buffer(4 * DISPLAY_WIDTH * DISPLAY_HEIGHT)
# 用于画框
draw_img_ulab = np.zeros((DISPLAY_HEIGHT,DISPLAY_WIDTH,4),dtype=np.uint8)
draw_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, alloc=image.ALLOC_REF,data = draw_img_ulab)
# 用于拷贝画框结果,防止画框过程中发生buffer搬运
osd_img = image.Image(DISPLAY_WIDTH, DISPLAY_HEIGHT, image.ARGB8888, poolid=buffer.pool_id, alloc=image.ALLOC_VB,
phyaddr=buffer.phys_addr, virtaddr=buffer.virt_addr)
return ret
def media_deinit():
# meida资源释放
global buffer,media_source, media_sink
media.release_buffer(buffer)
media.destroy_link(media_source, media_sink)
ret = media.buffer_deinit()
return ret
#********************for face_detect.py********************
def face_mesh_inference():
# 人脸检测kpu初始化
kpu_face_detect = fd_kpu_init(fd_kmodel_file)
# 人脸mesh kpu初始化
kpu_face_mesh = fm_kpu_init(fm_kmodel_file)
# face_mesh_post kpu初始化
kpu_face_mesh_post = fmpost_kpu_init(fmpost_kmodel_file)
# camera初始化
camera_init(CAM_DEV_ID_0)
# 显示初始化
display_init()
rgb888p_img = None
# 注意:将一定要将一下过程包在try中,用于保证程序停止后,资源释放完毕;确保下次程序仍能正常运行
try:
# 注意:媒体初始化(注:媒体初始化必须在camera_start之前,确保media缓冲区已配置完全)
ret = media_init()
if ret:
print("face_detect_test, buffer init failed")
return ret
# 启动camera
camera_start(CAM_DEV_ID_0)
time.sleep(5)
while True:
with ScopedTiming("total",1):
# (1)读取一帧图像
rgb888p_img = camera_read(CAM_DEV_ID_0)
# (2)若读取失败,释放当前帧
if rgb888p_img == -1:
print("face_detect_test, capture_image failed")
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
continue
# (3)若读取成功,推理当前帧
if rgb888p_img.format() == image.RGBP888:
# (3.1)推理当前图像,并获取人脸检测结果
dets = fd_kpu_run(kpu_face_detect,rgb888p_img)
## (3.2)针对每个人脸框,推理得到对应人脸mesh
mesh_result = []
for det in dets:
param = fm_kpu_run(kpu_face_mesh,rgb888p_img,det)
fmpost_kpu_run(kpu_face_mesh_post,param)
global vertices
mesh_result.append(vertices)
## (3.3)将人脸mesh 画到屏幕上
display_draw(dets,mesh_result)
# (4)释放当前帧
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
rgb888p_img = None
#with ScopedTiming("gc collect", debug_mode > 0):
#gc.collect()
except Exception as e:
# 捕捉运行运行中异常,并打印错误
print(f"An error occurred during buffer used: {e}")
finally:
# 释放当前帧
if rgb888p_img is not None:
#先release掉申请的内存再stop
camera_release_image(CAM_DEV_ID_0,rgb888p_img)
# 停止camera
camera_stop(CAM_DEV_ID_0)
# 释放显示资源
display_deinit()
# 释放kpu资源
fd_kpu_deinit(kpu_face_detect)
fm_kpu_deinit(kpu_face_mesh)
fmpost_kpu_deinit(kpu_face_mesh_post)
# 垃圾回收
gc.collect()
time.sleep(1)
# 释放媒体资源
ret = media_deinit()
if ret:
print("face_mesh_test, buffer_deinit failed")
return ret
#print("face_mesh_test end")
return 0
if __name__ == '__main__':
face_mesh_inference()