机器视觉

LCD

LCD 液晶显示屏是非常常见的一个外接显示设备,跟前面的 OLED 显示屏相比,LCD 会更常用一些,我们看到的手持设备、小型电器,很多都用到 LCD,部分配合触摸屏应用,能实现非常多的功能。

除此之外,LCD 还是 pyAI-K210 机器视觉应用中显示的重要工具。

本实验用的 LCD 是 2.8 寸,驱动是常见的 ST7789V,使用 8 位接口跟 pyAIK210 通信,按以往嵌入式 C 语言开发,我们需要对 ST7789 进行编程实现驱动,然后再建立各种字符显示及显示图片等函数。使用 MicroPython 其实也需要做以上工作,但由于可读性和移植性强的特点,我们只需要搞清各个对象函数使如何使用即可。总的来说和之前一样,有构造函数和功能函数。构造函数解决的是初始化问题,告诉 pyAI-K210 外设是怎么接线,是什么样的;而功能函数解决的则是使用问题,我们基于自己的需求直接调用相关功能函数,实现自己的功能即可!我们管这些函数的集合叫驱动,MaixPy 已经将这 LCD.py 驱动写好了,我们学会如何使用即可。其构造函数和使用方法如下:

 

构造函数

1
lcd.init(type=1,freq=15000000,color=lcd.BLACK)

初始化 LCD。

【type】LCD 类型;

【freq】通信频率;

【color】LCD 初始化的颜色。

使用方法

1
lcd.deinit()

注销 LCD 驱动,释放 IO 引脚。

1
lcd.clear(color)

填充指定颜色。默认是黑色

1
lcd.draw_string(x,y,str,color,bg_color)

写字符

【x,y】起始坐标;

【str】字符内容

【color】字体颜色

【bg_color】字体背景颜色

1
lcd.display(image,roi=Auto)

显示图片。

【image】RGB565 或 GRAYSCALE 图片。

【ROI】显示的感兴趣区域,未指定则为图像大小。

1
lcd.rotation(dir)

LCD 屏幕方向设定。

【dir】取值范围[0-3],从 0 到 3 依顺时钟旋转。

1
lcd.mirror(invert)

镜面显示。

【invert】=True 则为镜面显示;=False 则否。

更多 LCD 模块说明请看 MaxiPy 官方文档:

https://wiki.sipeed.com/soft/maixpy/zh/api_reference/machine_vision/lcd.html

有了上面的对象构造函数和使用说明,编程可以说是信手拈来了,我们来跑

一下其主要功能显示字符和图像,代码编写流程如下:

1
2
3
4
5
6
7
8
9
import lcd,image,utime
lcd.init() #初始化 LCD
lcd.clear(lcd.WHITE) #清屏白色
#显示字符
lcd.draw_string(110, 120, "Hello 01Studio!",lcd.BLACK, lcd.WHITE) #显示字符
utime.sleep(2) #延时 2 秒
lcd.rotation(1) #由于图像默认是 240*320,因此顺时钟旋转 90°。
#显示图像,必须先将 01Studio.bmp 文件发送到开发板才能正常运行
lcd.display(image.Image("01Studio.bmp"))

 

摄像头应用

从前面的基础实验我们熟悉了 K210 基于 MicroPython 的编程方法,但那可以说是只发挥了 K210 冰山一角的性能应用,摄像头是整个机器视觉应用的基础。今天我们就通过示例代码来看看 pyAI-K210 是如何使用摄像头的。

 

MaixPy 机器视觉库代码大部分都是参考 OpenMV 移植过来,其已经将所有的摄像头功能封装到 sersor 模块中,用户可以通过调用轻松使用。这也是使用MicroPython 编程的魅力所在。

 

构造函数

sensor

摄像头对象,通过 import 直接调用

使用方法

1
sensor.reset()

初始化摄像头

1
sensor.set_pixformat(*pixformat*)

设置像素格式。pixformat 有 3 个参数。

sensor.GRAYSCAL:灰度图像,每像素 8 位(1 字节),处理速度快;

