[Amazon SageMaker] イメージ分類で画面の一部に写っているものを検出してみました
1 はじめに
CX事業本部の平内(SIN)です。
最近、棚に陳列された商品を確認する作業を進めています。
ここまで、商品を検出するためのモデル作成には、Amazon SageMaker(以下、SageMaker)のビルトインアルゴリズムである物体検出(object-detection)を利用してきました。
マーカーなどで、商品の写っている部分を特定することで、ある程度の精度を上げることは可能なのですが、要求に耐えるモデルを作成するためには、一定量の精度の高いデータセットが必要であり、アノテーション作業を伴うデータセット作成のコストを削減することは、なかなか難しいと感じていました。
今回は、アルゴリズムを同じくSageMakerのビルトインであるイメージ分類(image-classification)に変更することで、「データセットの作成コストを大きく下げることができないか」という検討の記録です。
2 image-classification
イメージ分類(image-classification)は、画像自体がどのようなシーンであるかを分類するもので、画像とラベルだけでデータセットが、作成可能です。
ここまで使用してきた物体検出(object-detection)では、ターゲットを囲ったBounding boxが必要であり、これが必要なくなることでデータセット作成のコストは格段に下がります。
※ Ground TruthのJob作成画面より
image-classificationは、本来、画面自体に何が写っているかを分類するもですが、既に、商品の位置を特定し、部分画像として切り出しているので、この切り出した部分を、「全画面」として扱い推論することで、object-detectionの代替とします。
下記は、検出対象を中央の水色枠に固定し、推論を行っている様子です。
3 データセット
準備したデータセットは、白地の上に商品を置いて、800*600で撮影した画像です。 各ラベルごと80枚用意しましたが、フォルダ名をラベル名にして、その下に画像を置いて、プログラムで処理すれば、あっという間に作成できます。
4 学習
5種類のラベル各80枚(計400枚)の画像を8:2(320:80)に分けて学習用と検証用に使用しました。
設定したパラメータは、以下のようになっています。
ml.p2.xlargeでepoch=5で回して143秒でした。
以下は、ログの抜粋ですが、Validation-accuracyが、あっという間に1.0になっているのに驚きます。
Epoch[0] Train-accuracy=0.521875 Epoch[0] Train-top_k_accuracy_5=1.000000 Epoch[0] Time cost=6.138 Epoch[0] Validation-accuracy=0.962500 Epoch[1] Train-accuracy=0.984375 Epoch[1] Train-top_k_accuracy_5=1.000000 Epoch[1] Time cost=2.210 Epoch[1] Validation-accuracy=0.912500 Epoch[2] Train-accuracy=0.993750 Epoch[2] Train-top_k_accuracy_5=1.000000 Epoch[2] Time cost=2.168 Epoch[2] Validation-accuracy=1.000000 Epoch[3] Train-accuracy=1.000000 Epoch[3] Train-top_k_accuracy_5=1.000000 Epoch[3] Time cost=2.167 Epoch[3] Validation-accuracy=1.000000 Epoch[4] Train-accuracy=1.000000 Epoch[4] Train-top_k_accuracy_5=1.000000 Epoch[4] Time cost=2.166 Epoch[4] Validation-accuracy=1.000000
5 動画での利用
以下は、Webカメラの画像を、推論しているコードです。
from boto3.session import Session import json import cv2 profile = 'developer' endPoint = 'sampleEndPoint' classes = ['ASPARA','CRATZ','OREO','PRETZEL','PRIME'] deviceId = 3 # Webカメラ height = 600 width = 800 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) 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)) # 切り出す部分の定義 x1 = int(width/2-80) x2 = int(width/2+80) y1 = int(height/2-120) y2 = int(height/2+120) while True: # カメラ画像取得 ret, frame = cap.read() if(frame is None): continue # 部分画像の切り出し img = frame[y1: y2, x1: x2] # 推論 _, jpg = cv2.imencode('.jpg', img) results = sageMake.invoke(jpg.tostring()) # 結果表示 probabilitys = {} for i,result in enumerate(results): probabilitys[classes[i]] = result probabilitys = sorted(probabilitys.items(), key = lambda x:x[1], reverse=True) (name, probability) = probabilitys[0] str = "{} {}".format(name, probability) cv2.putText(frame,str,(20, int(height)-20), cv2.FONT_HERSHEY_SIMPLEX, 2, (255,255,255), 2, cv2.LINE_AA) # 水色枠の描画 frame = cv2.rectangle(frame,(x1, y1), (x2, y2), (255,255,0),3) cv2.imshow('frame', frame) # カメラ画像 cv2.imshow('img', img) # 切り出し画像 cv2.waitKey(1) cap.release() cv2.destroyAllWindows()
6 最後に
イメージ分類(image-classification)は、物体検出(object-detection)に比較して、非常に軽量です。そして、データセットの作成コストは、比較にならないぐらい低いと思います。
運用場面をよく考察し、うまく適用していければと考えています。