5.6.2.7ディープラーニング：SSDによる物体検出

# 目次へのリンク


[MATLABによる画像処理・コンピュータービジョン入門目次](https://qiita.com/tohruk/items/e5f7939b622bcf2e3c4c)


# 概要


このデモではSSD(Single Shot Multibox Detector)による物体検出器の学習および推論を行います。 




実行には下記のToolboxが必要になります。 



   -  MATLAB R2020a 以降
   -  Image Processing Toolbox 
   -  Computer Vision Toolbox 
   -  Deep Learning Toolbox 
   -  Parallel Computing Toolbox (NVIDIA GPUでの学習・推論の高速化） 



この例ではバックボーンのネットワークとしてResNet50を使用します。




ResNet50の読み込みには無償のアドオンをインストールが必要です。 (※[ホーム]-[アドオン]からインストール)




対応ファイル



   -  [I5_06_2_7_trainSSDObjectDetector.m](https://github.com/mathworks/MATLAB-IPCV-Eval-Kit-JP/blob/master/demo_files/I5_06_2_7_trainSSDObjectDetector.m) 



さらにGPUコード生成を通してNVIDIA Jetson Nanoへの実装を行います。




GPU実装には下記のToolboxが必要です。



   -  MATLAB Coder 
   -  GPU Coder 
   -  [GPU Coder Support Package for NVIDIA GPUs](https://www.mathworks.com/help/releases/R2020a/supportpkg/nvidia/index.html) (GPU Coderのアドオン) 



CUDAコードのビルドはターゲット上で行うためホスト側にNVIDIA GPUやCUDA Toolkitなどは不要です。


# 初期化
```matlab:code
clear; close all force; clc; rng('default');
```
# 車両検出器用の学習データセットをロード


同梱の車両検出用のラベリング済み学習データセットを展開します。


```matlab:code
unzip vehicleDatasetImages.zip
data = load('vehicleDatasetGroundTruth.mat');
vehicleDataset = data.vehicleDataset;
```
# 学習データとテスト用データに分割


学習用データ(60%)とテスト用データ(40%)に分割


```matlab:code
rng(0); % 乱数シードを実行毎に固定にする(同じ結果を再現できるように)
shuffledIndices = randperm(height(vehicleDataset));
idx = floor(0.6 * length(shuffledIndices) );
trainingData = vehicleDataset(shuffledIndices(1:idx),:);
testData = vehicleDataset(shuffledIndices(idx+1:end),:);

imdsTrain = imageDatastore(trainingData{:,'imageFilename'});
bldsTrain = boxLabelDatastore(trainingData(:,'vehicle'));

imdsTest = imageDatastore(testData{:,'imageFilename'});
bldsTest = boxLabelDatastore(testData(:,'vehicle'));

trainingData = combine(imdsTrain,bldsTrain);
testData = combine(imdsTest, bldsTest);
```
# SSDネットワークを定義


ResNet50をベースにSSDのネットワークを定義します。


```matlab:code
% 入力画像サイズ
inputSize = [300 300 3];

% クラス数(今回は車両のみなので1個)
numClasses = width(vehicleDataset)-1;

% SSDネットワークの作成(ベースはvgg16, resnet50, resnet101が使用可能)
lgraph = ssdLayers(inputSize, numClasses, 'resnet50');

% プロットする
figure('Units','normalized','Position',[0.1 0.1 0.8 0.8]);
plot(lgraph);
```

![figure_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/8eecb7bd-a202-889a-a0c3-faca24936a3e.png)


# データの水増しと前処理


反転やシフトなどのデータ拡張(水増し、augmentation)を学習時に適用し、検出器の汎用性を高めます。




その後、ネットワークの入力に合わせてリサイズを行います。


```matlab:code
% データの水増し(反転・移動)
augmentedTrainingData = transform(trainingData,@augmentData);
% 前処理（リサイズ）
preprocessedTrainingData = transform(augmentedTrainingData,@(data)preprocessData(data,inputSize));
```
# 学習オプションを指定


学習率は初期値は0.1とし、30エポックごとに0.8かけて小さくしていきます。


```matlab:code
options = trainingOptions('sgdm', ...
        'MiniBatchSize', 16, ....
        'InitialLearnRate',1e-1, ...
        'LearnRateSchedule', 'piecewise', ...
        'LearnRateDropPeriod', 30, ...
        'LearnRateDropFactor', 0.8, ...
        'MaxEpochs', 300, ...
        'VerboseFrequency', 30, ...        .
        'Shuffle','every-epoch');
```
# SSDネットワークの学習


学習を実行します。(GPUがない場合は数十時間かかります。)


```matlab:code
doTraining = false;
if doTraining
    [detector, info] = trainSSDObjectDetector(preprocessedTrainingData,lgraph,options);
    save('ssdResNet50VehicleExample_20a','detector');
else
    % 学習済みモデルは以下でダウンロード可能
    if ~exist('ssdResNet50VehicleExample_20a.mat','file')
        disp('Downloading pretrained detector (44 MB)...');
        pretrainedURL = 'https://www.mathworks.com/supportfiles/vision/data/ssdResNet50VehicleExample_20a.mat';
        websave('ssdResNet50VehicleExample_20a.mat',pretrainedURL);
    end
    pretrained = load('ssdResNet50VehicleExample_20a.mat');
    detector = pretrained.detector;
end
```
# テスト画像の読み込みと学習済みSSDによる検出


テストデータから1枚画像を読み込み、検出を実行します。




実行結果を可視化し、目視で確認します。


```matlab:code
data = read(testData); % テストデータから1枚読み込み
I = data{1,1}; % 画像のみ取り出す
I = imresize(I,inputSize(1:2));
[bboxes,scores] = detect(detector,I, 'Threshold', 0.4);

I = insertObjectAnnotation(I,'rectangle',bboxes,scores);
figure
imshow(I)
```

![figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/53271030-abd2-8b49-b4f7-0c3375d525a4.png)


# テストデータセットを使い検出器の性能評価


テストデータセット全体を使って検出器の性能を評価します。




テストデータの真値と検出結果を使ってPR曲線を計算します。




今回はIoU(どれぐらい真値のバウンディングボックスと検出結果のバウンディングボックスが重なるか)が




0.5以上の検出を対象します。


```matlab:code
% テストデータに前処理適用(ただし、データの拡張は行わない)
preprocessedTestData = transform(testData,@(data)preprocessData(data,inputSize));

% 全てのテストデータに検出器をかける (スコアが0.4以上のものを検出結果として出力)
detectionResults = detect(detector, preprocessedTestData, 'Threshold', 0.4);

% 平均精度, 再現率, 適合率を計算
[ap,recall,precision] = evaluateDetectionPrecision(detectionResults, preprocessedTestData);

% PR曲線を表示
figure
plot(recall,precision)
xlabel('Recall')
ylabel('Precision')
grid on
title(sprintf('Average Precision = %.2f',ap))
axis([0 1 0 1]);
```

![figure_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/455f1879-2fd7-b5f5-786c-16005bcb12e6.png)


# 学習したSSDをNVIDIA Jetson Nano向けにCUDAコード生成


学習したSSDの検出器をNVIDIA Jetson Nanoに実装します。GPU実装には下記のToolboxが必要です。



   -  MATLAB Coder 
   -  GPU Coder 
   -  [GPU Coder Support Package for NVIDIA GPUs](https://www.mathworks.com/help/releases/R2020a/supportpkg/nvidia/index.html) (GPU Coderのアドオン) 



Jetson実装に関連するハードウェアやソフトウェアの設定は上記のアドオンの[ヘルプ](https://www.mathworks.com/help/releases/R2020a/supportpkg/nvidia/setup-and-configuration.html)をご覧ください。




セットアップが完了していないと下記は実行できないのでご注意ください。


## Jetsonに接続


`jetson`コマンドでJetsonに接続します。


```matlab:code
hwobj = jetson('192.168.55.1','nvidia','nvidia')
```
```text:output
Checking for CUDA availability on the Target...
Checking for 'nvcc' in the target system path...
Checking for cuDNN library availability on the Target...
Checking for TensorRT library availability on the Target...
Checking for prerequisite libraries is complete.
Gathering hardware details...
Checking for third-party library availability on the Target...
Gathering hardware details is complete.
 Board name         : NVIDIA Jetson TX1
 CUDA Version       : 10.0
 cuDNN Version      : 7.3
 TensorRT Version   : 5.0
 GStreamer Version  : 1.14.5
 V4L2 Version       : 1.14.2-1
 SDL Version        : 1.2
 Available Webcams  : MicrosoftÂ® LifeCam Cinema(TM)
 Available GPUs     : NVIDIA Tegra X1
hwobj = 
  jetson のプロパティ:

       DeviceAddress: '192.168.55.1'
                Port: 22
           BoardName: 'NVIDIA Jetson TX1'
         CUDAVersion: '10.0'
        cuDNNVersion: '7.3'
     TensorRTVersion: '5.0'
          SDLVersion: '1.2'
         V4L2Version: '1.14.2-1'
    GStreamerVersion: '1.14.5'
             GPUInfo: [1x1 struct]
          WebcamList: {'MicrosoftÂ® LifeCam Cinema(TM)'}

```
## Jetson Nanoのカメラに映すテスト画像を表示


画像をディスプレイに最大サイズで表示します。


```matlab:code
data = read(testData);
I = data{1,1};
figure;
set(gcf,'Visible','on','units','normalized','outerposition',[0 0 1 1]);
imshow(I)
```

![figure_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/9b2f5923-a7ad-09a3-1ba0-5128e682a83d.png)



## Jetsonに接続されているカメラで画像取得


表示している車両画像を取り込みます。


```matlab:code
clear cam display;
cam = hwobj.webcam; % カメラデバイスを取得
img = snapshot(cam); % 1枚画像取得
display = hwobj.imageDisplay; % ディスプレイを取得
figure;
display.image(img); % ディスプレイに表示
```

![figure_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/48ebe525-3e4d-f0ed-0c8f-eed28c29df4f.png)



## 車両検出のプログラムを作成
```matlab:code
type vehicleDetection.m
```
## MATLAB上で作成した車両検出プログラムを実行


Jetsonのカメラから画像をMATLAB上に取り込んで処理します。




まだ、Jetson上でプログラムが動いているわけではないことに注意してください。


```matlab:code
vehicleDetection
```
## CUDA Cをコード生成しJetsonでビルド


Jetson Nano向けにCUDAコードを生成し、ビルドします。




クロスコンパイルではなくターゲット上でのコンパイルのためCUDA ToolkitはホストPCには不要です。




SSDの推論はTensorRTをバックエンドとして使用します。


```matlab:code
cfg = coder.gpuConfig('exe');
cfg.Hardware = coder.hardware('NVIDIA Jetson');
cfg.Hardware.BuildDir = '~/remoteBuildDir';
cfg.GenerateExampleMain = 'GenerateCodeAndCompile';
cfg.GenerateReport = true;
cfg.LaunchReport = true;
codegen('-config ',cfg,'vehicleDetection','-report','-v');
```

## Jetson上でアルゴリズム実行


ビルドしたJetson Nano上の実行ファイルを実行します。


```matlab:code
clear hwobj cam display;
hwobj = jetson
```
```text:output
Checking for CUDA availability on the Target...
Checking for 'nvcc' in the target system path...
Checking for cuDNN library availability on the Target...
Checking for TensorRT library availability on the Target...
Checking for prerequisite libraries is complete.
Gathering hardware details...
Checking for third-party library availability on the Target...
Gathering hardware details is complete.
 Board name         : NVIDIA Jetson TX1
 CUDA Version       : 10.0
 cuDNN Version      : 7.3
 TensorRT Version   : 5.0
 GStreamer Version  : 1.14.5
 V4L2 Version       : 1.14.2-1
 SDL Version        : 1.2
 Available Webcams  : MicrosoftÂ® LifeCam Cinema(TM)
 Available GPUs     : NVIDIA Tegra X1
hwobj = 
  jetson のプロパティ:

       DeviceAddress: '192.168.55.1'
                Port: 22
           BoardName: 'NVIDIA Jetson TX1'
         CUDAVersion: '10.0'
        cuDNNVersion: '7.3'
     TensorRTVersion: '5.0'
          SDLVersion: '1.2'
         V4L2Version: '1.14.2-1'
    GStreamerVersion: '1.14.5'
             GPUInfo: [1x1 struct]
          WebcamList: {'MicrosoftÂ® LifeCam Cinema(TM)'}

```
```matlab:code
hwobj.setDisplayEnvironment('0.0');
pid = hwobj.runApplication('vehicleDetection');
```
```text:output
### Launching the executable on the target...
Executable launched successfully with process ID 10988.
Displaying the simple runtime log for the executable...

Note: For the complete log, run the following command in the MATLAB command window:
system(hwobj,'cat /home/nvidia/remoteBuildDir/MATLAB_ws/R2020a/C/IPCV_Eval_Kit/demo_files/vehicleDetection.log')
```


下記のような実行結果が得られます。(Jetson Nano上の実行結果をVNCを使ってキャプチャ)

![image_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14336/2e5bda04-c84d-42f9-503d-343bdb724f8c.png)



今回はバックボーンがResNet50のためJetson Nano上で数fps程度の速度しか得られません。




[Mobilenet v2などをバックボーンにする](https://www.mathworks.com/help/releases/R2020a/vision/examples/create-ssd-object-detection-network.html)とより軽量で高速な検出器が学習できます。







実行中のプロセスを停止


```matlab:code
% hwobj.killProcess(pid);
```
# まとめ


SSDによる物体検出器の学習、推論およびGPUコード生成について紹介しました。


# 参考

   -  [Object Detection Using SSD Deep Learning](https://jp.mathworks.com/help/releases/R2020a/vision/examples/object-detection-using-single-shot-detector.html) 
   -  [Code Generation for Object Detection by Using Single Shot Multibox Detector](https://jp.mathworks.com/help/releases/R2020a/vision/examples/code-generation-for-object-detection-using-single-shot-multibox-detector.html) 




# 謝辞


本記事は @eigs さんの[livescript2markdown](https://github.com/minoue-xx/livescript2markdown)を使わせていただいてます。



# 目次へのリンク


[MATLABによる画像処理・コンピュータービジョン入門目次](https://qiita.com/tohruk/items/e5f7939b622bcf2e3c4c)