sensor.RGB565: 每像素为 16 位(2 字节),5 位用于红色,6 位用于绿色,5 位用于蓝色,处理速度比灰度图像要慢。

1
sensor.set_framesize(*framesize*)

设置每帧大小(即图像尺寸)。常用的 framesize 参数有下面这些:

sensor.QQVGA: 160*120;

sensor.QVGA: 320*240;

sensor.VGA: 640*480;

sensor.skip_frames([n, time])

摄像头配置后跳过 n 帧或者等待时间 time 让其变稳定。n:跳过帧数;time:等待

时间,单位 ms。

(如果 n 和 time 均没指定,则默认跳过 300 毫秒的帧。)

1
sensor.snapshot()

使用相机拍摄一张照片,并返回 image 对象。

*其它更多用法请阅读 MaixPy 官方文档:

https://wiki.sipeed.com/soft/maixpy/zh/api_reference/machine_visio

n/sensor.html

 

我们再来看看本例程用于计算 FPS(每秒帧数)的 clock 模块。

构造函数

1
clock=time.clock()

创建一个时钟。

使用方法

1
clock.tick()

开始追踪运行时间。

1
clock.fps ()

停止追踪运行时间,并返回当前 FPS(每秒帧数)。

在调用该函数前始终首先调用 tick 。

*其它更多用法请阅读 Maixpy 官方文档:

文档链接:http://docs.openmv.io/library/omv.time.html

 

 

我们来看看 helloword 代码的编写流程图:

这个实验运行的就是编辑框里面的 helloworld 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sensor, image, time, lcd
lcd.init(freq=15000000) #初始化 LCD
sensor.reset() #复位和初始化摄像头,执行 sensor.run(0)停止。
#sensor.set_vflip(1) #将摄像头设置成后置方式(所见即所得)
sensor.set_pixformat(sensor.RGB565) # 设置像素格式为彩色 RGB565 (或灰色)
sensor.set_framesize(sensor.QVGA) # 设置帧大小为 QVGA (320x240)
sensor.skip_frames(time = 2000) # 等待设置生效.
clock = time.clock() # 创建一个时钟来追踪 FPS(每秒拍摄帧数)
while(True):
clock.tick() # 更新 FPS 时钟.
img = sensor.snapshot() # 拍摄一个图片并保存.
lcd.display(img) # 在 LCD 上显示
print(clock.fps()) # 注意: 当 K210 连接到 IDE 时候,运行速度减
#半,因此当断开 IDE 时 FPS 会提升。

 

画图

通过摄像头采集到照片后,我们会进行一些处理,而这时候往往需要一些图形来指示,比如在图片某个位置标记箭头、人脸识别后用矩形框提示等。本节就是学习在图形上画图的使用功能。

构造函数

1
img=sensor.snapshot() 或 img=image.Image(path[, copy_to_fb=False])

创建图像,通过拍摄或者读取文件路径获取。

copy_to_fb=True**:可以加载大图片;

copy_to_fb=False:不可以加载大图片。

示例:img = image.Image(“01Studio.bmp”, copy_to_fb=True),表示加载根

目录下的 01Studio.bmp 图片。

使用方法

1
image.draw_line(*x0*, *y0*, *x1*, *y1*[, *color*[, *thickness=1*]])

画线段。(x0,y0):起始坐标;(x1,y1):终点坐标;color:颜色,如

(255,0,0)表示红色;thickness:粗细。

1
image.draw_rectangle(*x*, *y*, *w*, *h*[, *color*[, thickness*=1*[, *fill=False*]]])

画矩形。(x,y):起始坐标;w:宽度;h:长度;color:颜色;thickness:边框粗细;fill:是否填充。

1
image.draw_circle(*x*, *y*, *radius*[, *color*[, thickness*=1*[, *fill=False*]]])

画圆。(x,y):圆心; radius:半径; color:颜色;thickness:线条粗细;

fill:是否填充。

