SPRESENSE で小型AIカメラを作ってみた! [SPRESENSE]
SPRESENSEにOLEDがついたので、今度はカメラをつけてAIカメラにしてみようと思います。久しぶりにNeural Network Console を使います。
1. 画像を準備
まず、画像を用意します。ネットから適当に集めてきた”鳥”、”馬”、”うさぎ”、”人”、”壁”の画像を使いました。
2. 画像を加工
これらを、ImageMagick を使って、28x28pixel 8bit (256階調)のグレースケールの画像に変換し、ニューラルネットワークのデータセットとして登録します。(詳細は、また別の機会に)
3. ニューラルネットワークをデザイン
Neural Network Console を開いてニューラルネットワークをデザインします。今回は LeNet ベースで作ってみました。(Spresenseに入るように少し改造しています)
4. ニューラルネットワークを学習・評価
登録したデータセットを使って、デザインしたニューロンを学習・評価します。
5. ニューロンモデルの出力
SPRESENSEで解釈できるニューロンモデル(model.nnb)を出力します。
6. Arduino スケッチの作成・焼き込み
ArduinoのAIスケッチを作成します。AIカメラを小さくしかったので、ニューロンモデルはSPRESENSEメインボードのフラッシュの中に格納してみました。
動作の様子は動画でどうぞ!!( ^ω^ )/~
関連記事
SPRESENSEでSony Neural Network Console を使ってみた!
https://makers-with-myson.blog.ss-blog.jp/2019-07-14
1. 画像を準備
まず、画像を用意します。ネットから適当に集めてきた”鳥”、”馬”、”うさぎ”、”人”、”壁”の画像を使いました。
2. 画像を加工
これらを、ImageMagick を使って、28x28pixel 8bit (256階調)のグレースケールの画像に変換し、ニューラルネットワークのデータセットとして登録します。(詳細は、また別の機会に)
3. ニューラルネットワークをデザイン
Neural Network Console を開いてニューラルネットワークをデザインします。今回は LeNet ベースで作ってみました。(Spresenseに入るように少し改造しています)
4. ニューラルネットワークを学習・評価
登録したデータセットを使って、デザインしたニューロンを学習・評価します。
5. ニューロンモデルの出力
SPRESENSEで解釈できるニューロンモデル(model.nnb)を出力します。
6. Arduino スケッチの作成・焼き込み
ArduinoのAIスケッチを作成します。AIカメラを小さくしかったので、ニューロンモデルはSPRESENSEメインボードのフラッシュの中に格納してみました。
#include <Camera.h> #include <Flash.h> #include <DNNRT.h> #include <File.h> #include "Adafruit_GFX.h" #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); #define BAUDRATE 115200 #define OFFSET_X 48 #define OFFSET_Y 8 #define BOX_WIDTH 224 #define BOX_HEIGHT 224 #define DNN_IMG_WIDTH 28 #define DNN_IMG_HEIGHT 28 DNNRT dnnrt; DNNVariable input(DNN_IMG_WIDTH*DNN_IMG_HEIGHT); String gStrResult = ""; void CamCB(CamImage img) { if (!img.isAvailable()) return; img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565); Serial.println("start clip and resize"); gStrResult = ""; int x; int index = 0; static uint8_t label[5] = {0, 1, 2, 3, 4}; CamImage small; CamErr camErr = img.clipAndResizeImageByHW(small ,OFFSET_X ,OFFSET_Y ,OFFSET_X+BOX_WIDTH-1 ,OFFSET_Y+BOX_HEIGHT-1 ,DNN_IMG_WIDTH ,DNN_IMG_HEIGHT); if (!small.isAvailable()) { Serial.println("Error Occured at making a target image"); if (camErr) Serial.println("CamErr: " + String(camErr)); return; } uint16_t* buf = (uint16_t*)small.getImgBuff(); float* input_buffer = input.data(); for (int i = 0; i < DNN_IMG_WIDTH * DNN_IMG_HEIGHT; ++i, ++buf) { input_buffer[i] = (float)(((*buf & 0x07E0) >> 5) << 2) ; // extract green } Serial.println("DNN forward"); dnnrt.inputVariable(input, 0); dnnrt.forward(); DNNVariable output = dnnrt.outputVariable(0); float max_value = 0.0; for (int i = 0; output.size() > i; ++i) { if (output[i] > max_value) { max_value = output[i]; index = i; } } Serial.print("Result : "); Serial.println(label[index] + " (" + String(output[index]) + ")"); if (label[index] != 10) { gStrResult += String(label[index]); } else { gStrResult += String(" "); } display.clearDisplay(); display.setTextSize(3); // Normal 1:1 pixel scale display.setCursor(0, 0); // Start at top-left corner display.cp437(true); // Use full 256 char 'Code Page 437' font switch (index) { case 0: display.println("BIRD"); break; case 1: display.println("HORSE"); break; case 2: display.println("HUMAN"); break; case 3: display.println("-----"); break; case 4: display.println("RABBIT"); break; } display.display(); Serial.println("Recognition Result: " + gStrResult); } void setup() { Serial.begin(BAUDRATE); if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for(;;); // Don't proceed, loop forever } display.display(); delay(2000); // Pause for 2 seconds // Clear the buffer display.clearDisplay(); // Draw a single pixel in white display.drawPixel(10, 10, SSD1306_WHITE); display.display(); delay(2000); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.cp437(true); // Use full 256 char 'Code Page 437' font for(int16_t i=0; i<256; i++) { if(i == '\n') display.write(' '); else display.write(i); } display.display(); Serial.println("Loading network model"); File nnbfile = Flash.open("model.nnb", FILE_READ); if (!nnbfile) { Serial.println("nnb not found"); while(1); } Serial.println("Initialize DNNRT"); int ret = dnnrt.begin(nnbfile); if (ret < 0) { Serial.println("DNNRT initialize error."); while(1); } theCamera.begin(); theCamera.startStreaming(true, CamCB); } void loop() { /* do nothing here */ }
動作の様子は動画でどうぞ!!( ^ω^ )/~
関連記事
SPRESENSEでSony Neural Network Console を使ってみた!
https://makers-with-myson.blog.ss-blog.jp/2019-07-14
ソニー開発のNeural Network Console入門【増補改訂・クラウド対応版】--数式なし、コーディングなしのディープラーニング
- 出版社/メーカー: リックテレコム
- 発売日: 2018/11/14
- メディア: 単行本(ソフトカバー)
SONY SPRESENSE メインボード CXD5602PWBMAIN1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
SONY SPRESENSE カメラモジュール CXD5602PWBCAM1
- 出版社/メーカー: スプレッセンス(Spresense)
- メディア: Tools & Hardware
2020-02-18 23:59
nice!(23)
コメント(4)
こんなに小さいのに賢いですねぇ^^
by ぽちの輔 (2020-02-19 06:12)
コメント
Hi Taro, I have two questions:
1)- What's the size of your model file?
2)- is The Flash singleton automatically mapped to an SD card on the OLED?
by CGM11 (2020-02-20 01:01)
ぽちの輔さん、ありがとうございます!
Hi CGM11,
Here are the answers
1) 76kB
2) The Flash is not on the OLED. I'm using an equipped 8MB flash on Spresense Mainboard.
by ys_taro (2020-02-22 03:17)
すみません、質問させてください。
SpresenseのFlashエリアに予めnnbを書き込んでおく操作はどのように行われたのでしょうか。
Arduino IDEから行えるのでしょうか。
不躾な質問ですがどうぞ宜しくお願い申し上げます。
by renpoco (2021-04-13 12:23)