VGG16によるボトル画像識別のリベンジ

# はじめに

「[VGG16の結果をGrad-CAMで検証](https://qiita.com/yamamo-to/items/d899add39bdb3f41fcd4)」で検証した結果、無関係な背景部分が発火して「ボトル」が「障子」と判定されていた。そこで無関係な部分を切り取り対象物が最大となるように画像を切り取ったらどうなるかを試す。

# 画像の切り取り

まずはImageMagickのインストール[^1]。

[^1]: なぜか既にインストールされていた。

```bash:ターミナル
sudo apt install imagemagick
```

次に`convert`コマンドを使って画像を変換する。このとき

- ボトル部分の切り取り
- 画像のタテヨコ比を維持したまま
- VGG16の入力画像のサイズ 224x224 へリサイズ

する。オリジナル画像`boss.jpg`に対して、切り取った画像は`boss_crop_bg_white.jpg`である。

```bash:ターミナル
convert boss.jpg -crop 155x300+190+50 -resize 224x224 -gravity center -extent 224x224 boss_crop_bg_white.jpg
```

背景が黒の画像`boss_crop_bg_black.jpg`も用意する。

```bash:ターミナル
convert boss.jpg -crop 155x300+190+50 -resize 224x224 -background "rgb(0,0,0)" -gravity center -extent 224x224 boss_crop_bg_black.jpg
```

|![boss_crop_bg_white.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/f63f58d8-b4b0-07d0-8e9c-8bf5224762ee.jpeg)|
|:-:|

黒背景の画像は割愛[^2]。
[^2]: 何度やっても黒背景の画像がQiitaにアップロードできない。

# 結果

## 背景が白の場合

```bash:ターミナル
$ pipenv run python3 vgg16_test.py boss_crop_bg_white.jpg 
0.39188 898 n04557648 water bottle
0.24303 845 n04376876 syringe
0.10143 631 n03690938 lotion
0.05920 585 n03476991 hair spray
0.04263 720 n03937543 pill bottle
0.02618 680 n03825788 nipple
0.02432 440 n02823428 beer bottle
0.01932 737 n03983396 pop bottle, soda bottle
0.01780 711 n03916031 perfume, essence
0.00976 907 n04591713 wine bottle
```

```bash:ターミナル
$ pipenv run python3 trt_infer.py vgg16.trt boss_crop_bg_white.jpg 
[TensorRT] INFO: Glob Size is 607995296 bytes.
[TensorRT] INFO: Added linear block of size 12845056
[TensorRT] INFO: Added linear block of size 12845056
[TensorRT] INFO: Added linear block of size 1605632
[TensorRT] INFO: Deserialize required 1036159 microseconds.
0.27554 898 n04557648 water bottle
0.22802 845 n04376876 syringe
0.09374 720 n03937543 pill bottle
0.05751 631 n03690938 lotion
0.04994 585 n03476991 hair spray
0.03811 737 n03983396 pop bottle, soda bottle
0.03348 680 n03825788 nipple
0.02573 711 n03916031 perfume, essence
0.02069 440 n02823428 beer bottle
0.01260 512 n03109150 corkscrew, bottle screw
```

ChainerからVGG16を使用して識別した場合とTensorRTで識別した場合とで、双方とも「water bottle」と判定された！対象となる画像を切り取ることが重要そうだ。

## 背景が黒の場合

次に背景が黒の場合。

```bash:ターミナル
$ pipenv run python3 vgg16_test.py boss_crop_bg_black.jpg 
0.79459 898 n04557648 water bottle
0.09471 680 n03825788 nipple
0.07243 720 n03937543 pill bottle
0.01279 845 n04376876 syringe
0.00607 631 n03690938 lotion
0.00447 441 n02823750 beer glass
0.00257 711 n03916031 perfume, essence
0.00167 737 n03983396 pop bottle, soda bottle
0.00113 585 n03476991 hair spray
0.00087 899 n04560804 water jug
```

```bash:ターミナル
$ pipenv run python3 trt_infer.py vgg16.trt boss_crop_bg_black.jpg 
[TensorRT] INFO: Glob Size is 607995296 bytes.
[TensorRT] INFO: Added linear block of size 12845056
[TensorRT] INFO: Added linear block of size 12845056
[TensorRT] INFO: Added linear block of size 1605632
[TensorRT] INFO: Deserialize required 1031389 microseconds.
0.69833 898 n04557648 water bottle
0.10562 845 n04376876 syringe
0.05046 720 n03937543 pill bottle
0.02833 680 n03825788 nipple
0.02342 737 n03983396 pop bottle, soda bottle
0.02234 631 n03690938 lotion
0.00959 585 n03476991 hair spray
0.00834 711 n03916031 perfume, essence
0.00547 440 n02823428 beer bottle
0.00499 818 n04286575 spotlight, spot
```

こちらも双方とも「water bottle」と判定された。素晴らしい。

# まとめ

画像を切り取ることにより正しく「ボトル」と判定されるようになった。画像を切り取ったり対象物を分離することに夢中になる理由が理解できた。

# 残る疑問点

白と黒の背景により判定結果は同じであったが、黒背景の方がSoftmax後のスコアが驚くほど改善されている。ここから湧き上がる疑問として

- 黒背景はどんなときでも良いのか
- 画像が白基調なので黒によりコントラストがついたのが良いのか
- たまたま偶然なのか

である。Grad-CAMによる発火場所は殆ど差がないにも関わらず、である。

![gcam_bg_white.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/cffb6a38-7300-4a8c-7e02-4e39ae7b0da4.png)![gcam_bg_black.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252224/28549ea0-ac5c-0666-f6c6-fc84d3b03a1d.png)
(左)白背景のGrad-CAM画像、(右)黒背景のGrad-CAM画像。