1
image.draw_arrow(*x0*, *y0*, *x1*, *y1*[, *color*[, size,[thickness*=1]*]])

画箭头。(x0,y0):起始坐标;(x1,y1):终点坐标;color:颜色;size:箭头位置大小。thickness:线粗细。

1
image.draw_cross(*x*, *y*[, *color*[, *size=5*[, *thickness=1*]]])

画十字交叉。(x,y):交叉坐标;color:颜色;size:尺寸;thickness:线粗细。

1
image.draw_string(*x*, *y*, *text*[, *color*[, *scale=*1[,*mono_space=True*…]]]])

写字符。(x,y): 起始坐标;text:字符内容;color:颜色;scale:字体大小;

mono_space:强制间距。

*其它更多用法请阅读 MaixPy 官方文档:

https://wiki.sipeed.com/soft/maixpy/zh/api_reference/machine_vision/image/image.html

 

熟悉了 image 对象的画图功能后,我们尝试在摄像头采集到的画面依次画出线段、矩形、圆形、箭头、十字交叉和字符。具体编程思路如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import sensor, image, time, lcd
lcd.init(freq=15000000)
sensor.reset() #复位摄像头
#sensor.set_vflip(1) #将摄像头设置成后置方式(所见即所得)
sensor.set_pixformat(sensor.RGB565) # 设置像素格式 RGB565 (or GRAYSCALE)
sensor.set_framesize(sensor.QVGA) # 设置帧尺寸 QVGA (320x240)
sensor.skip_frames(time = 2000) # 灯带设置响应.
clock = time.clock() # 新建一个时钟对象计算 FPS.
while(True):
clock.tick()
img = sensor.snapshot()

# 画线段:从 x0, y0 到 x1, y1 坐标的线段,颜色红色,线宽度 2。
img.draw_line(20, 20, 100, 20, color = (255, 0, 0), thickness = 2)

#画矩形:绿色不填充。
img.draw_rectangle(150, 20, 100, 30, color = (0, 255, 0),
thickness = 2, fill = False)

#画圆:蓝色不填充。
img.draw_circle(60, 120, 30, color = (0, 0, 255), thickness = 2,
fill = False)

#画箭头:白色。
img.draw_arrow(150, 120, 250, 120, color = (255, 255, 255), size =
20, thickness = 2)

#画十字交叉。
img.draw_cross(60, 200, color = (255, 255, 255), size = 20,
thickness = 2)

#写字符。
img.draw_string(150, 200, "Hello 01Studio!", color = (255, 255,
255), scale = 2,mono_space = False)

lcd.display(img) # Display on LCD
print(clock.fps()) # Note: MaixPy's Cam runs about half as fast when connected
# to the IDE. The FPS should increase once disconnected

 

颜色识别

我们活在一个色彩斑斓的世界里。本节我们来学习机器视觉中的颜色识别。我们会预先设定颜色阈值,如红、绿、蓝。这样 K210 摄像头采集图像后就能自动识别了。

 

通过编程实现 pyAI-K210 识别程序预先设定的颜色色块,分别是红、绿、蓝三种颜色。

MaixPy 集成了 RGB565 颜色块识别 find_blobs 函数,主要是基于 LAB 颜色模型(每个颜色都是用一组 LAB 阈值表示,有兴趣的用户可以自行查阅相关模型资料)。其位于 image 模块下,因此我们直接将拍摄到的图片进行处理即可,那么我们像以往一样像看一下本实验相关对象和函数说明,具体如下:

构造函数

1
image.find_blobs(thresholds[,invert=False[,roi[,x_stride=2[,y_stride=1[,area_threshold=10[,pixels_threshold=10[,merge=False[,margin=0[, threshold_cb=None[, merge_cb=None]]]]]]]]]])

查找图像中指定的色块。返回 image.blog 对象列表;

【thresholds】 必须是元组列表。 [(lo, hi), (lo, hi), …, (lo, hi)] 定义你想追踪的颜

色范围。 对于灰度图像,每个元组需要包含两个值 - 最小灰度值和最大灰

