STM32H747I-DISC0を用いたAI画像分類

STマイクロエレクトロニクス株式会社(STマイクロ)が提供しているAIキットを使用して、機械学習による画像分類を試してみました。
STM32H747I-DISC0ボードとB-CAMS-OWVカメラを使った機械学習のデモとして用意されているFood Recognitionデモのニューラルネットワーク(NN)データ部分を置き換え、オレンジとバナナの２種類を認識するようにしてみました。

# 準備

- ハード
開発ボード：STM32H747I-DISC0
カメラモジュール：B-CAMS-OMV

- ソフト
AIソフトウェアデモ：FP-AI-VISION1 (V3.1.0) ※1
コード生成ツール：STM32CubeMX (V6.4.0)、X-CUBE-AI (V7.1.0) ※1
ボード用ドライバ：STM32 ST-LINK Utility (V4.6.0) ※1
統合開発環境：IAR EWARM (V9.20.2) ※2
AIモデル生成ツール：Anaconda3 (2021.11)

※1 これらのソフトのダウンロードには、STマイクロの会員登録(無料)が必要です。
※2 このソフトのダウンロードには、IARシステムズ株式会社(IAR)の会員登録(無料)が必要です。

# 各ソフトのインストール

- FP-AI-VISION1
以下のSTマイクロのURLからダウンロードして、展開するだけです。
https://www.stmcu.jp/design/sw_dev/firmware/69743/

- STM32CubeMX
以下のSTマイクロのURLからダウンロードして、指示に従ってインストールしました。
https://www.stmcu.jp/design/sw_dev/pc_soft/52798/

- X-CUBE-AI
STM32CubeMXを使用してインストールしました。STM32CubeMXを立ち上げ、``Help``のプルダウンメニューから``Manage embedded software packages``を選び、"Embedded Software Packages Manager"ウィンドウを表示します。``STMicroelectronics``タブを選択し、X-CUBE-AIのArtificial Intelligence 7.1.0にチェックを入れ、``Install Now``ボタンでインストールしました。

- STM32 ST-LINK Utility
以下のSTマイクロのURLからダウンロードして、指示に従ってインストールしました。
https://www.stmcu.jp/design/sw_dev/pc_soft/53396/

- IAR EWARM
本記事では統合開発環境としてIARのEWARMを使用しました。他にSTM32CubeIDEなどもあるので、使い慣れた統合開発環境で良いと思います。
以下のIARのURLからダウンロードして、指示に従ってインストールしました。
https://www.iar.com/jp/products/architectures/arm/iar-embedded-workbench-for-arm/

