TensorFlowの学習済みモデルにカメラ画像を認識させる

Deep Learningで画像認識してみようと、手持ちのRaspberry Pi 2にUSBカメラを接続してから半年以上、進捗が無いので、とにかく何か動かしてみる。

現在Deep Learningのプラットフォームとして最もメジャーなTensorFlowが公式にRaspberry Piをサポートしているので、まずはTensorFlowを使うことにする。
TensorFlowはpipでインストールできる。筆者のRaspberry Piでは機械学習関係のPython環境はPython 2.7で揃えており、しかしながらPython 3のpipもインストールしており、pipをアップデートするとPython 3のpipが壊れるので、virtualenvを使用して、Python 2.7用のTensorFlowをインストールする。

公式ページの手順に従って、

virtualenv --system-site-packages -p python2.7 ./venv
source ./venv/bin/activate
pip install --upgrade pip
pip install --upgrade tensorflow
とすると、.whlファイルのダウンロードが途中で失敗し、
THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
というエラーになった。何度やっても、 pip install --no-cache-dir tensorflowとやっても、 pip install tensorflow==1.12などとバージョンを変えても、 時間帯を変えてやっても日を改めても駄目で、他に適当な方法が見つからなかったので、ダウンロードに失敗する.whlをwgetで取得してインストールした。
wget -t 0 https://www.piwheels.org/simple/tensorflow/tensorflow-1.13.1-cp27-none-linux_armv7l.whl
pip install tensorflow-1.13.1-cp27-none-linux_armv7l.whl 

今回はKeras DocumentationのApplicationのページを参考に、ImageNetで学習済みのモデルで画像認識させる。
同ページの"Classify ImageNet classes with ResNet50"の所にあるコードと、以前に作成したUSBカメラを動かすコードを組み合わせ、ResNet50をより少し小さく少し認識精度が高いとされるInceptionV3に変えて、次のコードにすると、無事に動いた。

from tensorflow.keras.applications.inception_v3 import InceptionV3
from tensorflow.keras.preprocessing import image
from tensorflow.keras.applications.inception_v3 import preprocess_input, decode_predictions
import numpy as np
import cv2

model = InceptionV3(weights='imagenet')

WINNAME = "Capture"
FRAME_INTERVAL = 30 # msec

CAPTURE_WIDTH = 1280
CAPTURE_HEIGHT = 720
CENTER_CROP_X1 = int((CAPTURE_WIDTH - CAPTURE_HEIGHT) / 2)
CENTER_CROP_X2 = CAPTURE_WIDTH - CENTER_CROP_X1
DISPLAY_WIDTH = 480
DISPLAY_HEIGHT = 480

ret = False
while ret == False:
    cap = cv2.VideoCapture(0)
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, CAPTURE_WIDTH)
    cap.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, CAPTURE_HEIGHT)
    ret, img = cap.read()

key = 0
while key != ord('q'):
    ret, img = cap.read()
    img = img[:, CENTER_CROP_X1:CENTER_CROP_X2]  # crop center square
    cv2.moveWindow(WINNAME, 0, 0)
    x = cv2.resize(img, (299, 299))  # input size of InceptionV3
    x = image.img_to_array(x)
    x = x[:, :, [2,1,0]]  # BGR -> RGB
    x = np.expand_dims(x, axis=0)
    x = preprocess_input(x)

    preds = model.predict(x)
    preds = decode_predictions(preds, top=3)[0]

    img = cv2.resize(img, (DISPLAY_WIDTH, DISPLAY_HEIGHT))
    for i in range(3):
        _, class_name, accuracy = preds[i]
        if accuracy >= 0.2:
            print("{} ({:.3f})".format(class_name, accuracy))
            cv2.rectangle(img, (0, 20*(i+1)), (200, 20*i), (255, 255, 0), cv2.cv.CV_FILLED)
            cv2.putText(img, "{} ({:.3f})".format(class_name, accuracy),
                        (0, 20*(i+1)-5), cv2.cv.CV_FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0))
    cv2.imshow(WINNAME, img)

    key = cv2.waitKey(FRAME_INTERVAL)
    if key == ord('s'):
        cv2.imwrite('result.jpg', img)

cap.release()
cv2.destroyAllWindows()

●結果(Raspberry Pi 2 + Webcam C270で実施)


遅いだろうとは思っていたが、2011年製のMacBook Air(Intel Core i5 1.6GHz)でも起動に30秒、画像認識に1フレーム当たり0.5秒くらいかかるが、Raspberry Pi 2(v1.2)だと起動に3分、画像認識5秒くらいかかる。


やってみれば1日か2日でできることだが、この所、この1日や2日が取れない。
Raspberry Piにふさわしい小規模なニューラルネットワークを学習させたり、重み係数を8bitや1bitに量子化して計算量を下げたり、C言語にして高速化したりするのは、いつになることやら...