度值。 仅考虑落在这些阈值之间的像素区域。 对于 RGB565 图像,每个元

组需要有六个值(l_lo,l_hi,a_lo,a_hi,b_lo,b_hi) - 分别是 LAB L,A 和 B

通道的最小值和最大值。

【area_threshold】若色块的边界框区域小于此参数值,则会被过滤掉;

【pixels_threshold】若色块的像素数量小于此参数值,则会被过滤掉;

【merge】若为 True,则合并所有没有被过滤的色块;

【margin】调整合并色块的边缘。

使用方法

以上函数返回 image.blob。

1
blob.rect()

返回一个矩形元组(x,y,w,h),如色块边界。可以通过索引[0-3]来获得这些值。

1
blob.cx()

返回色块(int)的中心 x 位置。可以通过索引[5]来获得这个值。

1
blob.cy()

返回色块(int)的中心 y 位置。可以通过索引[6]来获得这个值。

*更多使用说明请阅读官方文档:

https://wiki.sipeed.com/soft/maixpy/zh/api_reference/machine_vision/image/image.html

了解了找色块函数应用方法后,我们可以理清一下编程思路,代码编写流程如下:

参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import sensor,lcd,time
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) #后置模式,所见即所得
#lcd 初始化
lcd.init()
clock=time.clock()
# 颜色识别阈值 (L Min, L Max, A Min, A Max, B Min, B Max) LAB 模型
# 下面的阈值元组是用来识别 红、绿、蓝三种颜色,当然你也可以调整让识别变得更好。
thresholds = [(30, 100, 15, 127, 15, 127), # 红色阈值
(30, 100, -64, -8, -32, 32), # 绿色阈值
(0, 30, 0, 64, -128, -20)] # 蓝色阈值
while True:

clock.tick()

img=sensor.snapshot()

blobs = img.find_blobs([thresholds[2]]) # 0,1,2 分别表示红,绿,蓝色。
if blobs:
for b in blobs:
tmp=img.draw_rectangle(b[0:4])
tmp=img.draw_cross(b[5], b[6])

lcd.display(img) #LCD 显示图片
print(clock.fps()) #打印 FPS

在 IDE 中运行代码,代码默认检测的是蓝色,用户可以自行修改 find_blobs()参数的阈值数组编号来切换识别颜色

 

二维码识别

相信大家都知道二维码了,特别是在扫描支付越来越流行的今天,二维码的应用非常广泛。今天我们就来学习如何使用 pyAI-K210 开发套件实现二维码信息识别

而对于 pyAI-K210 而言,直接使用 MicroPython 中的 find_qrcodes()即可获取摄像头采集图像中二维码的相关信息。具体说明如下:

构造函数

1
image.find_qrcodes([roi])

查找 roi 区域内的所有二维码并返回一个 image.qrcode 的对象列表。

使用方法

以上函数返回 image.qrcode 对象列表。

qrcode.rect()

返回一个矩形元组(

x,y,w,h);

qrcode.payload()

返回二维码字符串信息。可以通过索引[4]来获得这个值。

qrcode.verison()

返回二维码版本号。

*更多使用说明请阅读官方文档:

https://wiki.sipeed.com/soft/maixpy/zh/api_reference/machine_vision/image/image.html#image.find_qrcodes%28%5Broi%5D%29

 

从上表可以看到,使用 MicroPython 编程我们只需要简单地调find_qrcodes()函数,对得到的结果再进行处理即可,非常方便。代码编写流程如下图所示:

参考代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sensor,lcd,time
#摄像头模块初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) #后置模式
sensor.skip_frames(30)
#lcd 初始化
lcd.init()
clock = time.clock()
while True:
clock.tick()
img = sensor.snapshot()
res = img.find_qrcodes() #寻找二维码
if len(res) > 0: #在图片和终端显示二维码信息
img.draw_rectangle(res[0].rect())
img.draw_string(2,2, res[0].payload(), color=(0,128,0), scale=2)
print(res[0].payload())
lcd.display(img)
print(clock.fps())

