前言
在已发布的《CSK6 AI模型部署教程》系列文章中,我们实现了将训练的LNN模型在CSK 6芯片上运行的完整流程:将一张苹果的图片提取到的特征,并打包到了固件里面,然后在固件里去将它送给thinker模型去进行推理,得出一个苹果的一个推理结果。
在以往分享的示例中我们的thinker模型是打包WASM APP里面去的(以头文件的形式),而新发布的版本中我们做了以下更新:
- 允许模型直接烧录到Flash分区,通过传递Flash地址和模型大小的方式就可以初始化模型,更方便我们在开发的过程中对模型进行升级换代。
- 更新了SDK仓库地址,CP固件不在跟随SDK仓库更新,以后会跟着示例工程一起发布CP固件。
图像分类示例概况
该示例是基于 CSK6视觉AI套件,从GCO32A 摄像头采集实时图像,传输给 Thinker 进行图像分类。
camera image detect项目仓库地址: https://cloud.listenai.com/listenai/samples/camera_image_detect
本文是对上一篇图像分类示例做的一个拓展,在上一篇文章中,我们实验是将图片从电脑上用Python脚本把它提取一个特征,然后把特征给打包到固件里面,最后再来做这个推理结果。在本文的示例中,我们会从视觉开发套件的摄像头采集一个图像,把提取特征的操作在CSK6芯片上来进行,然后把这个特征文件送给这个模型进行推理,最后来得出一个实时的推理结果。
快速上手
实验使用的聆思CSK6 视觉AI开发套件包含CSK6 nanokit、GC032A摄像头模组、拓展板
获取项目并编译
开发环境搭建(支持Ubuntu/Windows/macOS):
https://docs2.listenai.com/x/xfI6rkDCKmW
获取摄像头图像分类示例项目:
lisa zep create --from-git
https://cloud.listenai.com/listenai/samples/camera image detect.qit
编译zephyr应用
lisa zep build -b csk6011a nano
烧录
除了烧录我们编译的本应用固件之外,我们还有其他的三个资源是要进行烧录的,具体如下:
烧录zephyr应用固件:
lisa zep flash
烧录CP固件资源
lisa zep exec cskburn -s /der/ttyUSBO -C 6 0x100000 ./resources/cp.bin -b 748800
烧录WASM应用
lisa zep exec cskburn -s /dev/ttyUSBO -C 6 0x200000 ./resources/thinker resnet18.aot -b 748800
烧录WASM 模型
lisa zep exec cskburn -s /dev/ttyUSBO -C 6 0x300000 ./resources/resnet18 model.bin -b 748800
查看串口日志
开发套件烧录固件后,可以通过串口终端连接开发板上的DAPLink USB口,出现下图信息的时候代表就绪了。
为方便大家进行调试验证,聆思科技也提供了一个开放的通过Chrome直接打开串口连接的工具,当开发板连接电脑后会弹出提示,打开后可以选择对应的串口选项,界面如下:
开发板准备就绪后,我们以下图的识别苹果为例, 当拍到苹果,识别的推理结果是apple。
流程讲解
运行流程
识别苹果示例中,固件启动后按下按钮就会从摄像头获取一个图像,程序先做图像处理,然后输入到thinker(AI模型)进行推理,最后获取推理结果。
图像处理
图像处理之前是在PC上处理的,通过我们开源的一个图像处理脚本(https//github.com/LISTENAl/thinker/blob/main/tools/image_preprocesspy)来将这个图像转换成特征文件。但在本文的示例项目上,我们是在CSK6芯片上进行图像处理。
上图为示例执行的处理脚本,在这个代码中可以看到它首先对图片做了一个处理,相关代码在transform部分。包含了三个操作:
- reside缩放,图像缩放成了32×32,使用一个双线性差值法来做一个缩放;
- 转成一个TOTensor,转成 tensor训练的数据,也就是直接转成了一个浮点性,在它的源码实现里就是除以一个255转成的一个浮点;
- Noramlize,进行训练模型中常用的一个操作,减均值除标准差。
Import部分使用了我们训练中特定的一个参数,可以直接搬到 C代码里面。在做完这个 transform之后,最后一步的代码段里有一个floor,floor执行的是强转整形。
代码中的公式中的“*64+0.5”的意思是乘以64之后做的一个四舍五入,这段代码执行后就会先乘以64,然后做四舍五入,最后转成一个int8类型数据。每个数据作为一个字节来存到一个文件中,这就是这个 Python脚本里面完整的一个处理过程。
图像缩放
在图像处理的代码中lib_image.h是函数定义的头文件;
lib_image.c文件包含了程序实现;
Pillow库: https://github.com/python-pillow/Pillow;
想了解详细的信息可以搜索我们的同步讲解视频了解更多。
缩放的过程使用双线性差值法,这里结合实际运行在CSK6示例代码来说明。下面代码中定的一些参数,目的是为了跟 Python的pillow库保持一致。
/**
* 从 Python Pillow 库中抽离的 Resize 算法
* https://github.com/python-pillow/Pillow
*采用双线性插值处理
* @param input 输入的 RGB 格式原图
* @param input_xsize 输入图像的宽度
* @param input_ysize 输入图像的高度
* @param output 输出的 RGB 格式图像
* @param xsize 输出图像的宽度
* @param ysize 输出图像的高度
* @param box 目标矩形,代表原图中目标区域的左上角坐标和右下角坐标
*/
void ImagingResample(uint8_t *input, int input_xsize, int input_ysize, uint8_t *output, int xsize,
int ysize, float box[4]);
/**
* 将摄像头的 vyuy 格式图像转换为 rgb 格式图像
* @param in_buf 摄像头的 vyuy 格式图像
* @param out_buf 转换后的 rgb 格式图像
* @param pixel_count 输入图像的像素个数
*/
void vyuy_to_rgb24(uint8_t *in_buf, uint8_t *out_buf, int pixel_count);
图像处理库最核心的是libImaging.,用C实现的,我们直接迁移到CSK6芯片上并可运行起来。
在运行的过程中会去计算在垂直方向上跟水平方向上分别需不需要做缩放,然后再得出结论。之后,会依次的去做缩放。先做水平方向的,然后再做垂直方向的,具体的实现可以在lib_images.c的代码中看到。
按钮按下的事件触发摄像头采集到图像数据,程序会过滤掉最开始的一帧,然后处理后续获取的图像数据。从摄像头驱动直接出来的数据是VYUY格式的,所以在处理之前首先要转成这个RGB24, RGB24中每一个像素代表24位,然后每8位是一个颜色,然后再得出RGB的数值之。再去对它做一个缩放,这个就是图像处理的完整过程了。
更多学习资源
如果需要获取本教程相关的学习资源、代码,
或者了解更多与嵌入式开发、AI芯片相关的其他课程,可以点击查看 目录导航。