K210 图像识别 (加训练模型)

本文简介K210的图像识别

首先要明确的是图像识别 ,我们需要识别的是什么  ,如何让机器代替我们识别  ,我们人类认识世界万物  知道这个是玫瑰花  这个是豆腐  这个是火车   这个是我们一步一步的学习所得的,当然如何让机器识别出物品  也需要机器学习

关于具体的机器学习  神经网络  不再这里讲解 

本例程所用的硬件环境是最近新出的K210开发板模块     maixpy  

https://maixpy.sipeed.com/zh/course/ai/train/maixhub.html

micropython logo

网站导航 地址
MaixPy 唯一官方文档官网 官网: maixpy.sipeed.com
中文站: cn.maixpy.sipeed.com
MaixPy 例程仓库 github: github/maixpy_script
国内:gitee/maixpy_scripts
MaixPy 源码 github: MaixPy
开发板资料下载 dl.sipeed.com
Sipeed WIKI wiki.sipeed.com

1. 关于 MaixPy

1. 关于 MaixPy

MaixPy 是将 Micropython 移植到 K210(一款 64 位双核带硬件 FPU、卷积加速器、FFT、Sha256 的 RISC-V CPU ) 的一个项目, 支持 MCU 常规操作, 更集成了硬件加速的 AI 机器视觉和麦克风阵列,1TOPS 算力 核心模块却不到¥50, 以快速开发具有极低成本和体积实用的 AIOT 领域智能应用。

MicroPython 是基于 Python3 的语法做的一款解析器,包含了 Python3 的大多数基础语法, 主要运行在性能和内存有限的嵌入式芯片上。(注意 Micropython 不包含 Python3 的所有语法)

MaixPy 让我们在 K210 上编程更加简单快捷, 我们也将源代码开源在 github

使用 MaixPy 可以做很多有趣的事情, 具体可以看这里

2. 简洁的代码实例

比如我们需要扫描 I2C 总线上的设备,不需要复杂的开发环境和工程,只需要通过串口发送如下代码即可实现:

from machine import I2C                          # 导入内置库

i2c = I2C(I2C.I2C0, freq=100000, scl=28, sda=29) # 定义一个I2C对象, 使用I2C0, 频率100kHz,SCL引脚是IO28, SDA 引脚是IO29
devices = i2c.scan()                             # 调用函数扫描设备
print(devices)                                   # 打印设备

同样,我们需要实现一个呼吸灯,只需要如下代码:

from machine import Timer,PWM
from board import board_info
import time

tim = Timer(Timer.TIMER0, Timer.CHANNEL0, mode=Timer.MODE_PWM)
ch = PWM(tim, freq=500000, duty=50, pin=board_info.LED_G)
duty=0
dir = True
while True:
    if dir:
        duty += 10
    else:
        duty -= 10
    if duty>100:
        duty = 100
        dir = False
    elif duty<0:
        duty = 0
        dir = True
    time.sleep(0.05)
    ch.duty(duty)

实时拍照

import sensor
import image
import lcd

lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.run(1)
while True:
    img=sensor.snapshot()
    lcd.display(img)

我们本次例程  就以识别汽车为例

我们要识别汽车要有足够的图片来训练模型   然后将模型装载在K210中

关于模型的训练

我们如何获取汽车的图片    可以从网络上选择下载  这是不方便的,  合理的运用爬虫是最合理的

下面是我下载好的图片

K210训练的模型  需要修改图片的格式  (224*224)

参考Maixhub 模型训练平台使用说明

https://www.maixhub.com/index/mtrain/help.html

采用下面的工具将图片大小修改  (最后都会放上百度网盘链接)

或者

如果想要拍照训练模型这里有拍照脚本 

上面标注的两个脚本文件是  加载在K210进行拍照的    拍下的照片自动保存在内存卡中

