

NNCで作成した鯨と海を識別する画像認識用学習モデルをspresenseを用いて、arduino ideで実行したのですが、画像右のシリアルモニターの反応が
k1.jpg is -1 のように「-1」となっています。

[![画像の説明をここに入力](https://i.stack.imgur.com/Av3Od.png)](https://i.stack.imgur.com/Av3Od.png)

プログラムは以下リンク先の手順に従い作成しました。

[SPRESENSEでSony Neural Network Console を使ってみた！](https://makers-with-
myson.blog.ss-blog.jp/2019-07-14)

それによれば、-1のところは本来、識別する画像が鯨か海かを  
0~1の確率で表記されるようになっています。  
なお今回の場合0に近いほど鯨と認識するようにしています。  
全て-1と表示されるため画像認識どころではありません。  
-1と表示されるのはなぜなのでしょうか。

ソースコード自体がグレースケールの画像を識別しているので、  
RGBの画像が対応していないだけなのでしょうか。

また自分が調べたところ、dnnに使えるファイル形式はpgm形式でないとだめというコメントを確認しました。  
NNCによる学習モデル作成では、公式の動画でjpgでも大丈夫とのことだったので  
それに従いjpgで作成しました。NNC上では普通に動作しましたが、  
公式の動画ではspresenseに実装する動画は現在までにアップされていません。  
spresenseで動かすとなるとファイル形式は改めなければならないのでしょうか。

参考となる回答をよろしくお願いいたします。


CC BY-SA 4.0
2021-05-27T11:22:06.767
76118
2021-05-28T03:48:44.103
2021-05-27T11:42:26.793
3060
44392
question
spresense
Neural Network Console (NNC)で作成した画像認識用学習モデルをspresenseで実装しようとしたが


サイトを参照していただきありがとうございます。

**結論から言いますと、Spresense の DNNRT への入力に、jpg画像をそのまま使うことはできません。**

NNCはjpgデータをピクセルデータに復号してニューラルネットワークに入力しているのでjpgも扱えるようになっているようです。（学習データにjpgを使ったこと無いので推測です）

なぜ自信満々にそう言えるかというと、ニューラルネットワークの ”inputレイヤー” 定義をみれば一目瞭然です。

”inputレイヤー” のパラメータが「1,28,28」になっている場合は、(1,28,28)
の行列を入力するという意味になります。つまりモノクロの28x28のピクセルデータに展開しなければネットワークに入力できません。

カラー画像の場合は、「3,28,28」に ”inputレイヤー"
が定義されます。これは(3,28,28)の行列の入力、つまりRGBの28x28のピクセルデータを入力するという意味になります。ですのでjpgデータは事前にRGBの画像に展開されていなければなりません。

Spresense の場合は組み込みなので、NNC
ほど親切ではありません。DNNRTに入力する場合は、あらかじめ圧縮された画像は展開しておかなければなりません。（さらにデータをピクセル毎に、0.0-1.0の間に正規化して入力する必要があります）

jpg画像を扱いたい場合は、Spresense
にjpgデコーダを組み込んでおき、jpgデータをピクセルデータに展開する必要があります。（ただし組み込むのは大変です。お勧めしません）

さらにカラー画像を DNNRT に入力する場合は注意が必要です。カラーのデータはRデータ、Gデータ、Bデータのブロック単位で DNNRT
に入力する必要する必要があります。すなわちRGBのピクセルデータを分解し、Rデータ、Gデータ、Bデータのまとまりにする必要があります。これは、"inputレイヤー"
の行列の構造が(3,28,28) となっていることを考えれば分かると思います。

例えば、カメラのRGB565（16bit）のデータを扱う場合はこんな感じになります。

[code]

      uint16_t* buf = (uint16_t*)img.getImgBuff();
    
      DNNVariable input(INPUT_WIDTH * INPUT_HEIGHT * 3);  
      float* r = input.data();
      float* g = r + INPUT_WIDTH * INPUT_HEIGHT;
      float* b = g + INPUT_WIDTH * INPUT_HEIGHT;
      for (int i = 0; i < INPUT_WIDTH * INPUT_HEIGHT; ++i) {
       *(r++) = (float)((*buf >> 11) & 0x1F)/ 31.0; // 0x1F = 31
       *(g++) = (float)((*buf >>  5) & 0x3F)/ 63.0; // 0x3F = 64
       *(b++) = (float)((*buf)       & 0x1F)/ 31.0; // 0x1F = 31
       ++buf;
      }
      dnnrt.inputVariable(input, 0);
      dnnrt.forward();
      DNNVariable output = dnnrt.outputVariable(0);
    
[/code]

このような背景があるので、Spresense で画像ファイルを扱う場合は単純なフォーマットである
[PNM](https://ja.wikipedia.org/wiki/PNM_\(%E7%94%BB%E5%83%8F%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88\))
がよいと言われているのだと思います。ただ、PNMしか使えないのもPCで作業する上では不便ですよね。

なので、私は Spresense でBMPを扱えるライブラリを作ってみました。

<https://github.com/YoshinoTaro/BmpImage_ArduinoLib>

このライブラリですが、BMPの構造上、無駄にパレットを使うのでSpresenseでは小さな画像しか扱えません。（もともとDNNRTで小さい画像を扱うことしか想定しておらずメモリ最適化していません）

もし、必要だったら使ってみてください。ただし、出来たてほやほやなので、実装上の不具合がたくさんあると思います。使用の際はは自己責任にてお願いします。


CC BY-SA 4.0
2021-05-28T03:04:30.357
76136
2021-05-28T03:48:44.103
2021-05-28T03:48:44.103
27334
27334
76118
answer