本文最后更新于2022年6月4日,已超过 60 天没有更新,如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

MaixHub是MaixPy官方提供的在线训练k210模型的平台(目前只支持YOLOv2的模型),MaixHub可以在线训练模型,在本地只需要上传训练集数据(需限制大小为20M以下),等待云端训练完成就能下载到训练好的模型。下面为制作训练集的步骤。

MaixPy 是将 Micropython 移植到 K210(一款 64 位双核带硬件 FPU、卷积加速器、FFT、Sha256 的 RISC-V CPU ) 的一个项目; MaixPy 不但支持 MCU 常规操作, 还集成了硬件加速的 AI 机器视觉和麦克风阵列相关的算法。相应的高达 1TOPS 算力核心模块却不到¥50, 凭借着快速开发和较低成本与其较小的体积很适合 AIOT 领域智能应用。

1.图片采集及预处理

使用手机等拍照设备采集

这种方式采集到的图片可能不是 224x224分辨率,可以使用Image_tool软件(下载)自动裁剪,在Image_tool中选中需要处理的图片目录即可,Image_tool会自动裁剪出中心的物体,输出格式为224x244的图像。

imge_tool

使用k210开发版采集

在k210中执行下面拍照功能的脚本 ,用k210作为相机,这样获取的图片为224x224不需要做预处理

import sensor, lcd
from Maix import GPIO
from fpioa_manager import fm
from board import board_info
import os, sys
import time
import image

#### image size ####
set_windowing = (224, 224)       

#### sensor config ####

sensor.reset(freq=22000000, dual_buff=False)
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA) # 320x240
try:
    sensor.set_jb_quality(95)         # for IDE display quality
except Exception:
    pass # no IDE support
if set_windowing:
    sensor.set_windowing(set_windowing)
# sensor.set_auto_gain(False)
# sensor.set_auto_whitebal(False, rgb_gain_db=(0x52,0x40,0x4d))
# sensor.set_saturation(0)
# sensor.set_brightness(4)
# sensor.set_contrast(0)
# sensor.set_hmirror(True)        # image horizonal mirror
# sensor.set_vflip(True)          # image vertical flip
# sensor.set_auto_whitebal(False)

sensor.skip_frames()

#### lcd config ####
lcd.init(type=1, freq=15000000)
lcd.rotation(2)

#### boot key ####
boot_pin = 16 # board_info.BOOT_KEY
fm.register(boot_pin, fm.fpioa.GPIOHS0)
key = GPIO(GPIO.GPIOHS0, GPIO.PULL_UP)

######################################################

