[Amazon SageMaker] 組み込みアルゴリズムのオブジェクト検出(ResNet-50)をRaspberryPi上のMXNetで利用してみました

2020.06.22

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

1 はじめに

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

先日、Amazon SageMaker(以下、SageMaker)のビルトインアルゴリズムであるオブジェクト検出(Object Detection)で作成したモデルを変換して、MacOSにインストールしたMXNet上で利用してみました。

今回は、同じ要領で、RaspberryPiで利用可能かどうかを確認してみました。

結論(感想)から言ってしまうと・・・動作はしますが、推論にちょっと時間がかかるため、リアルタイム的な利用は難しいという感じです。

2 RaspberryPi

Raspberri Piは、Model4で、OSは、今年5月の最新版(Raspberry Pi OS (32-bit) with desktop and recommended software)2020-05-27-raspios-buster-full-armhf.img です

$ cat /proc/cpuinfo  | grep Revision
Revision    : c03112

$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:    10
Codename:   buster

$ uname -a
Linux raspberrypi 4.19.118-v7l+ #1311 SMP Mon Apr 27 14:26:42 BST 2020 armv7l GNU/Linux

3 MXNet

MXNetは、最新の1.6.0が、ちょっとうまく動作させることが出来ず、1つ前の1.5.0を利用しました。

(1) Ver 1.6.0

下記のように、pipを利用すると、最新の1.6.0がインストールされます。

$ pip3 install mxnet

しかし、importの段階で、下記のようなエラーとなってしまいました。

$ python3
>>> import mxnet
OSError: /home/pi/.local/lib/python3.7/site-packages/mxnet/libmxnet.so: wrong ELF class: ELFCLASS64
>>>

今回は、上記エラーの回避を諦めました。

(2) Ver 1.5.0

1.5.0のインストールは、下記に置かれている、mxnet-1.6.0-py2.py3-none-any.whlを利用させて頂きました。
http://mxnet-public.s3.amazonaws.com/

pipでのインストールは、以下のとおりです。

$ pip3 install https://mxnet-public.s3.amazonaws.com/install/raspbian/mxnet-1.5.0-py2.py3-none-any.whl
Successfully built numpy
Installing collected packages: numpy, mxnet
Successfully installed mxnet-1.5.0 numpy-1.15.2

なお、上記のインストールだけでは、ライブラリ不足となっておりましたので、これを追加しました。

OSError: libopenblas.so.0: cannot open shared object file: No such file or directory

$ sudo apt-get install libopenblas-base

バージョン1.5.0が、利用可能になっている様子です。

 $ python3
Python 3.7.3 (default, Dec 20 2019, 18:57:59)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> import mxnet
>>> mxnet.__version__
'1.5.0'
>>>


参考:https://github.com/apache/incubator-mxnet/issues/17978

4 利用状況

RaspberryPi上で推論している様子です。時間はかかりますが、一応、動作できています。

下記が、ログの一例です。1回の推論に10秒弱かかっています。

$ python3 index.py 
[04:57:56] /work/mxnet/src/nnvm/legacy_json_util.cc:204: Warning: loading symbol saved by MXNet version 10501 with lower version of MXNet v10500. May cause undefined behavior. Please update MXNet if you encounter any issue
fps:24.0 width:800.0 height:600.0
inference start
[PRETZEL]0.8488292694091797 21, 45, 353, 487
inference finish:9.958193302154541[sec]
inference start
[PRIME]0.836922824382782 98, 118, 388, 499
inference finish:9.596295356750488[sec]
inference start
[OREO]0.9958897233009338 57, 155, 449, 502
inference finish:9.709836483001709[sec]
inference start
[CRATZ]0.9407762289047241 21, 88, 322, 468
inference finish:9.705724239349365[sec]
inference start
[ASPARA]0.7363123297691345 57, 134, 368, 511
inference finish:9.655270338058472[sec]

5 コード

使用したPython3のコードは、以下のとおりです。

変換で生成したモデルを、./modelに置き、Webカメラの画像を入力インターフェースに変換して推論にかけています。

import mxnet as mx
import cv2
import time
import numpy as np
from collections import namedtuple

# Webカメラ
DEVICE_ID = 0 
WIDTH = 800
HEIGHT = 600
FPS = 24

MODEL_PATH = './model/deploy_model_algo_1'
CLASSES = ['ASPARA','CRATZ','PRETZEL','PRIME','OREO']
COLORS = [(128, 0, 0),(0, 128, 0),(0, 0, 128),(128, 128, 0),(0, 128,128)]

def main():
    # Model Initialize
    SHAPE = 512
    input_shapes=[('data', (1, 3, SHAPE, SHAPE))]
    Batch = namedtuple('Batch', ['data'])
    sym, arg_params, aux_params = mx.model.load_checkpoint(MODEL_PATH, 0)
    mod = mx.mod.Module(symbol=sym, label_names=[], context=mx.cpu())
    mod.bind(for_training=False, data_shapes=input_shapes)
    mod.set_params(arg_params, aux_params)

    # Video Initialize
    cap = cv2.VideoCapture (DEVICE_ID)
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
    cap.set(cv2.CAP_PROP_FPS, FPS)
    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print("fps:{} width:{} height:{}".format(fps, width, height))

    while True:
        # カメラ画像取得
        _, frame = cap.read()
        if(frame is None):
            continue

        print("inference start")
        start = time.time()

        # 入力インターフェースへの画像変換
        frame = frame[0 : int(height), 0 : int(height)] # 800*600 -> 600*600
        frame = cv2.resize(frame, (SHAPE, SHAPE)) # 600*600 -> 512*512
        img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) # BGR -> RGB
        img = img.transpose((2, 0, 1)) # 512,512,3 -> 3,512,512
        img = img[np.newaxis, :] # 3,512,512 -> 1,3,512,512

        # 推論
        mod.forward(Batch([mx.nd.array(img)]))
        prob = mod.get_outputs()[0].asnumpy()
        prob = np.squeeze(prob)
        index = int(prob[:, 0][0])
        confidence = prob[0][1]
        x1 = int(prob[0][2] * SHAPE)
        y1 = int(prob[0][3] * SHAPE)
        x2 = int(prob[0][4] * SHAPE)
        y2 = int(prob[0][5] * SHAPE)

        # 表示
        print("[{}]{} {}, {}, {}, {}".format(CLASSES[index], confidence, x1, y1, x2, y2))
        if(confidence > 0.6): # 信頼度
            frame = cv2.rectangle(frame,(x1, y1), (x2, y2), COLORS[index],2)
            frame = cv2.rectangle(frame,(x1, y1), (x1 + 150,y1-20), COLORS[index], -1)
            label = "{} {:.2f}".format(CLASSES[index], confidence)
            frame = cv2.putText(frame,label,(x1+2, y1-2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1, cv2.LINE_AA)

        elapsed_time = time.time() - start
        print ("inference finish:{0}".format(elapsed_time) + "[sec]")

        # 画像表示
        cv2.imshow('frame', frame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

        #  読み飛ばし        
        for _ in range(int(fps*10)):
            _, dmy = cap.read()

    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

6 最後に

今回は、RaspberryPiにインストールしたMXNet上で試してみました。

推論に10秒程度必要ということで、リアルタイム(的)な要件には、ちょっと厳しいと思います。しかし、推論にかかるコストは非常に低くなると思うので、要件によっては有りかも知れません。