#本程序用于使用Maixpy系列开发板进行图像数据收集
#使用方法:
#0.为Maixpy系列开发板插入一张TF卡
#1.将本程序烧入Maixpy系列开发板中,上电运行
#2.阅读用户说明后,按下BOOT按钮进行拍照,LCD屏左下角会显示当前存储目录
#3.拍照后的图片默认存储在TF卡目录./0/下面,拍摄成功后,会在LCD屏正下方显示"OK!"字样
#长按BOOT按钮可切换拍照后图片的默认存储路径(默认支持./1/~./10/,也就是十分类,如有其他需求,请修改Classes_num变量)
#4.完成所有分类的图像采集后,将TF卡中的数据移动到电脑上,并自行按需修改存储图片的文件夹名称(标签名),
#并删除没有用到的标签文件夹
#5.用DataAssitant数据集与处理软件进行处理。
#6.将得到的.zip压缩文件上传MaixHub进行训练。
#PS:
#DataAssitant数据集与处理软件下载地址:
#https://cdn.sipeed.com/donwload/12kj346465hjkv68g7c/DataAssitantV1.0.zip
#数据集收集教程:https://www.maixhub.com/index.php/index/mtrain/help.html
##################################################################################
#This program is used for image data collection using Maixpy ​​series development boards#Instructions:
# 0. Insert a TF card for Maixpy ​​series development board
# 1. Burn the program into the Maixpy ​​series development board and power on
# 2. After reading the user's instructions, press the BOOT button to take a picture,
#    the current storage directory will be displayed in the lower left corner of the LCD screen
# 3. The picture after taking the picture is stored in the TF card directory by default.
#    Long press the BOOT button to switch the default storage path of pictures after taking pictures
#    (default supports ./1/~./10/, which is very class, if you have other needs, please modify the Classes_num variable)
# 4. After completing the collection of all the classified images, move the data in the TF card to the computer,
#    and modify the folder name (tag name) where the pictures are stored as needed,
#    And delete unused tag folders# 5. Use DataAssitant dataset and processing software for processing.
# 6. Upload the obtained .zip compressed file to MaixHub for training.
#PS:
#DataAssitantDataset and processing software download address:
#https: //cdn.sipeed.com/donwload/12kj346465hjkv68g7c/DataAssitantV1.0.zip
#Dataset Collection Tutorial: https://www.maixhub.com/index.php/index/mtrain/help.html
##################################################################################
import sensor, image, time, lcd
import utime
import uos
import sys
from Maix import GPIO
from board import board_info
from fpioa_manager import fm
##################################################################################
Classes_num = 10 #十分类(10个标签文件夹)|Tier 10 (10 tag folders)
##################################################################################
bg = lcd.RED
text = lcd.WHITE
boot_press_flag = 1
start = time.ticks_ms()
end = time.ticks_ms()
ui_num = 0
image_save_path = "/sd/image/"#图片保存目录头
claass = 0#文件夹名
image_num = 0#图像文件保存名
#完整图片保存路径image_save_path+claass+"/"
image_data = image.Image()#图像
shoot_flag = 0
##################################################################################
def boot_key_irq(pin_num):#
    global ui_num
    global boot_press_flag,start,end
    global claass
    global image_num
    global shoot_flag
    #utime.sleep_ms(100)
    if(boot_press_flag == 1):
        start = time.ticks_ms()
        boot_press_flag = 0
    elif(boot_press_flag == 0):
        end = time.ticks_ms()
        boot_press_flag = 1
        time_diff = time.ticks_diff(end, start)
        if(time_diff >120 and time_diff <500):
            print("短按拍摄",time_diff)
            ui_num = 1
            if(ui_num == 1):#已进入拍摄
                image_num = image_num + 1
                #shoot_flag = 1
                image_data.save("/sd/image/"+str(claass)+"/"+str(utime.ticks_us())+str(image_num)+".jpg")
                lcd.draw_string(160, 224,"ok!"+str(image_num))
                utime.sleep_ms(500)
                #print(str(utime.ticks_us()))
        elif(time_diff >=500 and time_diff <=2000):
            print("长按切换文件夹",time_diff)
            if(ui_num == 1):#已进入拍摄
                claass = claass + 1
                if(claass >Classes_num-1):#让保存路径始终有效
                    claass = 0
        elif():
            boot_press_flag = 1
            start = 0
            end = 0
    #print("key", pin_num)

fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0, force=True)
boot_key=GPIO(GPIO.GPIOHS0, GPIO.IN, GPIO.PULL_UP)
boot_key.irq(boot_key_irq, GPIO.IRQ_BOTH, GPIO.WAKEUP_NOT_SUPPORT, 7)
##################################################################################
def draw_help_ui():#显示帮助页面
    lcd.draw_string(60, 10, "Data Collection Assistant", text, bg)
    lcd.draw_string(20, 30, "1.Press the BOOT button to take a pi-", text, bg)
    lcd.draw_string(10, 50, "cture. The current storage directory", text, bg)
    lcd.draw_string(10, 70, "will be displayed in the lower left corner of the LCD screen.", text, bg)
    lcd.draw_string(10, 90, "corner of the LCD screen.", text, bg)

    lcd.draw_string(20, 120, "2.Long press BOOT button to switch ", text, bg)
    lcd.draw_string(10, 140, "the default storage folder path of pi-", text, bg)
    lcd.draw_string(10, 160, "ctures after taking photos.", text, bg)
    lcd.draw_string(10, 200, "--Press the BOOT button to start shoot", text, bg)
##################################################################################
def not_found_tf():#没有找到TF卡
    lcd.clear(bg)
    lcd.draw_string(10, 90, "ERROR: ", text, bg)
    lcd.draw_string(20, 110, "No TF card found", text, bg)
    lcd.draw_string(20, 130, "The Reason:", text, bg)
    lcd.draw_string(20, 150, "1.No TF card inserted", text, bg)
    lcd.draw_string(20, 170, "2.TF card model is not supported", text, bg)
    lcd.draw_string(20, 190, "3.TF card format is not FAT", text, bg)
##################################################################################
def init():#初始化相关
    i = 0
    sensor.reset()
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)
    sensor.skip_frames(time = 2000)
    sensor.set_vflip(1)
    sensor.run(1)
    lcd.init(type=1, freq=15000000, color=bg)
    try:
        uos.mkdir("/sd/image")#创建image文件夹,顺路TF卡检测
        for i in range(Classes_num):
            uos.mkdir("/sd/image/"+str(i))#创建image/1-10文件夹,顺路TF卡检测
            print("/sd/image/"+str(i))
    except Exception as e:
        if(str(e) == "[Errno 17] EEXIST"):
            pass
        else:
            not_found_tf()
            sys.exit(0)
    finally:
        try:
            for i in range(Classes_num):
                uos.mkdir("/sd/image/"+str(i))#创建image/1-10文件夹,顺路TF卡检测
                #print("/sd/image/"+str(i))
                lcd.draw_string(0, 224,str(claass))
        except Exception as e:
            if(str(e) == "[Errno 17] EEXIST"):
                pass
            else:
                not_found_tf()
                sys.exit(0)
    draw_help_ui()
##################################################################################
def image_ui():
    global image_data
    image_data = sensor.snapshot()         # Take a picture and return the image.
    lcd.display(image_data,oft=(0,0))                # Display on LCD
    lcd.draw_string(0, 224,"/sd/image/"+str(claass)+"/")
##################################################################################
def main():#主函数
    init()
    while(True):
        if(ui_num == 1):
            image_ui()

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        print(e)
        lcd.clear(bg)
        lcd.draw_string(10, 90, "ERROR:unknown mistake", text, bg)
        lcd.draw_string(20, 110, "Please contact sipeed for help.", text, bg)

上述拍照的效果并不太好

在电脑上加载摄像仪进行拍照脚本

import cv2
import time
import os

cap = cv2.VideoCapture(1)
cap.set(3,320)
cap.set(4,240)