人脸识别

pyAI-K210 开发套件,配 SD 卡放模型文件。

我们来简单介绍一下 K210 的 KPU。KPU 是 K210 内部一个神经网络处理器,它可以在低功耗的情况下实现卷积神经网络计算,实时获取被检测目标的大小、坐标和种类,对人脸或者物体进行检测和分类。

KPU 具备以下几个特点:

➢ 支持主流训练框架按照特定限制规则训练出来的定点化模型

➢ 对网络层数无直接限制,支持每层卷积神经网络参数单独配置,包括输

入输出通道数目、输入输 出行宽列高

➢ 支持两种卷积内核 1x1 和 3x3

➢ 支持任意形式的激活函数

➢ 实时工作时最大支持神经网络参数大小为 5.5MiB 到 5.9MiB

➢ 非实时工作时最大支持网络参数大小为(Flash 容量-软件体积)

简单来说就是 KPU 能加载和运行各种现成的 AI 算法模型,实现各种机器视

觉等功能。

MaixPy 中人脸识别本质是目标检测,主要通过在 K210 的 KPU 上跑 YOLO(You Only Look Once)目标检测算法来实现。我们来看一下 KPU 在 MaixPy 下的用法

构造函数

1
import KPU as kpu

常用的 KPU 模块导入方法。

使用方法

1
kpu.load(offset or file_path)

加载模型。

【offset】模型存放在 flash 的偏移量,如 0x300000;

【file_path】模型在文件系统为文件名,如“xxx.kmodel”

1
kpu.init_yolo2(kpu_net,threshold,nms_value,anchor_num,anchor)

初始化 yolo2 网络;

【kpu_net】kpu 网络对象;

【threshold】概率阈值;

【nms_value】box_iou 门限;

【anchor_num】描点数;

【anchor】描点参数与模型参数一致。

1
kpu.run_yolo2(kpu_net,image)

运行 yolo2 网络;

【kpu_net】从 kpu_load()中返回的网络对象;

【image】从 sensor 中采集到的图像

1
kpu.deinit(kpu_net)

反初始化。

【kpu_net】kpu 网络对象;

 

从上表可以看到通过 KPU 模块直接加载 YOLO2 网络,再结合人脸检测模型

来实现人脸识别。具体编程思路如下:

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import sensor,lcd,time
import KPU as kpu
#设置摄像头
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
#sensor.set_vflip(1) #设置摄像头后置
lcd.init() #LCD 初始化
clock = time.clock()
#需要将模型(face.kfpkg)烧写到 flash 的 0x300000 位置
#task = kpu.load(0x300000)
#将模型放在 SD 卡中。
task = kpu.load("/sd/facedetect.kmodel") #模型 SD 卡上
#模型描参数
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437,
6.92275, 6.718375, 9.01025)
#初始化 yolo2 网络
a = kpu.init_yolo2(task, 0.5, 0.3, 5, anchor)
while(True):
clock.tick()
img = sensor.snapshot()
code = kpu.run_yolo2(task, img) #运行 yolo2 网络
#识别到人脸就画矩形表示
if code:
for i in code:
print(i)
b = img.draw_rectangle(i.rect())
#LCD 显示
lcd.display(img)

print(clock.fps()) #打印 FPS

有了代码后我们还需要将模型放在文件系统中。这里介绍 2 个方法:

方法一:将模型放在 SD 卡中。

在本节示例程序路径中可以看到有 1 个件夹 face_model_at_0x300000将里面的 facedetect.kmodel 文件移动到 SD 卡,运行上述代码即可。

方法二:将模型烧录到 K210 Flash 中。

打开本节示例程序路径的件夹face_model_at_0x300000里面的flash-list.json文件,内容如下(告诉烧录软件烧写地址和文件名):

1
2
3
4
5
6
7
8
9
10
{
"version": "0.1.0",
"files": [
{
"address": 0x00300000,
"bin": "facedetect.kmodel",
"sha256Prefix": false
}
]
}

