いま流行りのAIカメラ M5StickVを使って、
スマートスピーカーを画像認識して、ウェイクワードをしゃべらせてみました。
(デモ動画の3番目のはFire HD(amazonタブレット)です。Alexa用に使用しているのでウェイクさせようとしました。出来ていませんが)Niwatori wake-word speaker pic.twitter.com/MZg5V2WXpa
— Ryota Ikeuchi (@IkeuchiRyota) January 4, 2020
概要
カメラ画像に対して画像判別を行い、スマートスピーカーと認識されれば、ウェイクワードをスピーカーから再生します。([Open JTalk] (http://open-jtalk.sp.nitech.ac.jp/index.php)を使用して音声ファイルを予め用意しておく)
Google Homeを写すと「OK Google」としゃべり、
Clovaなら「ねえ Clova」、
Fire HD(amazonタブレット)なら「アレクサ」と言います。
M5StickV単体で動作します。
何に役立つかは…とくに考えていませんが、
メガネに組み込んで、「しばらく見つめると、ウェイクワードを代わりに言ってくれる」というのは便利かも?(「アイズフリー(見る必要がない)」という音声UIのメリットを台無しにする使い方ですが)
画像認識や音声読み上げを試したくて作ったものです。
AIカメラ M5StickV
2019年夏ごろに発売された、"AIカメラ"です。いろいろすごくて話題になっています。
- CNNを高速に実行できる"AIチップ"を搭載
- カメラ、ディスプレイ、バッテリー、スピーカーなどが一体
- 基板むき出しじゃなくケース入り
- たったの3,000円
既にスマホという高性能な"AIカメラ"があるので、スマホには出来ない使い方を考えてみたいですね。
- 大量に買って、いろんな場所に取り付ける(安いので)
- 子供(ちびっこ)のおもちゃにする
- 機器に組み込んだり、建物に固定で設置する
とかでしょうか。
画像認識のモデル
認識対象が一般的なものなら、既にあるモデルをそのまま使えばOKです。
例えば、MobileNet v1の1,000クラスのモデルが公開されています。(https://www.maixhub.com/index.php/index/index/detail/id/2.html たぶんM5StickVでそのまま使える)
しかし、今回の認識対象は、「Google Home」とか、「Clova Friends Sally」とかです。そのようなものは用意されていませんので、自分でモデルをつくる必要があります。
ここが腕の見せ所であり、私のようなAIビギナーのがんばりどころなのですが・・・
**今回は[V-Training] (https://docs.m5stack.com/#/en/related_documents/v-training)というツールを使いました。**今回使用するAIカメラのメーカーが提供しているものです。
認識させたいものそれぞれ35枚ずつ写真を撮ってアップロードすれば、学習済みモデルをつくってくれるというものです。お手軽。
写真はM5StickVを使用して撮影します。撮影時に使うプログラムも用意されていて、とっても簡単です。
AIの開発の敷居がどんどん低くなっていることを感じますね。
コード
V-Trainingを使うと、学習済みモデルとともにそれをM5StickVで動作させるためのコードも送られておきます。
これは、単に認識されたラベルを画面上に表示させる程度のものです。
今回は、そのラベルに応じて読み上げをさせたい。
読み上げの部分は[Brownie] (https://github.com/ksasao/brownie)のコードを参考にさせていただきました。
(そもそも、読み上げさせるというアイデア自体がBrownieの真似のようなものですが)
import image
import lcd
import sensor
import sys
import time
import KPU as kpu
from fpioa_manager import *
import KPU as kpu
import audio
import gc
import uos
from machine import I2C
from Maix import I2S, GPIO
lcd.init()
lcd.rotation(2)
i2c = I2C(I2C.I2C0, freq=400000, scl=28, sda=29)
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)
fm.register(board_info.BUTTON_A, fm.fpioa.GPIO1)
but_a=GPIO(GPIO.GPIO1, GPIO.IN, GPIO.PULL_UP) #PULL_UP is required here!
fm.register(board_info.BUTTON_B, fm.fpioa.GPIO2)
but_b = GPIO(GPIO.GPIO2, GPIO.IN, GPIO.PULL_UP) #PULL_UP is required here!
fm.register(board_info.LED_W, fm.fpioa.GPIO3)
led_w = GPIO(GPIO.GPIO3, GPIO.OUT)
led_w.value(1) #RGBW LEDs are Active Low
fm.register(board_info.LED_R, fm.fpioa.GPIO4)
led_r = GPIO(GPIO.GPIO4, GPIO.OUT)
led_r.value(1) #RGBW LEDs are Active Low
fm.register(board_info.LED_G, fm.fpioa.GPIO5)
led_g = GPIO(GPIO.GPIO5, GPIO.OUT)
led_g.value(1) #RGBW LEDs are Active Low
fm.register(board_info.LED_B, fm.fpioa.GPIO6)
led_b = GPIO(GPIO.GPIO6, GPIO.OUT)
led_b.value(1) #RGBW LEDs are Active Low
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
try:
from pmu import axp192
pmu = axp192()
pmu.enablePMICSleepMode(True)
except:
pass
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/model/vtraining_smartspeakers_1.kmodel")
labels=["G_Home","clove_sally","fire_hd","G_Home_mini"] #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()
# isButtonPressedA = 0
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()))
# if but_a.value() == 0 and isButtonPressedA == 0:
if but_a.value() == 1:
play_sound("/sd/voice/ja/"+str(max_index)+".wav")
# isButtonPressedA = 1
# if but_a.value() == 1:
# isButtonPressedA = 0
a = kpu.deinit(task)
Brownieでは(物体が認識されている状態で)ボタンを押すと読み上げですが、
今回は見つけた瞬間に読み上げとしてみました。
黙らせたいときにはボタンを押します。
課題と今後
スマートスピーカー以外のものを写したときの誤認識が多いです。
ざっくり言うと「とりあえず白ければGoogle Home」、「黄色のはClova Friends Sally」という感じです。
「どのスマートスピーカーでもない」という学習用データを与えていないので当然でしょうか。
やはり学習用データやモデルの工夫が重要そうです。
V-Trainingにおまかせでなく、自分で色々と試してみようと思います。
こちらのcolab notebookが詳しそうです:
Creating an Image Classification Model for M5StickV by Transfer Learning
参考
セットアップなどは、[M5StickVで超お手軽エッジAI画像認識(からあげ様)] (https://karaage.hatenadiary.jp/entry/2019/08/14/073000)が参考になりました。