この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Amazon SageMager Neo(以下、Neo)を使用すると、既存のモデルを簡単にRaspberryPiで動作させる事ができます。
[Amazon SageMaker] イメージ分類のモデルをNeoで最適化してRaspberryPi Model 4で使用してみました
今回、DLRを利用する部分は、上記と同じですが、カメラをWebカメラに変更して、OpenCVの画像を入力として扱う要領を試してみました。
はじめに動作している様子です。
2 入力データ
Amazon SageMakerのイメージ分類(組み込みアルゴリズム)の入力となる、{"data":[1,3,224,224]} の形式にOpenCVの画像データを使用するためには、以下の点に考慮が必要です。
(1) 正方形
カメラから取得できる画像のサイズは、カメラの出力に依存しますが、通常、これは長方形になっていると思います。 このため、まず、正方形に変形(トリミング)する必要があります。
下記のコードでは、フレームが横長である場合、高さを1辺の長さとした正方形にトリミングしています。
img = img[0 : int(height), 0 : int(height)]
(2) サイズ
サイズを244*244に変更する場合、resize() が利用可能です。正方形以外の画像で、この変換を行うと、画像が変形してしまうので注意が必要です。
img = cv2.resize(img, dsize=(224, 224))
(3) RGB
色相チャンネルのRBGの順は、期待されるもの違います。このため、下記のように変換が必要です。
# BGR => RGB
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
(4) ndarray
モデルの入力は、numpy.ndarrayです。OpenCVの画像データは、元々、numpy.ndarrayになっているので、この意味では変換の必要はありませんが、期待される、shapeが、(チャンネル数、列、行)であるため、この順番を入れ替える必要がります。
# (244,244,3) => (3,244,244)
img = img.transpose((2, 0, 1))
3 コード
使用したすべてのコードは以下のとおりです。推論に時間を要するため、2秒に1回だけ推論して、結果表示を更新しています。具体的には、カメラのフレームレートを5fpsに設定し、10フレームに1回だけ推論処理しています。
import numpy as np
from dlr import DLRModel
from PIL import Image
import cv2
# Webカメラ
DEVICE_ID = 0
WIDTH = 800
HEIGHT = 600
FPS = 5
# Model
CLASSES = ['PORIPPY(GREEN)', 'OREO', 'CUNTRY_MAM', 'PORIPPY(RED)', 'BANANA',
'CHEDDER_CHEESE', 'PRETZEL(YELLOW)', 'FURUGURA(BROWN)', 'NOIR', 'PRIME',
'CRATZ(RED)', 'CRATZ(GREEN)', 'PRETZEL(BLACK)', 'CRATZ(ORANGE)', 'ASPARA',
'FURUGURA(RED)', 'PRETZEL(GREEN)']
model_path = './models'
class Model:
def __init__(self, model_path):
self.__model = DLRModel(model_path, 'cpu')
def inference(self, image):
out = self.__model.run({'data': image})
return out
def putText(frame, x, y, text):
font_size = 2
font_color = (255, 255, 255)
cv2.putText(frame, text, (x, y), cv2.FONT_HERSHEY_SIMPLEX, font_size, font_color, 3, cv2.LINE_AA)
return frame
def decode_fourcc(v):
v = int(v)
return "".join([chr((v >> 8 * i) & 0xFF) for i in range(4)])
def main():
# Model初期化
model = Model(model_path)
# カメラ初期化
cap = cv2.VideoCapture (DEVICE_ID)
# フォーマット・解像度・FPSの設定
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y','U','Y','V'))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
cap.set(cv2.CAP_PROP_FPS, FPS)
# フォーマット・解像度・FPSの取得
fourcc = decode_fourcc(cap.get(cv2.CAP_PROP_FOURCC))
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
print("fourcc:{} fps:{} width:{} height:{}".format(fourcc, fps, width, height))
# 推論は2秒に1回実行する
counter = 0
interval = 2
name = ""
probability = ""
while True:
# カメラ画像取得
_, frame = cap.read()
if(frame is None):
continue
frame = frame[0 : int(height), 0 : int(height)] # => 正方形
counter += 1
if(counter % (fps * interval) == 0):
# ModelのInput用に画像変換
image = cv2.resize(frame, dsize=(224, 224)) # => 244*244
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) # RGB => BGR
image = image.transpose((2, 0, 1)) # transpose shape (244,244,3)=>(3,244,244)
# 推論
out = model.inference(image)
print(out)
name = CLASSES[np.argmax(out[0])]
probability = "{:.5f}".format(np.max(out))
print("name:{} probability:{}".format(name,probability))
# 結果(テキスト)表示
frame = putText(frame, 10, int(height) - 100, name)
frame = putText(frame, 10, int(height) - 30, probability)
# 画像表示
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# VideoCaptureオブジェクト破棄
cap.release()
cv2.destroyAllWindows()
if __name__ == '__main__':
main()
4 最後に
今回は、Neoで生成したモデルをOpenCVで取得した画像で試してみました。
SageMakerのエンドポイントと違い、入力データの形式を自前で整合させる必要があるので、少し、手間がかかると思います。