接下来直接将 kmodel json 这两个文件用 zip 方式压缩(不要用文件夹),然后将 zip 后缀名改成 kfpkg,得到一个可以用 K210 固件烧录工具烧录的文件。

再使用 K210 固件烧录工具烧录直接烧录该文件即可,烧录软件会根据上述的 json 文件自动调整烧录地址,无需再次填写。

当我们去识别图片时候,可以将摄像头设置成后置,sensor 初始化时增加以

下代码:(LCD 装在 pyAI-K210 核心板背面,横屏测试。)

1
sensor.set_vflip(1) #设置摄像头后置

 

 

物体识别

在上一节人脸检测章节我们已经介绍过 KPU 的用法,这里不再重复。本实验还是使用到 YOLO2 网络,结合 20class 模型(20 种物体分类模型)来识别图像中的物体。下面重温一下 KPU 的用法

构造函数

1
import KPU as kpu

常用的 KPU 模块导入方法。

使用方法

1
kpu.load(offset or file_path)

加载模型。

【offset】模型存放在 flash 的偏移量,如 0x300000;

【file_path】模型在文件系统为文件名,如“xxx.kmodel”

1
kpu.init_yolo2(kpu_net,threshold,nms_value,anchor_num,anchor)

初始化 yolo2 网络;

【kpu_net】kpu 网络对象;

【threshold】概率阈值;

【nms_value】box_iou 门限;

【anchor_num】描点数;

【anchor】描点参数与模型参数一致。

1
kpu.run_yolo2(kpu_net,image)

运行 yolo2 网络;

【kpu_net】从 kpu_load()中返回的网络对象;

【image】从 sensor 中采集到的图像

1
kpu.deinit(kpu_net)

反初始化。

【kpu_net】kpu 网络对象;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import sensor,image,lcd,time
import KPU as kpu
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) #摄像头后置方式
lcd.init() #LCD 初始化
clock = time.clock()
#模型分类,按照 20class 顺序
classes = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus',
'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse',
'motorbike', 'person', 'pottedplant', 'sheep', 'sofa',
'train', 'tvmonitor']
#下面语句需要将模型(20class.kfpkg)烧写到 flash 的 0x500000 位置
#task = kpu.load(0x500000)
#将模型放在 SD 卡中。
task = kpu.load("/sd/20class.kmodel") #模型 SD 卡上
#网络参数
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437,
6.92275, 6.718375, 9.01025)
#初始化 yolo2 网络,识别可信概率为 0.7(70%)
a = kpu.init_yolo2(task, 0.7, 0.3, 5, anchor)
while(True):
clock.tick()
img = sensor.snapshot()
code = kpu.run_yolo2(task, img) #运行 yolo2 网络
if code:
for i in code:
a=img.draw_rectangle(i.rect())
a = lcd.display(img)
lcd.draw_string(i.x(), i.y(),classes[i.classid()],lcd.RED, lcd.WHITE)
lcd.draw_string(i.x(), i.y()+12,'%f1.3'%i.value(),lcd.RED,lcd.WHITE)
else:
a = lcd.display(img)
print(clock.fps()) #打印 FPS

 

在线训练模型

当我们想自己学习识别自

己的东西(比如键盘和鼠标的区分),就可以通过在线训练平台训练自己的模型。

在线训练上有完整的教程,这里不再重复

MaixHub 在线训练链接:https://maixhub.com/

当然上面也有很多现成别人训练好的模型可以直接使用,在模型库中选择

nncase 即可以看到适合 K210 使用的模型

 

图片拍摄

我们在前面摄像头应用章节已经学习过拍摄是使用 image=sensor.snapshot()函数模块,那么我们只需要学会将图片保存即可。保存也是可以直接使用 image下的 save 模块,具体如下:

构造函数

1
img=sensor.snapshot()

通过拍摄创建图像 img

使用方法

1
image.save(*path*[, *roi*[, *quality=50*]])

保存图片。

path:保存路径;