def snapShotCt():
    # camera_idx的作用是选择摄像头。如果为0则使用内置摄像头,比如笔记本的摄像头,用1或其他的就是切换摄像头。
    # ret, frame = cap.read()  # cao.read()返回两个值,第一个存储一个bool值,表示拍摄成功与否。第二个是当前截取的图片帧。
    count = 100
    while True:

        # 从摄像头读取图片
        success, img = cap.read()
        
        cv2.imwrite("save_pic/" + str(count) + '.jpg', img)
        cv2.imshow('image', img)
        time.sleep(0.5)  # 休眠一秒 可通过这个设置拍摄间隔,类似帧。
        count += 1
        # ret, frame = cap.read()  # 下一个帧图片
        # 保持画面的连续。waitkey方法可以绑定按键保证画面的收放,通过q键退出摄像
        #      k = cv2.waitKey(1)
        #      if k == '27':
        #         break
        # 或者得到800个样本后退出摄像,这里可以根据实际情况修改数据量,实际测试后800张的效果是比较理想的
        if count >= 100:
            break


# 关闭摄像头,释放资源
snapShotCt()
cap.realease()
cv2.destroyAllWindows()
# camera.release()
# cv2.destroyAllWindows()

有了照片  开始标注

vott标注和 labimag

标注400张

labeIimg 标注

标注完之后   打包上传到云端 

注意上传的格式要求 labeIimg

VOTT

三、采集照片

确定了方案,就可以采集照片了, 目标是采集目标分辨率的照片, 以下都以224x224为例, 将他们按照目录进行分类收集, 比如 采集整理好的目录结构:

采集照片有以下几种方式:

  1. 使用开发板采集到SD卡, 直接采集成需要的分辨率 (推荐)

  2. 手机拍照, 然后使用预处理工具处理成需要的分辨率, 注意, 处理完后一定要手动检查数据是否符合要求, 不然可能影响训练精准度

  3. 使用现成的图片, 使用预处理工具处理成需要的分辨率, 注意, 处理完后一定要手动检查数据是否符合要求, 不然可能影响训练精准度

1. 使用开发板进行数据采集

  • 使用 这个 脚本来进行采集图片

  • 按照 图片采集脚本使用说明 采集图片到SD

  • SD卡中的图片拷贝到电脑, 整理成上面的目录结构, 所有图片的分辨率为224x224

  • 注意 SD卡需要硬件支持SPI通信, 并使用MBR(msdos)分区方案,并格式化为FAT32格式

2. 使用其它图片

整理成上面的目录结构, 最后所有图片分辨率已经是224x224, 如果不是, 后面还需要处理

四、预处理数据集为目标分辨率

  • 如果所有图片已经都是224x224的分辨率,本步骤跳过

  • 如果有图片不是224x224, 那么需要先把所有图片处理成224x224, 稍后Maixhub会推出预处理工具

五、 标注数据集

对于不同的训练类型:

目标分类

跳过此步骤

目标检测 标注数据集

注意, 一定要先保证分辨率正确, 再标注

标注有以下两种工具:

  1. vott: 点击这里下载

    • 创建项目, 这里比较麻烦的就是数据输入输出文件夹需要创建一个连接(connection)

    • 然后左边导出设置, 选择TFRecord格式

    • 标注数据

    • 标注完成后需要手动点击导出按钮来导出TFRecord文件

    • 结果文件目录结构, 注意tf_label_map.pbtxt是必须的,vott自动生成的, 不要手动修改

 
  1. labelimg : 点击这里下载
    如下图, 选择图片目录, 以及存放(输出)目录,(左边PascalVOC格式不要改动)按下W按键后, 用鼠标框出图片中的物体, 并且给它给一个标签, 这里使用了ball作为标签(一张图中可以有多个物体)


保存后会生成xml格式的文件(即PascalVOC格式),每张图对应一个xml文件

结果文件: 完成后目录结构和文件如下:

 

或者两级目录

六、 打包数据集

将前面处理好的数据集进行打包, 使用zip压缩,暂不支持其它格式, 而且文件不要超过20MiB

目标分类