- Anaconda3
Anaconda3のインストールについては[「STM32H747I-DISC0を用いたAI画像分類(NNモデル作成編)」](https://qiita.com/v1471_modoki/items/bcd8c367c45871981fc3)にまとめましたので、そちらをご覧下さい。

# ニューラルネットワーク(NN)モデルの作成
オレンジとバナナを区別するNNモデルの作成については、[「STM32H747I-DISC0を用いたAI画像分類(NNモデル作成編)」](https://qiita.com/v1471_modoki/items/bcd8c367c45871981fc3)にまとめましたので、そちらをご覧下さい。

# NNモデルのC言語ソース変換
NNモデル作成編で作成したNNモデルをSTM32CubeMXのX-CUBE-AIで、C言語ソースに変換します。
STM32CubeMXを起動し、新規プロジェクトを作成します。そのとき、Board SelectorでSTM32H747I-DISC0を選択します。
![CubeMX_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343121/c41df887-7d38-d769-d355-2bee88e53c1b.png)
``Software Packs``->``Select Components``を押し、"Software Packs Component Selector"ウィンドウを表示させます。"Show components for context"は、``Cortex-M7``が選択されていることを確認します。
X-CUBE-AIの版数は7.1.0を選択して、X-CUBE-AIの項目を展開します。そして、CoreのSelectionにチェックを入れ、Applicationを``Validation``を選択して、OKボタンを押します。
![CubeMX_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343121/3496add4-30dc-65d8-a7ad-a7965e4afbb4.png)
X-CUBE-AIを有効にしたところで、一度プロジェクトを名前を付けて保存しておきます。
次に左の欄の"Software Packs"の中にある``STMicroelectronics.X-CUBE-AI.7.1.0_M7``を押して、STMicroelectronics.X-CUBE-AI.7.1.0_M7のConfiguration画面を表示させます。
![CubeMX_031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343121/46b61a23-a693-8555-749d-bed2bc055d71.png)
``Add network``を押し、モデル名は"network"のままにして、モデル種別はKerasのSaved modelを設定して、NNモデル作成編で作成したNNモデルのファイルを選択します。``Analyze``を押し、NNモデルを解析します。
![CubeMX_032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2343121/eb72cd45-e82e-2b33-953d-1bf4e590a739.png)
解析が完了すると解析結果が表示されます。注目すべきはROMサイズでSTM32H747I-DISC0は2MbyteのFlash memoryを搭載していますが、約1Mbyte強はデモプログラムに使用されるので、NNモデルに使用できるサイズは1Mbyte弱です。

```
Exec/report summary (analyze)
------------------------------------------------------------------------------------------------------------------------
model file            : C:\local\development\qiita\fruit\fruit_qiita.h5
type                  : keras
c_name                : network
compression           : None
workspace dir         : C:\Users\qiita\AppData\Local\Temp\mxAI_workspace32755982772040013800634662094901120
output dir            : C:\Users\qiita\.stm32cubemx\network_output
model_name            : fruit_qiita
model_hash            : adf3862d6d67769248776be1f4117b61
input 1/1             : 'input_0'
                        150528 items, 588.00 KiB, ai_float, float, (1,224,224,3), domain:user/
output 1/1            : 'dense_2'
                        2 items, 8 B, ai_float, float, (1,1,1,2), domain:user/
params #              : 150,994 items (589.82 KiB)
macc                  : 2,095,312
weights (ro)          : 603,976 B (589.82 KiB) (1 segment)
activations (rw)      : 159,744 B (156.00 KiB) (1 segment)
ram (total)           : 761,864 B (744.01 KiB) = 159,744 + 602,112 + 8

Model name - fruit_qiita ['input_0'] ['dense_2']
---------------------------------------------------------------------------------------------------------------------------------------------------------
id   layer (type)               oshape             param/size        macc        connected to      |   c_size   c_macc             c_type               
---------------------------------------------------------------------------------------------------------------------------------------------------------
0    input_0 (Input)            (None,224,224,3)                                                   |                              
     max_pooling2d_1 (Pool)     (None,112,112,3)                     150,528     input_0           |                               pool()[0]            
---------------------------------------------------------------------------------------------------------------------------------------------------------
1    conv2d_1_conv2d (Conv2D)   (None,55,55,16)    448/1,792         1,306,816   max_pooling2d_1   |            +95,056(+7.3%)     optimized_conv2d()[1]
     conv2d_1 (Nonlinearity)    (None,55,55,16)                      48,400      conv2d_1_conv2d   |            -48,400(-100.0%)  
---------------------------------------------------------------------------------------------------------------------------------------------------------
2    max_pooling2d_2 (Pool)     (None,27,27,16)                      46,656      conv2d_1          |            -46,656(-100.0%)  
---------------------------------------------------------------------------------------------------------------------------------------------------------
3    conv2d_2_conv2d (Conv2D)   (None,13,13,16)    2,320/9,280       389,392     max_pooling2d_2   |            +5,008(+1.3%)      optimized_conv2d()[2]
     conv2d_2 (Nonlinearity)    (None,13,13,16)                      2,704       conv2d_2_conv2d   |            -2,704(-100.0%)   
---------------------------------------------------------------------------------------------------------------------------------------------------------
4    max_pooling2d_3 (Pool)     (None,6,6,16)                        2,304       conv2d_2          |            -2,304(-100.0%)   
---------------------------------------------------------------------------------------------------------------------------------------------------------
5    flatten_1 (Reshape)        (None,1,1,576)                                   max_pooling2d_3   |                              
---------------------------------------------------------------------------------------------------------------------------------------------------------
6    dense_1_dense (Dense)      (None,1,1,256)     147,712/590,848   147,712     flatten_1         |                               dense()[3]           
     dense_1 (Nonlinearity)     (None,1,1,256)                       256         dense_1_dense     |                               nl()[4]              
---------------------------------------------------------------------------------------------------------------------------------------------------------
8    dense_2_dense (Dense)      (None,1,1,2)       514/2,056         514         dense_1           |                               dense()[5]           
     dense_2 (Nonlinearity)     (None,1,1,2)                         30          dense_2_dense     |                               nl()/o[6]            
---------------------------------------------------------------------------------------------------------------------------------------------------------
model/c-model: macc=2,095,312/2,095,312  weights=603,976/603,976  activations=--/159,744 io=--/602,120
```

このモデルでは、"weights (ro)"は603,976 Bと1MByte弱に収まっているので、右上にある``Generate code``ボタンでコードを生成します。

# FP-AI-VISION1へ生成コードをコピー
展開したFP-AI-VISION1に上記で生成したNNモデルのコード(5ファイル)をコピーします。

★コピー元ファイル
$(AI)/CM7/X-CUBE-AI/App/neteork.c
$(AI)/CM7/X-CUBE-AI/App/neteork.h
$(AI)/CM7/X-CUBE-AI/App/neteork_config.h
$(AI)/CM7/X-CUBE-AI/App/neteork_data.c
$(AI)/CM7/X-CUBE-AI/App/neteork_data.h

※　$(AI)はSTM32CubeMXで作成したプロジェクトのディレクトリ

★コピー先ディレクトリ
・ヘッダファイル
$(FP-AI-VISION1)/Projects/STM32H747I-DISC0/Applications/FoodReco_MobileNetDerivative/Float_Model/CM7/Inc

・Cファイル
$(FP-AI-VISION1)/Projects/STM32H747I-DISC0/Applications/FoodReco_MobileNetDerivative/Float_Model/CM7/Src

※　$(FP-AI-VISION1)を展開したディレクトリ

また、今回は分類の種類をorangeとbananaに変更したので、デモコード部分も一部変更します。

```c:FoodReco_MobileNetDerivative/Float_Model/CM7/Src/fp_vision_app.c一部
136. #if 0
137. const char* output_labels[AI_NET_OUTPUT_SIZE] = {
138.   "Apple Pie",      "Beer",    "Caesar Salad", "Cappuccino", "Cheesecake",          "Chicken Wings",
139.   "Chocolate Cake", "Coke",    "Cup Cakes",    "Donuts",     "French Fries",        "Hamburger",
140.   "Hot Dog",        "Lasagna", "Pizza",        "Risotto",    "Spaghetti Bolognese", "Steak"};
141. #else
142. const char* output_labels[AI_NET_OUTPUT_SIZE] = {
143.   "Orange",      "Banana"};
144. #endif

```

# EWARMによるビルドと実行
FP-AI-VISION1には以下の場所にEWARMのプロジェクトファイルが用意されているので、それを使ってEWARMを起動します。
$(FP-AI-VISION1)/Projects/STM32H747I-DISC0/Applications/FoodReco_MobileNetDerivative/Float_Model/EWARM/Project.eww

EWARMが立ち上がると、全ビルドを行います。
USBケーブルでSTM32H747I-DISC0とパソコンを接続して、ファームウェアをダウンロードして実行すればorangeとbananaを分類するデモが動き出します。

最後に、YouTubeにorangeとbananaを分類するデモ動画をアップしていますので、ご覧下さい。
[YouTube動画「STM32H747I-DISC0を用いたAI画像分類」](https://youtu.be/cA6bAiCih4U)