roi:指定保存区域(x, y, w, h),默认全图保存;

quality:仅针对 JPEG 格式的质量控制,有效值为 0-100。

 

掌握了拍照和保存功能,我们就可以编程实现了,例程编程代码流程图如下:

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sensor, lcd, image
from Maix import GPIO
from fpioa_manager import fm
#配置 LED 蓝、红引脚
fm.register(12, fm.fpioa.GPIO0,force=True)
fm.register(14, fm.fpioa.GPIO1,force=True)
LED_B = GPIO(GPIO.GPIO0, GPIO.OUT,value=1) #构建 LED 对象
LED_R = GPIO(GPIO.GPIO1, GPIO.OUT,value=1) #构建 LED 对象
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.skip_frames(40)
lcd.init() #LCD 初始化
#红灯亮提示拍照开始
LED_R.value(0)
sensor.skip_frames(time = 2000) # 给 2 秒时间用户准备.
LED_R.value(1)
#蓝灯亮提示正在拍照
LED_B.value(0)
print("You're on camera!")
# 拍摄并保存相关文件,也可以用"example.bmp"或其它文件方式。
sensor.snapshot().save("/sd/example.jpg")
LED_B.value(1) #l 蓝灯灭提示拍照完成
lcd.display(image.Image("/sd/example.jpg")) #LCD 显示照片

 

视频录制

pyAI-K210 使用的 MaixPy 集成了 vedio 视频模块,也就是通过 MicroPython编程可以轻松实现录制视频功能,我们来看看 vedio 对象:

构造函数

1
2
3
import vedio

v=vedio.open((path, record=False, interval=100000, quality=50,width=320, height=240, audio=False, sample_rate=44100, channels=1))

播放或录制视频文件。

【path】文件路径,比如:/sd/badapple.avi;

【record】=True 表示视频录制,=False 表示视频播放;

【interval】录制帧间隔,单位是微妙;FPS=1000000/interval,默认值

是 100000,即 FPS 默认是 10(每秒 10 帧);

【quality】jpeg 压缩质量(%),默认 50;

【width】录制屏幕宽度,默认 320;

【height】录制屏幕高度,默认 240;

【audio】是否录制音频,默认 False;

【sample_rate】录制音频采样率,默认 44100(44.1k);

【channels】录制音频声道数,默认 1,即单声道。

使用方法

1
v.play()

播放视频;

1
v.volume([value])

设置音量值。

【value】0-100;

1
v.revord ()

录制音视频;

1
v.revord_finish ()

停止录制;

*更多使用说明请阅读官方文档:

https://maixpy.sipeed.com/zh/libs/machine_vision/video.html

 

学习了 vedio 的相关用法后,我们整理思路,代码编写流程如下:

参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import video, sensor, image, lcd, time
#摄像头初始化
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_vflip(1) #后置拍摄模式
sensor.skip_frames(30)
#LCD 初始化
lcd.init()
#指定录制文件路径和文件名
v = video.open("/sd/example.avi", record=1)
i = 0 #计算录制帧数
while True:
tim = time.ticks_ms()
img = sensor.snapshot()
lcd.display(img)
img_len = v.record(img) #img_len 为返回的录制帧长度。
print("record",time.ticks_ms() - tim) #打印录制的每帧间隔
#录制 100 帧,每帧默认 100ms,即 10 秒视频。
i += 1
if i > 100:
break
v.record_finish() #停止录制
print("finish") #录制完成提示

人脸识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 
import sensor
import image
import lcd
import KPU as kpu
import time
from Maix import FPIOA, GPIO
import gc
from fpioa_manager import fm
from board import board_info
import utime

# task_fd = kpu.load(0x300000)
# task_ld = kpu.load(0x400000)
# task_fe = kpu.load(0x500000)

task_fd = kpu.load("/sd/FaceDetection.smodel")
task_ld = kpu.load("/sd/FaceLandmarkDetection.smodel")
task_fe = kpu.load("/sd/FeatureExtraction.smodel")

