【プログラミングなし】自分だけのAI画像認識モデルを作る

M5stickVで紙幣の分類

こんにちは、けいです。

今回は、M5stickVで簡単にAIで画像認識モデルを作る方法についてまとめていきたいと思います。

M5stickVについては今までの記事でまとめているので、ぜひ読んでみて下さい。

V-Trainingとは

V-Trainingは、M5stack社が提供している、AIモデルを作ってくれるサービスです。
なんと、プログラミングなしで、自分だけの画像認識モデルを作ることができます。

こちらは、先日作ったものです。

このモデルでは、下の4種類のものを分類することができます。

  • 1000円札
  • 5000円札
  • 10000円札

私が作ったものは、デフォルトのプログラムに少し手を加えて、音がでたり、二連続で認識した場合のみ作動するように組んでいます。
しかし、何もプログラムしなくても手順通り進めていけば、認識してくれます。

V-Trainingの手順

基本的にM5stack公式の情報を元に、気づいた注意点などを書いていきます。

ファームウェアの更新

https://docs.m5stack.com/#/en/related_documents/v-training
上のリンクより、最新のファームウェアを焼いてください。
私はWindowsを使用しているのでEasyLoaderを使いました。

デバイスマネージャーより、M5stickVのCOMポートを確認してから、Burnを選択して焼きましょう。

SDカードに保存

上のリンクで、Material Trainingという項目があると思います。
そこで、zipファイルをダウンロードし、解凍します。

その中に入っているファイルを/sdディレクトリの直下に保存します。


※SDカードに相性問題があるので、以下のリンクより使用可能なものを判断して下さい。

https://docs.m5stack.com/#/en/core/m5stickv

私が、現在使っているものはこちらになります。

写真を撮る

sdカードをM5stickVに入れて起動します。
起動画面にVtrainingというロゴが入っていれば正常に動作しています。


自分が分類したいものをひたすらパシャリます。
1つのクラスにつき35個以上とるようにしましょう。
1~30枚目までは/sd/trainフォルダに入り、30~35枚目は/sd/vailedに入り、35枚目以降はまた/sd/trainフォルダに入ります。


Aボタン(正面)を押すと写真を撮ります。
Bボタン(正面右)を押すとクラスが変わります。
クラスは /10 と液晶に映っていますが、10クラスまで作らなくても大丈夫です。

最低3クラスは必要なようです。

※ここで注意なのは、train, vailedフォルダの名前を変えないことです。


ここで名前を変えてしまうと、クラウドにデータを送って学習してもらう時に、エラーが生じます。

写真ファイルの圧縮

撮った写真が保存されているtrain、vailedフォルダを圧縮(zip)します。

※train、vailedフォルダを一つのファイルに圧縮してください。

それぞれを圧縮しても学習はできません。二つのファイルを選択(Ctrl+クリック)をしてzipファイルにします。
windowsであれば右クリックで[送る]を選択してzip化します。

圧縮ファイルをクラウドに送る

https://docs.m5stack.com/#/en/related_documents/v-training

のUpload data to cloudのクリックというリンクより、VTrainingのwebページに飛びます。

そこに、先ほど作ったzipファイルをアップロードし、メールアドレスを入力します。

数分したらメールが送られてきます。

成功すればリンクがあるので、クリックするとファイルのダウンロードが始まります。

もし、失敗しても何回でも送ることができるので焦らなくても大丈夫です。

エラーメッセージを読んで、修正しましょう。

AIモデルを動かす

ここまで来たら後は、実際に動かすだけです。
先ほどメールでダウンロードしたzipファイルを解凍し、3つのファイルを/sdディレクトリ直下に保存します。
始めの学習に使ったファイルは削除してください。

sdカードをM5stickVに入れ、電源をいれます。

自分の認識したいものが正確に認識できているでしょうか?

不安定でしたら、先ほどのtrain、vailedファイルに学習枚数を増やす手があります。

また、何も映っていないときにも認識してしまうことがあります。

それを防止するために背景をクラスに加えるという手があります。


youtube も投稿しているので、ぜひよかったら見て下さい。

さらに精度をよくするには

V-Trainingのデフォルトのコードでは、連続で写真を撮影して、少しでも対象がうつれば、すぐに認識します。

それがいいことでもあれば悪いことでもあると思います。

少し映り込んだだけで判断してしまうことは正確な認識をして欲しい時には向いていません。

むしろ、少し遅くなっても正確性を出したい時があると思います。

その場合はboot.pyを少しいじる必要があります。

boot.py

boot.pyの中身はこのようになっています。

import image
import lcd
import sensor
import sys
import time
import KPU as kpu
from fpioa_manager import *

import KPU as kpu


lcd.init()
lcd.rotation(2)

try:
    img = image.Image("/sd/startup.jpg")
    lcd.display(img)
