こんにちは。セーフィーで画像認識エンジニアをやっている柏木です。
今回はセーフィーで行ったPoC (Proof of Concept) の一つである、商品棚のSemantic Segmentationについて紹介いたします!
背景と課題
大手スーパーマーケット様より、商品棚の欠品状況を解析したいとのお話がありました。欠品の状況が解析できれば、品出しのタイミングを最適化し、機会損失を削減することができます。イメージングチームではこれらの課題を解決すべく、PoCを行ってみることとしました!
こちらが実際の商品棚の写真になります。
Semantic Segmentation
欠品状況を解析するためのアルゴリズムとして、Semantic Segmentationを選定しました。Semantic Segmentationとは、画素ごとにカテゴリ付けを行うニューラルネットワークのアルゴリズムです。自動運転や、医療用途で利用されています。
Semantic Segmentationは
- 境界をはっきり出すことができる
- 重なりがある物体や不定形の物体に強い
ことが特徴のアルゴリズムになります。
別のアルゴリズム案として物体検出も考えましたが、重なった商品や小さな商品を検出するのが難しいことから、今回はSemantic Segmentationを採用しました。
DeepLabV3
DeepLabV3はGoogleが2017年に発表したSemantic Segmentationアルゴリズムです。Atrous畳み込みを直列に何層も重ね、またAtrous rateを変えて並列に繋げたことが新規性のネットワークとなっています。論文は『Rethinking Atrous Convolution for Semantic Image Segmentation』です。
セーフィーではエッジAI機能を搭載したカメラである『Safie One』を提供しています。そこで、Safie Oneでも動作可能な軽量なモデルとしてDeepLabV3を使用することとしました。
DeepLabV3の公式リポジトリはTensorflow実装になっています。セーフィーではPytorchの知見が多いので、Pytorch実装であるMMSegmentationを使用しました。こちらのリポジトリはDeepLabV3に限らず多くのモデルが実装されており、バックボーンも豊富で使いやすいです。
データセットとアノテーション
データセットはスーパーマーケット様より提供いただきました。モデル自体が軽量ということや、固定カメラにおける利用を想定していることから多くの画像のバリエーションは不要と判断しました。そこで、Train(学習用)・Validation(検証用)・Test(テスト用)含めて200枚程度としました。
アノテーションツールは弊社が日頃から利用させていただいている『FastLabel』を使用しました。FastLabelは物体検出、物体追跡、Segmentationなど豊富なタスクをアノテーションすることができます。クラウドベースのサービスのため、アノテーションした結果をチーム内で共有することも容易にできます。またUIが日本語なので使いやすいです。
FastLabelが出力するアノテーション形式は選ぶことができます。今回はMMSegmentationで利用することが容易だったマスク画像を使用しました。
アノテーションにおけるクラスは
- background
- package
- person
- wagon
の4種類としました。packageが実際の商品を表すカテゴリです。backgroundとpackageさえあれば欠品状況は解析できるのですが、人やワゴンが映り込むことでオクルージョン(隠れ)が生じることがあり、そのような状況は検出不能と扱う必要があるためのでperson、wagonと別カテゴリを用意しました。
マスク画像(黒がbackground, 赤がpackage)
MMSegmentationを使った学習
データセットClassの作成
独自データセットで学習するためには、まずそのデータセットClassを作る必要があります。 mmseg/datasets/以下にデータセットの設定ファイルを作成します。
class SaladDataset(CustomDataset): """Salad dataset.""" CLASSES = ('background', 'package', 'person', 'wagon') PALETTE = [[0, 0, 0], [0, 0, 192], [0, 192, 0], [192, 0, 0]] def __init__(self, **kwargs): super().__init__(img_suffix='.jpg', seg_map_suffix='.png', **kwargs)
おもにクラス(カテゴリ)の名前CLASSES、推論結果の画像出力時の色であるPALETTEを変更すれば大丈夫です。
作成したら mmseg/datasets/init.py に作成したクラスを追加します。
データセットConfigの作成
configs/base/datasets/以下に学習・テストの設定ファイルを作成します。
dataset_type = 'SaladDataset' data_root = 'data/salad' img_norm_cfg = dict( mean=[121.13, 118.48, 115.98], std=[3.14, 2.97, 3.15], to_rgb=True) crop_size = (512, 512) train_pipeline = [ dict(type='LoadImageFromFile'), dict(type='LoadAnnotations'), dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)), dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75), dict(type='RandomFlip', prob=0.5), dict(type='PhotoMetricDistortion'), dict(type='Normalize', **img_norm_cfg), dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255), dict(type='DefaultFormatBundle'), dict(type='Collect', keys=['img', 'gt_semantic_seg']), ] ~~ 省略 ~~ data = dict( samples_per_gpu=4, workers_per_gpu=4, train=dict( type='SaladDataset', data_root='/workspace/mmsegmentation/data/salad', img_dir='train/images', ann_dir='train/annotations', pipeline=train_pipeline),
特に
- dataset_typeの指定
- data_rootの指定
- data augmentationのパイプライン(train_pipeline, test_pipeline)
- train, val, testのtype、data_rootの指定
に注意してください。
学習・テストConfigの作成
configs/ユーザー名/以下にネットワークモデルの設定ファイルを作成します。
_base_ = [ '../_base_/models/deeplabv3_r50-d8.py', '../_base_/datasets/salad_data.py', '../_base_/default_runtime.py', '../_base_/schedules/schedule_20k.py' ] model = dict( backbone=dict(type='ResNet', depth=18), decode_head=dict(in_channels=512, channels=128, num_classes=4), auxiliary_head=dict(in_channels=256, channels=64, num_classes=4))
特に
- データセット設定ファイルの指定('../base/datasets/salad_data.py')
- クラス数(num_classes)
- 学習スケジュール('../base/schedules/schedule_20k.py')
の指定に注意してください。
環境
Dockerfileからイメージを作成し、コンテナを立ち上げます。ただ元のDockerfileではmmsegmentationをgitでcloneしてインストールしていますが、今回は独自データセットを利用するためにコードを変更しています。そのため、git cloneではなく現在のディレクトリをマウントするようにする必要があります。また、データセットがあるフォルダをマウントしてください。
学習
学習はtools/train.pyで可能です。今までの設定で準備は終わっているので、学習・テストのconfigファイルを引数で指定すればOKです! 学習時間はGPUのNVIDIA GeForce RTX2080 Tiを使用して1時間ほどで終わりました。
評価結果
tools/test.pyで評価することができます。推論だけではなく評価もスクリプトに含まれているので、そのまま表示されます。
IoUは正解の領域と推論した領域の重なり具合を評価する指標です。一方でAccuracyはカテゴリを正確に推測できている画素の割合の指標になります。
Class | IoU | Acc |
---|---|---|
background | 95.31 | 97.35 |
package | 85.38 | 92.61 |
person | 87.1 | 97.14 |
wagon | 78.65 | 86.24 |
クラス別の値をみるとbackgroundとpersonで高い値が出ています。一方wagonは少し苦手なようです。
aAccはすべての画像でAccuracyを出したときの値、mIoUは画像ごとのIoUの平均値、mAccは画像ごとのAccuracyの平均値です。
aAcc | mIoU | mAcc |
---|---|---|
96.13 | 86.61 | 93.33 |
精度という観点でみると9割以上を達成してます。勿論、今後さらなる改善に向けた精度向上は必要ですが、欠品検知という課題解決の目的では十分な性能と言っていいのではないかと考えています。
終わりに
今回はセーフィーで行っている、商品棚のSemantic Segmentationに関する取り組みを紹介しました。これ以外にもお客さんの課題を解決するためのPoCをどんどん行っていく予定ですので、興味がある方は是非チェックしてみてください!
また一緒に働く仲間も募集しています!採用ページはこちらにあるので、よろしくお願いします!