clock = time.clock()

fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0)
key_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN)
start_processing = False

BOUNCE_PROTECTION = 50


def set_key_state(*_):
global start_processing
start_processing = True
utime.sleep_ms(BOUNCE_PROTECTION)


key_gpio.irq(set_key_state, GPIO.IRQ_RISING, GPIO.WAKEUP_NOT_SUPPORT)

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_hmirror(1)
sensor.set_vflip(1)
sensor.run(1)
anchor = (1.889, 2.5245, 2.9465, 3.94056, 3.99987, 5.3658, 5.155437,
6.92275, 6.718375, 9.01025) # anchor for face detect
dst_point = [(44, 59), (84, 59), (64, 82), (47, 105),
(81, 105)] # standard face key point position
a = kpu.init_yolo2(task_fd, 0.5, 0.3, 5, anchor)
img_lcd = image.Image()
img_face = image.Image(size=(128, 128))
a = img_face.pix_to_ai()
record_ftr = []
record_ftrs = []
names = ['Mr.1', 'Mr.2', 'Mr.3', 'Mr.4', 'Mr.5',
'Mr.6', 'Mr.7', 'Mr.8', 'Mr.9', 'Mr.10']

ACCURACY = 85

while (1):
img = sensor.snapshot()
clock.tick()
code = kpu.run_yolo2(task_fd, img)
if code:
for i in code:
# Cut face and resize to 128x128
a = img.draw_rectangle(i.rect())
face_cut = img.cut(i.x(), i.y(), i.w(), i.h())
face_cut_128 = face_cut.resize(128, 128)
a = face_cut_128.pix_to_ai()
# a = img.draw_image(face_cut_128, (0,0))
# Landmark for face 5 points
fmap = kpu.forward(task_ld, face_cut_128)
plist = fmap[:]
le = (i.x() + int(plist[0] * i.w() - 10), i.y() + int(plist[1] * i.h()))
re = (i.x() + int(plist[2] * i.w()), i.y() + int(plist[3] * i.h()))
nose = (i.x() + int(plist[4] * i.w()), i.y() + int(plist[5] * i.h()))
lm = (i.x() + int(plist[6] * i.w()), i.y() + int(plist[7] * i.h()))
rm = (i.x() + int(plist[8] * i.w()), i.y() + int(plist[9] * i.h()))
a = img.draw_circle(le[0], le[1], 4)
a = img.draw_circle(re[0], re[1], 4)
a = img.draw_circle(nose[0], nose[1], 4)
a = img.draw_circle(lm[0], lm[1], 4)
a = img.draw_circle(rm[0], rm[1], 4)
# align face to standard position
src_point = [le, re, nose, lm, rm]
T = image.get_affine_transform(src_point, dst_point)
a = image.warp_affine_ai(img, img_face, T)
a = img_face.ai_to_pix()
# a = img.draw_image(img_face, (128,0))
del (face_cut_128)
# calculate face feature vector
fmap = kpu.forward(task_fe, img_face)
feature = kpu.face_encode(fmap[:])
reg_flag = False
scores = []
for j in range(len(record_ftrs)):
score = kpu.face_compare(record_ftrs[j], feature)
scores.append(score)
max_score = 0
index = 0
for k in range(len(scores)):
if max_score < scores[k]:
max_score = scores[k]
index = k
if max_score > ACCURACY:
a = img.draw_string(i.x(), i.y(), ("%s :%2.1f" % (
names[index], max_score)), color=(0, 255, 0), scale=2)
else:
a = img.draw_string(i.x(), i.y(), ("X :%2.1f" % (
max_score)), color=(255, 0, 0), scale=2)
if start_processing:
record_ftr = feature
record_ftrs.append(record_ftr)
start_processing = False

break
fps = clock.fps()
print("%2.1f fps" % fps)
a = lcd.display(img)
gc.collect()
# kpu.memtest()

# a = kpu.deinit(task_fe)
# a = kpu.deinit(task_ld)
# a = kpu.deinit(task_fd)