「レジなし無人冷蔵庫」冷蔵庫内の商品を検出する物体検出モデルを作成してみました

2020.11.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

Amazon Web Services ブログでは、「レジなし無人販売冷蔵庫を構築についての記事」が公開されています。
レジなし無人販売冷蔵庫を構築できる、This is my Smart Cooler プログラムを公開しました

こちらでは、「お客様自らがレジなし無人販売冷蔵庫を迅速に構築し学習や体験ができる This is my Smart Cooler プログラムを発表します。」ということで、そのレシピがGithubで公開されています。
レジ無し無人販売冷蔵庫 構築レシピ

「これを真似てみたい」ということで、ここまで作業を進めています。

今回は、冷蔵庫の中の商品を検出するための物体検出モデルを作成してみました。

動画は、作成したモデルで商品を推論している様子です。

2 データセット

データセットは、下記の要領で作成したものです。
「レジなし無人冷蔵庫」アフィン変換した画像と背景を合成してデータセットを作成する

商品は、6種類です。元となるデータは、冷蔵庫内に色々な配置で商品を置いて撮影した56枚の画像です。

上記を、PowerPoint上で商品ごとの透過PNGに変換しました。それぞれ55枚程度となっています。

こちらを先の記事のスクリプトで、アフィン変換して背景と合成した画像がデータセットになっています。 各商品、500枚づつで合計3000件のデータになっています。

3 RecordIO形式

作成した、データセットは、Amazon SageMaker Ground Truthの出力形式となっているので、下記を使用してRecordIO形式に変換しました。

3000件のデータは、学習用と検証用で8:2に分割され、学習用は、2400件となっています。

全データ: 3000件 [0]jagabee: 500件 [1]chipstar: 500件 [2]butamen: 500件 [3]kyo_udon: 500件 [4]koara: 500件 [5]curry: 500件
jagabee => 400:100 残り:2500件
chipstar => 400:100 残り:2000件
butamen => 400:100 残り:1500件
kyo_udon => 400:100 残り:1000件
koara => 400:100 残り:500件
curry => 400:100 残り:0件
Train: 2400件

生成されたデータは、S3に配置します。

4 学習

学習は、SageMakerの組み込みアルゴリズム(物体検出)で行っています。

ml.p3.8xlarge✕1で10分弱です。

設定したパラメータは、以下のとおりです。

ベースモデルは、rasnet-50で、learning_rate:0.001、mini_batch_size:32、optimizer:sgdとなってます。 epochは、40と指定しましたが、eary_stoppingで21で止まりました。

もう少し、早く止めた方が、もっと良かったかも知れません。

epoch   mAP     smooth_l1       cross_entropy
-----------------------------------------
0       0.135   0.623   2.067
1       0.389   0.536   1.889
2       0.62    0.469   1.586
3       0.726   0.408   1.229
4       0.844   0.365   1.017
5       0.844   0.3     0.944
6       0.846   0.274   0.899
7       0.85    0.258   0.877
8       0.848   0.243   0.843
9       0.851   0.223   0.803
10      0.804   0.215   0.78
11      0.847   0.204   0.75
12      0.856   0.202   0.723
13      0.821   0.194   0.695
14      0.829   0.182   0.648
15      0.835   0.183   0.627
16      0.857   0.175   0.594
17      0.788   0.168   0.568
18      0.853   0.166   0.539
19      0.837   0.155   0.522
20      0.828   0.161   0.509
21      0.856   0.16    0.506

5 推論

推論のために使用したコードは、以下です。SageMakerでsampleEndPointという名前のエンドポイント作成して使用しています。

from boto3.session import Session
import json
import cv2
import glob
import time

profile = 'developer'
endPoint = 'sampleEndPoint'
CLASS_NAME = ["jagabee","chipstar","butamen","kyo_udon","koara","curry"]

linewidth = 2
colors = [(0,0,175),(175,0,0),(0,175,0),(175,175,0),(0,175,175),(175,175,175),(0,0,0)]

class SageMaker():
    def __init__(self, profile, endPoint):
        self.__endPoint = endPoint
        session = Session(profile_name = profile)
        self.__client = session.client('sagemaker-runtime')

    def invoke(self, image):
        data = self.__client.invoke_endpoint(
            EndpointName = self.__endPoint,
            Body = image,
            ContentType='image/jpeg'
        )
        results = data['Body'].read()
        return json.loads(results)

sageMake = SageMaker(profile, endPoint)


def main():
    deviceId = 0 # Webカメラ
    height = 600
    width = 800
    cap = cv2.VideoCapture(deviceId)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

    fps = cap.get(cv2.CAP_PROP_FPS)
    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    print("FPS:{} WIDTH:{} HEIGHT:{}".format(fps, width, height))

    while True:
        _, frame = cap.read()
        if(frame is None):
            continue

        _, jpg = cv2.imencode('.jpg', frame)
        detections = sageMake.invoke(jpg.tostring())

        for detection in detections["prediction"]:
            clsId = int(detection[0])
            confidence = detection[1]
            x1 = int(detection[2] * width)
            y1 = int(detection[3] * height)
            x2 = int(detection[4] * width)
            y2 = int(detection[5] * height)
            label = "{} {:.2f}".format(CLASS_NAME[clsId], confidence)
            if(confidence > 0.2):
                print(label)
                frame = cv2.rectangle(frame,(x1, y1), (x2, y2), colors[clsId],linewidth)
                frame = cv2.rectangle(frame,(x1, y1), (x1 + 150,y1-20), colors[clsId], -1)
                cv2.putText(frame,label,(x1+2, y1-2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA)

        cv2.imshow('frame', frame)
        cv2.waitKey(1)

    cap.release()
    cv2.destroyAllWindows()

main()

6 照明問題

実は、照明が商品の包装に反射して、なかなか物体検出の精度が上がらない問題がありました。データセットには、反射した画像も含んだつもりでしたが、なかなか効果が出せませんでした。

今回は、モデルの検証(推論)は、とりあえず、上部の照明を消灯して、間接照明的に100円ショップのライト置いて行っています。

実は、反射になんとか対応しようと、「偏光板」や「プラ板」をカメラの前に置いてみたり、照明にボリュームを付けてみたり、色々試してみたのですが、有効な手段は見つけられていません。

7 最後に

今回は、商品検出のために物体検出モデルを作ってみました。

なかなか時間が取れなくて進捗悪いです。「レジなし無人販売冷蔵庫」の道のりは、遠いです。