#### main ####
def capture_main(key):
    def draw_string(img, x, y, text, color, scale, bg=None , full_w = False):
        if bg:
            if full_w:
                full_w = img.width()
            else:
                full_w = len(text)*8*scale+4
            img.draw_rectangle(x-2,y-2, full_w, 16*scale, fill=True, color=bg)
        img = img.draw_string(x, y, text, color=color,scale=scale)
        return img

    def del_all_images():
        os.chdir("/sd")
        images_dir = "cap_images"
        if images_dir in os.listdir():
            os.chdir(images_dir)
            types = os.listdir()
            for t in types:
                os.chdir(t)
                files = os.listdir()
                for f in files:
                    os.remove(f)
                os.chdir("..")
                os.rmdir(t)
            os.chdir("..")
            os.rmdir(images_dir)

    # del_all_images()
    os.chdir("/sd")
    dirs = os.listdir()
    images_dir = "cap_images"
    last_dir = 0
    for d in dirs:
        if d.startswith(images_dir):
            if len(d) > 11:
                n = int(d[11:])
                if n > last_dir:
                    last_dir = n
    images_dir = "{}_{}".format(images_dir, last_dir+1)
    print("save to ", images_dir)
    if images_dir in os.listdir():
        img = image.Image()
        img = draw_string(img, 2, 200, "please del cap_images dir", color=lcd.WHITE,scale=1, bg=lcd.RED)
        lcd.display(img)
        sys.exit(1)
    os.mkdir(images_dir)
    last_cap_time = 0
    last_btn_status = 1
    save_dir = 0
    save_count = 0
    os.mkdir("{}/{}".format(images_dir, save_dir))
    while(True):
        img0 = sensor.snapshot()
        if set_windowing:
            img = image.Image()
            img = img.draw_image(img0, (img.width() - set_windowing[0])//2, img.height() - set_windowing[1])
        else:
            img = img0.copy()
        # img = img.resize(320, 240)
        if key.value() == 0:
            time.sleep_ms(30)
            if key.value() == 0 and (last_btn_status == 1) and (time.ticks_ms() - last_cap_time > 500):
                last_btn_status = 0
                last_cap_time = time.ticks_ms()
            else:
                if time.ticks_ms() - last_cap_time > 5000:
                    img = draw_string(img, 2, 200, "release to change type", color=lcd.WHITE,scale=1, bg=lcd.RED)
                else:
                    img = draw_string(img, 2, 200, "release to capture", color=lcd.WHITE,scale=1, bg=lcd.RED)
                    if time.ticks_ms() - last_cap_time > 2000:
                        img = draw_string(img, 2, 160, "keep push to change type", color=lcd.WHITE,scale=1, bg=lcd.RED)
        else:
            time.sleep_ms(30)
            if key.value() == 1 and (last_btn_status == 0):
                if time.ticks_ms() - last_cap_time > 5000:
                    img = draw_string(img, 2, 200, "change object type", color=lcd.WHITE,scale=1, bg=lcd.RED)
                    lcd.display(img)
                    time.sleep_ms(1000)
                    save_dir += 1
                    save_count = 0
                    dir_name = "{}/{}".format(images_dir, save_dir)
                    os.mkdir(dir_name)
                else:
                    draw_string(img, 2, 200, "capture image {}".format(save_count), color=lcd.WHITE,scale=1, bg=lcd.RED)
                    lcd.display(img)
                    f_name = "{}/{}/{}.jpg".format(images_dir, save_dir, save_count)
                    img0.save(f_name, quality=95)
                    save_count += 1
                last_btn_status = 1
        img = draw_string(img, 2, 0, "will save to {}/{}/{}.jpg".format(images_dir, save_dir, save_count), color=lcd.WHITE,scale=1, bg=lcd.RED, full_w=True)
        lcd.display(img)
        del img
        del img0

def main():
    try:
        capture_main(key)
    except Exception as e:
        print("error:", e)
        import uio
        s = uio.StringIO()
        sys.print_exception(e, s)
        s = s.getvalue()
        img = image.Image()
        img.draw_string(0, 0, s)
        lcd.display(img)
main()

2.标注数据集

maixhub提供两种模型类别

  • 物体分类:区分物体的类别,无位置检测
  • 物体检测:检测物体的位置,不进行分类

这里使用的是物体检测,可以使用labelImg工具(下载)进行标注,注意最终要生成下面的目录结构,每个xml文件与images文件一一对应。

----images
|        |
|        |---0.jpg
|        |
|         ---1.jpg
|
 ------xml
         |---0.xml
         |
         |---1.xml

或者两级目录

images
 |
     ----ball
    |        |
    |         ---0.jpg
    |        |
    |         ---1.jpg
    |
     ----toy
    |       |
    |        ---0.jpg
     ---pic0.jpg
xml
  |
     ----ball
    |        |
    |         ---0.xml
    |        |
    |         ---1.xml
    |
     ----toy
    |       |
    |        ---0.xml
     ----pic0.xml

标记图片过程演示,Open Dir 选中images目录下要标记的图片所在目录,Change Save Dir选中对应xml目录下与上步要标记图片目录同名的目录(没有的话就创建),用快捷键W绘制识别区域并选一个标签,D切换下一张图片。

labelImageUsage-min

【注意】:操作的路径不能有中文

【tips】:labelImg可以配置成自动保存,使用W键绘制区域,D键切换下一张图片

3.打包数据集

把上面的图片和标签按下面结构打包成zip文件,单级别目录结构

datasets.zip
            |
            datasets
                    |
                     ----images
                    |        |
                    |        |---0.jpg
                    |        |
                    |         ---1.jpg
                    |
                    |------xml
                    |        |---0.xml
                    |        |
                    |        |---1.xml
                     ----labels.txt

或者两级目录结构

datasets.zip
          |
           datasets
                  |
                  |---images
                  |      |
                  |       ----ball
                  |      |       |
                  |      |        ---0.jpg
                  |      |       |
                  |      |        ---1.jpg
                  |      |
                  |       ----toy
                  |      |      |
                  |      |       ---0.jpg
                  |       ---pic0.jpg
                  |---xml
                  |     |
                  |      ----ball
                  |     |        |
                  |     |         ---0.xml
                  |     |        |
                  |     |         ---1.xml
                  |     |
                  |      ----toy
                  |     |       |
                  |     |        ---0.xml
                  |      ----pic0.xml
                   --labels.txt

其中的labels.txt存放分类的标签

left
right
stop
straight

4.上传数据集进行训练

https://www.maixhub.com/mtrain.html 创建训练任务,其中机器码的获取方式(传送门

image-20220604123210777

上传数据集时需要选中上面步骤中做好的zip文件,然后提交训练任务即可

5.运行模型

模型训练好之后可以在个人中心下载到文件包

image-20220604124401859

文件包内的文件说明:

  • boot.py: 在 maixpy 上运行的代码
  • .kmodel 或者 .smodel: 训练好的模型文件( smodel 是加密模型 )
  • labels.txt: 分类标签
  • startup.jpg: 启动图标
  • report.jpg: 训练报告,包括了损失和准确度报告等
  • warning.txt: 训练警告信息,如果有这个文件,务必阅读, 里面的原因可能会导致训练精度低

使用方法:

  1. 按照文档(maixpy.sipeed.com)更新到最新的固件
    如果新固件出现bug,可以使用这个固件测试(选择minimum_with_ide_support.bin):
    https://api.dl.sipeed.com/shareURL/MAIX/MaixPy/release/master/maixpy_v0.6.0_2_g9720594
  2. 准备一张 SD 卡, 将本目录下的文件拷贝到 SD 卡根目录
  3. SD 卡插入开发板
  4. 开发板上电启动
  5. 摄像头对准训练的物体,
    屏幕左上角会显示 物体标签 和 概率
    屏幕左下角会显示 运行模型消耗的时间,单位是毫秒

有帮助的话请打个赏吧!