みらいテックラボ

音声・画像認識や機械学習など, 週末プログラマである管理人が興味のある技術の紹介や実際にトライしてみた様子などメモしていく.

YOLOv3 + TensorFlow 2.0を試してみる

昨年末に, こちら[1] のページで, YOLOv3アルゴリズムをTensorFlow 2.0で実行できるように対応したバージョンがあることを知りました.
(TensorfFlow 1.xで動作するものがあることは知ってましたが....)

f:id:moonlight-aska:20200112104453p:plain:w500

現在, ピープルカウンタの開発[2][3]でYOLOv3[4]を利用しているので興味がわき, 少し試してみることにした.

基本的には, こちら[5]Githubのページのリポジトリをダウンロードし, 動作させると問題なく動きます.
ただ, グレースケール画像を扱ったり, モデルの入力画像サイズを変更したりする場合, 正常に動作しなかったので, メモとしてまとめておく.


1. 基本的な動作
Githubの説明のとおりやれば, 問題なく動作しました.

1.1 学習済YOLOv3モデルの変換

# yolov3
wget https://pjreddie.com/media/files/yolov3.weights -O data/yolov3.weights
python convert.py

# yolov3-tiny
wget https://pjreddie.com/media/files/yolov3-tiny.weights -O data/yolov3-tiny.weights
python convert.py --weights ./data/yolov3-tiny.weights --output ./checkpoints/yolov3-tiny.tf --tiny

1.2 物体検知

# yolov3
python detect.py --image ./data/meme.jpg

# yolov3-tiny
python detect.py --weights ./checkpoints/yolov3-tiny.tf --tiny --image ./data/street.jpg

# webcam
python detect_video.py --video 0

# video file
python detect_video.py --video path_to_file.mp4 --weights ./checkpoints/yolov3-tiny.tf --tiny

# video file with output
python detect_video.py --video path_to_file.mp4 --output ./output.avi


2. グレースケール画像対応
ピープルカウンタの開発で学習したモデル(224x224x1)をconvert.pyで変換しようとすると, 以下のようなエラーが発生する.

       :
WARNING:tensorflow:Model was constructed with shape Tensor("input_2:0", shape=(None, None, None, 3), dtype=float32) for input (None, None, None, 3), but it was re-called on a Tensor with incompatible shape (None, None, None, 1).
W0112 10:48:52.597801 140539316311872 network.py:847] Model was constructed with shape Tensor("input_2:0", shape=(None, None, None, 3), dtype=float32) for input (None, None, None, 3), but it was re-called on a Tensor with incompatible shape (None, None, None, 1).
Traceback (most recent call last):
  File "convert.py", line 39, in <module>
    app.run(main)
  File "/home/aska/anaconda3/envs/yolov3-tf2-gpu/lib/python3.7/site-packages/absl/app.py", line 299, in run
    _run_main(main, args)
  File "/home/aska/anaconda3/envs/yolov3-tf2-gpu/lib/python3.7/site-packages/absl/app.py", line 250, in _run_main
    sys.exit(main(argv))

       :

  File "/home/aska/anaconda3/envs/yolov3-tf2-gpu/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/input_spec.py", line 213, in assert_input_compatibility
    ' but received input with shape ' + str(shape))
ValueError: Input 0 of layer conv2d is incompatible with the layer: expected axis -1 of input shape to have value 3 but received input with shape [None, None, None, 1]

原因を調べた結果, yolov3-tf2/models.pyで, channels=3固定でコーディングしてる箇所があり, そこを指定できるように変更することで問題なく変換できることを確認した.

[修正前]

def Darknet(name=None):
    x = inputs = Input([None, None, 3])
    x = DarknetConv(x, 32, 3)
    x = DarknetBlock(x, 64, 1)
       :

def DarknetTiny(name=None):
    x = inputs = Input([None, None, 3])
    x = DarknetConv(x, 16, 3)
    x = MaxPool2D(2, 2, 'same')(x)
    x = DarknetConv(x, 32, 3)
       :

[修正後]

def Darknet(name=None, channels=3):
    x = inputs = Input([None, None, channels])
    x = DarknetConv(x, 32, 3)
    x = DarknetBlock(x, 64, 1)
       :

def DarknetTiny(name=None, channels=3):
    x = inputs = Input([None, None, channels])
    x = DarknetConv(x, 16, 3)
    x = MaxPool2D(2, 2, 'same')(x)
    x = DarknetConv(x, 32, 3)
       :

グレースケール画像を扱うときは, convert.pyやdetect.py等からDarkent/DarknetTinyを呼び出すときに引数でchannels=1を指定すること.


3. モデルの入力画像サイズ対応
ピープルカウンタをJetson Nanoで動かすために, モデルの入力画像サイズを416から224に変更してモデルを学習しているのだが, 実行すると人物検出領域のボックス位置がおかしい.

f:id:moonlight-aska:20200112113047p:plain:w400

こちらも, 原因を調べてみると, yolov3-tf2/models.pyで, 入力画像サイズを416固定で処理している箇所が見つかった.
こちらは, とりあえず入力画像サイズを224に変更し, こちらが影響していることを確認した.

[変更前]

yolo_anchors = np.array([(10, 13), (16, 30), (33, 23), (30, 61), (62, 45),
                         (59, 119), (116, 90), (156, 198), (373, 326)],
                        np.float32) / 416
yolo_anchor_masks = np.array([[6, 7, 8], [3, 4, 5], [0, 1, 2]])
yolo_tiny_anchors = np.array([(10, 14), (23, 27), (37, 58),
                              (81, 82), (135, 169),  (344, 319)],
                             np.float32) / 416
yolo_tiny_anchor_masks = np.array([[3, 4, 5], [0, 1, 2]])

[変更後]

yolo_anchors = np.array([(10, 13), (16, 30), (33, 23), (30, 61), (62, 45),
                         (59, 119), (116, 90), (156, 198), (373, 326)],
                        np.float32) / 224
yolo_anchor_masks = np.array([[6, 7, 8], [3, 4, 5], [0, 1, 2]])
yolo_tiny_anchors = np.array([(10, 14), (23, 27), (37, 58),
                              (81, 82), (135, 169),  (344, 319)],
                             np.float32) / 224
yolo_tiny_anchor_masks = np.array([[3, 4, 5], [0, 1, 2]])

コード変更後に確認すると, 一応問題は解決されたようだ!!.

f:id:moonlight-aska:20200112113131p:plain:w400


YOLOv3 + TensorFlow 2.0で, ピープルカウンタが一応動作するようになった. (Linux PC上だが...)
せっかく動作するところまで確認できたので, こんどJetson Nanoでどの程度の処理スピードなのか, 是非確認してみたい.
15FPS以上でると嬉しいのだが...

----
参照URL:
[1] Tensorflow 2.0を用いた物体検知
[2] ピープルカウンタを考えてみる(7)
[3] Jetson NanoでIntel RealSenseを試してみる(3)
[4] YOLO: Real-Time Object Detection
[5] YoloV3 Implemented in Tensorflow 2.0