UNIT-VでopenMV使ってIMEモード判別してみた
初めに
ブラインドタッチができない人間(私)が日々悩まされている、「日本語モードだと思って打っていたら英数モードだった(その逆もしかり)問題」の対策として「あ」or「A」を判別させるAIカメラの作成に取り掛かった。
ファーストステップとしてM5StickVのV-Trainingで狙いの動作をさせることができた。詳細は以下の記事にて。
pikapikalight.hatenadiary.com
M5StickVにはLCDとバッテリーがついていて便利なのだが、今回やりたいことに対してはこの機能は不要、むしろいらない機能といえる。 今回の目的に対してはUNIT-Vを使うのが最適なので、セカンドステップとしてUNIT-Vで同じものを作ることにした。
エッジAIの挫折
M5StickVでは割とすんなりできたので、UNIT-Vでも簡単にできると思って始めたのだが、これが予想外の苦戦を強いられる。
V-Trainingは学習曲線を見るかぎり問題なく学習できていそうなのだが、そのKモデルをUNIT-Vにいれて動かすと正答率がすこぶる悪い。カメラの位置、角度をシビアに調整してやっと正解が出るといった具合で、使い物にならないレベル。
写真の撮り方、写真サイズ、ブライトネス、コントラスト、色々変えてみたりしたが、うまくいかず。果てはV-Trainingを使わず、自力で学習してモデル作成するということもやってみたが、それでもやっぱり実機動作で正答率悪し。
そのあたりのいきさつはこちら。
V-TrainingでのUNIT-Vの画像判別がうまくいかないので、自力で学習させる方法ないかと検索したら、からあげ先生の記事に行きつく。
— PikaPikaらいと (@KPmilk3) 2021年4月13日
困った時はだいたいたどり着くからあげ先生。ありがたや。https://t.co/MxS8OZnid1
結局はAIによる判別はあきらめたのですが、Google Colaboratoryを使っての自力学習の方法など非常に得たものは大きかったので、詳細は別の記事にしたいと思います。V-Trainingを使わない、M5UNIT-V用のモデルの自力作成。
— PikaPikaらいと (@KPmilk3) 2021年4月14日
からあげ先生の記事にリンクされていたNabeshinさん作の修正版コードを使わせていただき、なんとか学習とモデル変換できました😁
ただ、学習結果がダメダメ😓これは写真の撮り方が悪いのだと思うのでやり直す。https://t.co/xdN5j6jrvu
openMVに挑戦
色々と試している過程で必然的にMaixPyの機能について調べることとなったのだが、思っていた以上にいろいろな機能があることが分かった。openMVとやらの機能が使えて、フィルターをかけたり、特定の色の領域を判別させたりと、AI使わなくてもひょっとしてできるかもと思い始めた。
さらに調べるとこのような記事を発見
マーカーシールを使ってメーターの針の位置を読み取るというもの。このマーカーシールを使う方法で今回やりたいことを実現することができた。
概要
- マーカーシールを「あ」の真下に貼る
- sensor.set_auto_gain(False) でオートゲインを無効にする
- MaixPyのしきい値エディタでマーカーのしきい値と「あ」「A」のしきい値を確認
- .find_blobsでマーカーシールの領域を特定
- マーカーシールの領域情報から「あ」の領域を特定し、その領域を切り出す
- 切り出した領域に対して.find_blobsで「あ」「A」の領域サイズを判定
- マーカーシールのサイズと「あ」「A」の領域サイズの比から「あ」を判別(「あ」のほうが明らかにサイズが大きいので区別ができる)
しきい値エディタ
MaixPyでフレームバッファにカメラの絵が出ている状態で メニューの「ツール」→「マシンビジョン」→「しきい値エディタ」→ソースイメージの場所 フレームバッファ でしきい値エディタを起動する。 スライドバーを適当に動かして、マーカーシールのみが表示されるしきい値を確認する。 同じように「あ」「A」のしきい値も確認する
コード
import sensor, image, time #import gc, math from board import board_info from Maix import GPIO from fpioa_manager import fm import sys while 1: try: sensor.reset() #Reset sensor may failed, let's try some times break except: time.sleep(0.1) continue sensor.set_hmirror(1) sensor.set_vflip(1) # run automatically, call sensor.run(0) to stop sensor.set_pixformat(sensor.RGB565) # Set pixel format to RGB565 (or GRAYSCALE) sensor.set_framesize(sensor.QVGA) # Set frame size to QVGA (320x240) sensor.skip_frames(time = 1000) # Wait for settings take effect. #sensor.set_brightness(-2) #sensor.set_contrast(-2) sensor.set_auto_gain(False) #これ重要。無効にしないと周りの明るさで大きく変化してしまう。 #clock = time.clock() # Create a clock object to track the FPS. #LED点灯用 fm.register(35, fm.fpioa.GPIOHS0, force=True) pinout35 = GPIO(GPIO.GPIOHS0, GPIO.OUT) pinout35.value(0) mark_threshold = (0, 100, -128, -28, -128, 127) #マークシールのしきい値 text_threshold = (100, 34, -128, 127, 127, -128) #「あ」のしきい値 while True: img=sensor.snapshot() blobs = img.find_blobs([mark_threshold]) if blobs: for b in blobs: if b.area() > 1000: #ゴミとりのため、小さい領域は無視する。 #print(b) tmp = img.draw_rectangle(b[0:4],color=(0,255,0)) tmp = img.draw_cross(b[5], b[6],color=(0,255,0)) mark_x = b[0] mark_y = b[1] mark_w = b[2] mark_area = b.area() try: tmp = img.draw_rectangle(mark_x,mark_y - int(mark_w*1.5),mark_w,mark_w,color=(0,0,255)) #シールの位置から文字の位置を特定 img2 = img.copy((mark_x,mark_y - int(mark_w*1.5),mark_w,mark_w)) #文字の領域を切り出す blobs2 = img2.find_blobs([text_threshold]) #切り出した領域に対してしきい値判定 if blobs2: for b in blobs2: #print(b.area()) if b.area() > 200: #小さい領域は無視する。 img.draw_rectangle(mark_x+b[0],mark_y - int(mark_w*1.5) + b[1],b[2],b[3],color=(255,255,255)) text_area = b.area()/mark_area #比を計算 print(text_area) if text_area > 0.2: #「あ」の判定基準。実機確認の結果より。 pinout35.value(1) else: pinout35.value(0) except: pass
サードステップ
今回の実施条件は照明がほとんど一定の環境下のもの。照明条件が大きく変わるような条件、例えば直射日光が当たるような場所では正しく動かないはずである。
ホワイトバランスをとるような仕組みがあればより安定した動作ができると思う。今後の検討課題である。
また、「あ」と「A」の区別を領域のサイズ比で行ったが、この判定部分をAIにさせることでより精度の高い判定ができそうな気がする。 マシンビジョンとAIの組み合わせ、なんかカッコいいのでトライしてみたい。