本文介绍了一种基于嵌入式平台开发的图像识别部署装置,其主要功能包括实现机器与人的“猜拳博弈”,其组成分为三个部分:
手势检测数据集
图像识别模型训练
模型格式部署
maixbit开发板部署
手势检测数据集:本项目的数据集包括三种标签:石头,剪刀,布。数据集采用的是自己采集,自行采集数据集有两个优点:1.对最终的部署于maixbit开发板进行识别更加具有针对性;2.可以任意控制数据集的数量。
如上图所示:maixpy提供了线上的数据集制作平台,进行训练集以及验证集的编写,通过手动标注数据集,由于本项目中所使用的是图像分类的识别模型,不需要进行手动标注。
数据集的样本采集方式使用手机端进行直接的训练样本采集,上传至maixHub的后端服务器,图像样本以及对应的标签。
选取模型进行模型的训练,使用的分类模型为mobilenet,进行数据集的拟合。
踩坑经验:数据集三种标签的样本采集数量应该严格相等,同时尽可能数量多,不低于80张采集样本图片。模型选择方面,仅支持官方推荐的几种模型,也是因为芯片的RAM较小,并且其中固件占据了太多部分的内存。
选择部署方式,本项目最终部署的平台是maixBit开发板,因此适用于nncase的部署方式,如果选择部署于树莓派与安卓平台时,可以选择ncnn的部署方式。
配置项选择全部结束,创建训练任务,日志平台进行训练日志的监控,主要包括损失函数loss的下降趋势监控,以及模型acc准确率的显示等,训练完成的模型进行终端部署相关操作。
maixhub帮助我们实现了一个初步的推理代码,实现部署直接应用模型,即可实现调用maixbit开发进行模型的图像识别。
import sensor, image, lcd, time
import KPU as kpu
import gc, sys
input_size = (224, 224)
labels = ['cloth', 'Scissors', 'Stone']
def lcd_show_except(e):
import uio
err_str = uio.StringIO()
sys.print_exception(e, err_str)
err_str = err_str.getvalue()
img = image.Image(size=input_size)
img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
lcd.display(img)
def main(labels = None, model_addr="/sd/m.kmodel", sensor_window=input_size, lcd_rotation=0, sensor_hmirror=False, sensor_vflip=False):
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing(sensor_window)
sensor.set_hmirror(sensor_hmirror)
sensor.set_vflip(sensor_vflip)
sensor.run(1)
lcd.init(type=1)
lcd.rotation(lcd_rotation)
lcd.clear(lcd.WHITE)
if not labels:
with open('labels.txt','r') as f:
exec(f.read())
if not labels:
print("no labels.txt")
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "no labels.txt", color=(255, 0, 0), scale=2)
lcd.display(img)
return 1
try:
img = image.Image("startup.jpg")
lcd.display(img)
except Exception:
img = image.Image(size=(320, 240))
img.draw_string(90, 110, "loading model...", color=(255, 255, 255), scale=2)
lcd.display(img)
try:
task = None
task = kpu.load(model_addr)
while(True):
img = sensor.snapshot()
t = time.ticks_ms()
fmap = kpu.forward(task, img)
t = time.ticks_ms() - t
plist=fmap[:]
pmax=max(plist)
max_index=plist.index(pmax)
img.draw_string(0,0, "%.2f : %s" %(pmax, labels[max_index].strip()), scale=2, color=(255, 0, 0))
img.draw_string(0, 200, "t:%dms" %(t), scale=2, color=(255, 0, 0))
lcd.display(img)
except Exception as e:
raise e
finally:
if not task is None:
kpu.deinit(task)
if __name__ == "__main__":
try:
# main(labels=labels, model_addr=0x300000)
main(labels=labels, model_addr="/sd/model-26548.kmodel")
except Exception as e:
sys.print_exception(e)
lcd_show_except(e)
finally:
gc.collect()
maixbit开发使用摄像头以及LCD屏幕进行图像的结果显示,包括实时视频检测的结果以及模型识别的推理时间,进行显示。
maix bit主板示意图如下图所示:
maix bit开发板的内部包含KPU神经网络处理器,类似华为的NPU芯片,可以加快模型的推理速度。
目前手上有可以识别石头剪刀布三种手势的模型设备,需要进行人机交互层面的实现工作。
选取上电运行的主界面图片:
具体需要实现的交互功能为根据人的手势,识别其具体的手势类别,再做出相应的手势反应。
主界面的上电运行图片进行实际的切分,分为剪刀图片,石头图片以及布图片:
图片进行显示预处理,maix bit开发板的显示屏采用的LCD液晶显示屏,其如下图所示:
其显示屏的参数对于项目的区别在于320x240的视频显示分辨率,因此其主界面的图片显示应调整分辨率为320x240的图像分辨率,这里使用的是画图软件,进行图片大小的调整。
部署模型Python代码如下所示:
import sensor, image, lcd, time
import KPU as kpu
import gc, sys
input_size = (224, 224)
labels = ['cloth', 'Scissors', 'Stone']
cloth_pic = "/sd/bu.jpg"
Scissors_pic = "/sd/jian.jpg"
Stone_pic = "/sd/shi.jpg"
def lcd_show_except(e):
import uio
err_str = uio.StringIO()
sys.print_exception(e, err_str)
err_str = err_str.getvalue()
img = image.Image(size=input_size)
img.draw_string(0, 10, err_str, scale=1, color=(0xff,0x00,0x00))
lcd.display(img)
def main(labels = None, model_addr="", sensor_window=input_size, lcd_rotation=0, sensor_hmirror=False, sensor_vflip=False):
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing(sensor_window)
sensor.set_hmirror(sensor_hmirror)
sensor.set_vflip(sensor_vflip)
sensor.run(1)
lcd.init(type=1)
lcd.rotation(lcd_rotation)
lcd.clear(lcd.WHITE)
try:
img = image.Image("/sd/start.jpg")
lcd.display(img)
time.sleep(2)
lcd.clear()
img = image.Image(size=(320, 240))
img.draw_string(80, 110, "Mora Guess Game", color=(255, 255, 255), scale=2)
lcd.display(img)
time.sleep(2)
except Exception:
img = image.Image(size=(320, 240))
img.draw_string(50, 50, "model exception...", color=(255, 255, 255), scale=2)
lcd.display(img)
try:
task = None
task = kpu.load(model_addr)
lcd.clear()
while(True):
img = sensor.snapshot()
t = time.ticks_ms()
if img is None or img == "":
continue
fmap = kpu.forward(task, img)
t = time.ticks_ms() - t
fps = 1000/t
plist=fmap[:]
pmax=max(plist)
max_index=plist.index(pmax);
img.draw_string(0,0, "%.2f: %s" %(pmax,labels[max_index].strip()), scale=2, color=(000, 0,255))
img.draw_string(0, 200, "fps :%.1f" %(fps), scale=2, color=(0, 0, 255))
lcd.display(img,roi=(0, 0, 160, 240), oft=(0, 0))
if max_index == 0:
Scissors = image.Image(Scissors_pic)
lcd.display(Scissors,roi=(0, 0, 160, 240), oft=(160, 0))
elif max_index == 1:
Stone = image.Image(Stone_pic)
lcd.display(Stone,roi=(0, 0, 160, 240), oft=(160, 0))
elif max_index == 2:
cloth = image.Image(cloth_pic)
lcd.display(cloth,roi=(0, 0, 160, 240), oft=(160, 0))
except Exception as e:
raise e
finally:
if not task is None:
kpu.deinit(task)
if __name__ == "__main__":
try:
main(labels=labels, model_addr="/sd/model-26548.kmodel")
except Exception as e:
sys.print_exception(e)
lcd_show_except(e)
finally:
gc.collect()
maix bit可以外接SD卡配置,将进行显示所用的文件为了节约芯片的内存,将LCD屏幕的320x240的屏幕进行一分为二,因此以上三个手势图片分辨率为160x240。
进行视频显示视频流显示与模型处理结果进行分割,针对视频流采集的图片进行预测,进行模型的前向传播,获取各个预测标签对应的概率。获取最大的概率所对应的索引,输出对应的预测标签结果。
博弈操作的逻辑代码如下:
if max_index == 0:
Scissors = image.Image(Scissors_pic)
lcd.display(Scissors,roi=(0, 0, 160, 240), oft=(160, 0))
elif max_index == 1:
Stone = image.Image(Stone_pic)
lcd.display(Stone,roi=(0, 0, 160, 240), oft=(160, 0))
elif max_index == 2:
cloth = image.Image(cloth_pic)
lcd.display(cloth,roi=(0, 0, 160, 240), oft=(160, 0))
将图像识别的标签进行分布为[石头,布,剪刀],通过识别的图片结果,读取sd卡中的图像数据,在半个LCD显示屏进行操作结果的显示,以此类推。
如图为本智能设备的最终识别效果:
以上为MaixBit(K210芯片)的图像识别猜拳手势博弈装置的最终效果,完整源代码通过关注我的公众号“千与编程”,有详细教程。其实这个项目是我一早就很想做的项目,现在也算完整完成了,做一个完整的项目,用编程改变世界加油!
我是千与千寻,我们下期见!