一个文件夹一个分类, 分类名(标签/label)就是文件夹名

 

目标检测

  1. vott的输出zip文件结构:

  1. labelimg 的输出zip文件结构

新建了一个labels.txt, 输入标记的标签, 每行一个, 比如这里:

ball

toy

这是必须的, 否则数据无效

然后目录结构如下

 

或者两级labelimg输出

 

七、上传数据集进行训练

https://www.maixhub.com/mtrain.html 创建训练任务

  • 选择训练类型

  • 填写邮箱地址, 用于接收结果, 包括成功(模型等文件) 和 失败 (失败原因)结果

  • 如果要求填写机器码(不要求则跳过此步骤):

    • 警告: 由于需要进行模型加密,运行key_gen.bin永远关闭 JTAG端口,并写入一次性 AES KEY,请确认对自己开发没有影响再进行烧录。(如果不使用JTAG调试开发或者仅使用MaixPy开发不影响)

    • 下载 key_gen 固件 key_gen_v1.2.zip

    • 下载 kflash-gui

    • 使用 kflash-gui 烧录 key_gen 固件

    • 打开串口终端, 重启开发板, 可以看到打印出的机器码。

 

Please Send Bellow Data to Sipeed --> [email protected]:

6f80dccbe29**********cc7e9d69f92

Generate key end

其中 6f80dccbe29**********cc7e9d69f92 就是对应开发板的机器码

  • 上传打包好的zip格式的数据集

  • 点击创建训练任务

八、 训练结果及使用方法

训练结果(成功或者失败) 会发送到邮箱。 是一个zip压缩文件, 解压后仔细阅读README.txt,使用了中英文对使用方法进行了说明

默认是在有最新版固件的情况下, 将结果文件全部拷贝到SD卡根目录, 断电插入开发板, 然后上电就可以运行了

如果需要将模型放到flash, 烧录方法参考MaixPy教程

 以管理员的方式运行

下载模式  高速改为低速模式

# -*-encoding=utf-8-*-

import cv2
import os

# path = 'switch.avi'
# k = input('输入要打开的摄像头索引:')
# path = int(k)
# if path < 0:
#     path = 0
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 320)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 240)
cv2.namedWindow('mycamera')
num = -1
start = False
imagePath = 'JPEGImages'
fileKey = [str(i) for i in range(1, 10)] + ['b', 'c', 'd', 'x', 'y']

while True:
    success, frame = cap.read()
    if success:
        # frame = cv2.rotate(frame, cv2.ROTATE_90_COUNTERCLOCKWISE)
        # frame = cv2.flip(frame, 1)
        cv2.imshow('mycamera', frame)
    key = cv2.waitKey(4) & 0xff
    if start is False and key == ord('a'):
        print('按下:a, 开始')
        start = True
    if start is True and key == ord('p'):
        print('按下:p, 暂停')
        start = False
    # index = str(key - ord('0'))
    index = chr(key)
    if index in fileKey:
        # name = int(index) * 1000000
        name = index + '0'*6
        k = input("请输入照片对上次于%s的偏移:" % name)
        # name = name + int(k)
        name = index + k.zfill(6)
        num = int(k)
        print('按下:' + index + ', name:' + name)
        imagePath = 'JPEGImages' + index
    elif key == ord('q'):
        break
    elif key not in [255, ord('a'), ord('p')]:
        print('无效按键')
    if start is False or num < 0:
        continue
    currentPath = 'VOCdevkit/VOC2007/' + imagePath
    if not os.path.exists(currentPath):
        print('create image directory', currentPath)
        os.makedirs(currentPath)
    cv2.imwrite('VOCdevkit/VOC2007/' + imagePath + '/' + name + '.jpg',
                frame)
    # cv2.imwrite('VOCdevkit/VOC2007/JPEGImages/' + name + '.jpg', frame)
    num += 1
    name = name[0] + '%06d' % num

cv2.destroyWindow('mycamera')
cap.release()

猜你喜欢

转载自blog.csdn.net/qq_36958104/article/details/109637616