みらいテックラボ

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

Jetson NanoでIntel RealSenseを試してみる(3)

以前から開発を進めているピープルカウンタ[1]で, 人物の検出にYOLOv3[2]を試してみたいと思い, Jetson Nanoを購入した.
前回は, Jetson NanoでYOLOv3のセットアップについて紹したが, 今回はD415の出力をYOLOv3の入力として物体検知を動かすところを紹介する.


関連記事:
Jetson NanoでIntel RealSenseを試してみる(1)
Jetson NanoでIntel RealSenseを試してみる(2)
・Jetson NanoでIntel RealSenseを試してみる(3)
Jetson NanoでIntel RealSenseを試してみる(4)


1. ctypes利用
PythonからYOLOv3を利用する方法については, 「YOLOv3を試してみる(3) 」[3]に記載したように, いくつか方法がある.
今回は, ctypes(pythonC言語のライブラリを操作するためのライブラリ)を利用する方法を用いた.

1.1 PythonからYOLOv3呼び出し
オリジナルのリポジトリ[4]をクローンし, "darknet"のディレクトリ下のpython/darknet.pyなるサンプルコードがあるので, これをベースに, OpenCVの画像データを渡してYOLOv3で物体検出を行い, 検出結果を返すように, 以下のような処理を追加した.

# OpenCVの画像データをYOLOv3の画像データに変換
def cvimage_to_image(img):
    img = img.transpose(2, 0, 1)
    c, y, x = img.shape
    img = (img / 255.).flatten()
    data = c_array(c_float, img)
    image = IMAGE(x, y, c, data)
    return image

# YOLOv3を呼び出して, 物体検出を実行
# (detect関数を参照のこと)
def detect_cvimage(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45):
    im = cvimage_to_image(image)
    num = c_int(0)
    pnum = pointer(num)
    predict_image(net, im)
    dets = get_network_boxes(net, im.w, im.h, thresh, hier_thresh, None, 0, pnum)
    num = pnum[0]
    if (nms): do_nms_obj(dets, num, meta.classes, nms);

    res = []
    for j in range(num):
        for i in range(meta.classes):
            if dets[j].prob[i] > 0:
                b = dets[j].bbox
                res.append((meta.names[i], dets[j].prob[i], (b.x, b.y, b.w, b.h)))
    res = sorted(res, key=lambda x: -x[1])
    free_detections(dets, num)
    return res

1.2 D415処理ルーチン
D415の入力処理のメインループから, 先のdetect_cvimage関数を呼び出すことで, 物体検出を行う.

#############################################
##      D415 + YOLOv3
#############################################
import pyrealsense2 as rs
import numpy as np
import cv2
import time

import darknet

WIDTH = 640
HEIGHT = 480
FPS = 15

OUTPUT_VIDEO_FILE = 'output.avi'

# Configure depth and color streams
pipeline = rs.pipeline()
config = rs.config()
config.enable_stream(rs.stream.color, WIDTH, HEIGHT, rs.format.bgr8, FPS)

# YOLOv3設定
net = darknet.load_net(b'./cfg/yolov3-tiny.cfg', b'./weights/yolov3-tiny.weights', 0)
meta = darknet.load_meta(b'./cfg/yolov3-tiny.data')

# 結果出力ファイル準備
fourcc = cv2.VideoWriter_fourcc(*'DIVX')    
output_movie = cv2.VideoWriter(OUTPUT_VIDEO_FILE, fourcc, 15, (WIDTH, HEIGHT))

# ストリーミング開始
profile = pipeline.start(config)

try:
    while True:
        # フレーム待ち(Color)
        frames = pipeline.wait_for_frames()
        color_frame = frames.get_color_frame()
        if not color_frame:
            continue

        # フレーム画像取得
        image = np.asanyarray(color_frame.get_data())
        rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

        # YOLOv3物体検出
        objs = darknet.detect_cvimage(net, meta, rgb)
        # 検出結果表示
        for obj in objs:
            x, y, w, h = obj[2]
            x, y, hw, hh = int(x), int(y), int(w/2), int(h/2)
            print('{} : {}, {}, {}, {}'.format(obj[0], x, y, hw*2, hh*2))
            image = cv2.rectangle(image, (x-hw, y-hh), (x+hw, y+hh), (0, 0, 255), 2)

        # ファイル出力
        output_movie.write(image)     
        # 入力&検出結果表示
        cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
        cv2.imshow('RealSense', image)
        if cv2.waitKey(1) & 0xff == 27:
            break

finally:
    # ストリーミング停止
    pipeline.stop()
    output_movie.release()        
    cv2.destroyAllWindows()

1.3 動作確認
D415を使って動作検証を行ったところ, 一応物体検出できているようだが, フレーム毎の処理が完全に間に合っていない.
f:id:moonlight-aska:20190828231844p:plain:w500

どこの処理で時間がかかっているか, 調べてみた.

  (省略)
[yolo] params: iou loss: mse, iou_norm: 0.75, cls_norm: 1.00, scale_x_y: 1.00
Total BFLOPS 5.571 
 Allocate additional workspace_size = 52.43 MB 
Loading weights from ./weights/yolov3-tiny.weights...
 seen 64 
Done!
Loaded - names_list: data/coco.names, classes = 80 
c_array 0.4820216559992332
predict_image 0.24248922199967637
detect_cvimage 0.790845716000149
Gtk-Message: 23:04:53.687: Failed to load module "canberra-gtk-module"
c_array 0.4701981270000033
predict_image 0.10289384199950291
detect_cvimage 0.6075017419998403
c_array 0.4704276900001787
predict_image 0.0853231870005402
detect_cvimage 0.5848415290001867
c_array 0.47496766799940815
predict_image 0.09125199099980819
detect_cvimage 0.5956493580006281
c_array 0.44825363700056187

その結果, 約0.6s/frameかかっており, OpenCVの画像データからYOLOv3に渡す画像データに変換する処理(c_array in darknet.py)で時間がかかていることが判明.

def c_array(ctype, values):
    arr = (ctype*len(values))()
    arr[:] = values
    return arr

コードがシンプルなだけに, これは簡単には高速できそうにないので, ctypesを使用する方法はいったん中断.
(どなたか, ここ高速化した人があれば教えてくださ~い.)

次回はもう一つのkeras-yolo3を試してみようと思う.

----
参照URL:
[1] ピープルカウンタを考えてみる(1) ~ (5)
[2] YOLO: Real-Time Object Detection
[3] YOLOv3を試してみる(3)
[4] GitHub - pjreddie/darknet: Convolutional Neural Networks





機械学習のための「前処理」入門

機械学習のための「前処理」入門

  • 作者:足立 悠
  • 発売日: 2019/06/06
  • メディア: 単行本(ソフトカバー)


PythonとKerasによるディープラーニング

PythonとKerasによるディープラーニング

  • 作者:Francois Chollet
  • 発売日: 2018/05/28
  • メディア: 単行本(ソフトカバー)