except:
    lcd.draw_string(lcd.width()//2-100,lcd.height()//2-4, "Error: Cannot find start.jpg", lcd.WHITE, lcd.RED)


task = kpu.load("/sd/7f1d122ee02a6ad3_mbnet10_quant.kmodel")

labels=["1","2","3","4"] #You can check the numbers here to real names.

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.run(1)

lcd.clear()


while(True):
    img = sensor.snapshot()
    fmap = kpu.forward(task, img)
    plist=fmap[:]
    pmax=max(plist)
    max_index=plist.index(pmax)
    a = lcd.display(img)
    if pmax > 0.95:
        lcd.draw_string(40, 60, "Accu:%.2f Type:%s"%(pmax, labels[max_index].strip()))
a = kpu.deinit(task)

LCDに表示する名前を変えたい場合は、labelsというリストの番号を自分の好きな名前に変更することで表示を変えられます。

先ほどご紹介したように、私はお金を分類するプログラムを作りました。
その時の条件として、

  • 基本は机を認識する。
  • 机を認識しているときは音声を出さない。
  • もし、お金が映ったらもう一回認識して、二連続で同じ種類のお金だった場合にお金を認識し、音声で出力する。

これを実装するために、机を認識するときとそれ以外で分岐させる必要があります。

新boot.py

import image
import lcd
import sensor
import sys
import time
import KPU as kpu
from fpioa_manager import *

import KPU as kpu
import audio
from Maix import I2S, GPIO

fm.register(board_info.SPK_SD, fm.fpioa.GPIO0)
spk_sd=GPIO(GPIO.GPIO0, GPIO.OUT)
spk_sd.value(1) #Enable the SPK output

fm.register(board_info.SPK_DIN,fm.fpioa.I2S0_OUT_D1)
fm.register(board_info.SPK_BCLK,fm.fpioa.I2S0_SCLK)
fm.register(board_info.SPK_LRCLK,fm.fpioa.I2S0_WS)

wav_dev = I2S(I2S.DEVICE_0)

def play_sound(filename):
    try:
        player = audio.Audio(path = filename)
        player.volume(100)
        wav_info = player.play_process(wav_dev)
        wav_dev.channel_config(wav_dev.CHANNEL_1, I2S.TRANSMITTER,resolution = I2S.RESOLUTION_16_BIT, align_mode = I2S.STANDARD_MODE)
        wav_dev.set_sample_rate(wav_info[1])
        while True:
            ret = player.play()
            if ret == None:
                break
            elif ret==0:
                break
        player.finish()
    except:
        pass


lcd.init()
lcd.rotation(2)

try:
    img = image.Image("/sd/startup.jpg")
    lcd.display(img)
    time.sleep(0.5)
    play_sound("/sd/logo.wav")

except:
    lcd.draw_string(lcd.width()//2-100,lcd.height()//2-4, "Error: Cannot find start.jpg", lcd.WHITE, lcd.RED)


task = kpu.load("/sd/7f1d122ee02a6ad3_mbnet10_quant.kmodel")

labels=["1,000 YEN","5,000 YEN","10,000 YEN","desk"] #You can check the numbers here to real names.

sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.run(1)

lcd.clear()


while(True):
    img = sensor.snapshot()
    fmap = kpu.forward(task, img)
    plist=fmap[:]
    pmax=max(plist)
    max_index=plist.index(pmax)
    a = lcd.display(img)
    
    #desk
    if (pmax > 0.95) and (max_index==3):
        lcd.draw_string(40, 60, "Accu:%.2f  %s"%(pmax, labels[max_index].strip()))

    #1000
    elif (pmax > 0.95) and (max_index==0):
	time.sleep(1)
	img = sensor.snapshot()
        fmap = kpu.forward(task, img)
        plist=fmap[:]
        pmax=max(plist)
        max_index=plist.index(pmax)
        a = lcd.display(img)
	if (pmax > 0.95) and (max_index==0):
            lcd.draw_string(40, 60, "Accu:%.2f  %s"%(pmax, labels[max_index].strip()))
	    play_sound("/sd/1000yen.wav")
	    time.sleep(1)

    #5000
    elif (pmax > 0.95) and (max_index==1):
	time.sleep(1)
	img = sensor.snapshot()
        fmap = kpu.forward(task, img)
        plist=fmap[:]
        pmax=max(plist)
        max_index=plist.index(pmax)
        a = lcd.display(img)
	if (pmax > 0.95) and (max_index==1):
            lcd.draw_string(40, 60, "Accu:%.2f  %s"%(pmax, labels[max_index].strip()))
	    play_sound("/sd/5000yen.wav")
	    time.sleep(1)

    #10000
    elif (pmax > 0.95) and (max_index==2):
	time.sleep(1)
	img = sensor.snapshot()
        fmap = kpu.forward(task, img)
        plist=fmap[:]
        pmax=max(plist)
        max_index=plist.index(pmax)
        a = lcd.display(img)
	if (pmax > 0.95) and (max_index==2):
            lcd.draw_string(40, 60, "Accu:%.2f  %s"%(pmax, labels[max_index].strip()))
	    play_sound("/sd/10000yen.wav")
	    time.sleep(1)

a = kpu.deinit(task)

少し長いですが変えたところは、wavファイルを出力する関数と、認識モデルに対する分岐です。
始めの起動時が寂しいので、音を入れました。
ご自分でlogo.wavファイルにお好きな音声を入れて下さい。

wavファイルを使う方法は以前の記事でまとめたのでぜひ読んでみて下さい。

このプログラムでは、お金を手で動かす速さを想定しており、二連続で認識する程度の速さで丁度よいぐらいの認識速度になりました。

皆さんの用途に合わせてスピードなどを変えてみて下さい。

最後まで見て下さってありがとうございました。