Help us understand the problem. What is going on with this article?

OpenCVのカメラ読み込みを高速化し、遅延時間も短くする

この記事について

Raspberry PiにUSBカメラを接続してOpenCVで読み込むと、速度(FPS)が非常に遅いことがあります。また、PiCameraを使っても、解像度が高いと速度が出ないことがあります。
これを高速化します。対策は、単に圧縮フォーマットを指定するだけです。

  • 速度が必要な場合は、非圧縮フォーマットじゃなくて、H264フォーマットなどを指定しましょう
    • cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H', '2', '6', '4'));
  • H264非サポートなWebカメラの場合には、MJPGが使える可能性があります。だけど、MJPGだとCPUパワーを結構使います
  • CAP_PROP_BUFFERSIZE の設定値を小さくすることで、遅延時間を短くすることも可能です

環境

  • Raspberry Pi 3 B+
    • 2019-07-10-raspbian-buster
    • Python3 + OpenCV 3.2.0 (今はaptでインストール可能になってた sudo apt install python3-opencv )
    • Pi Camera v2.1
  • USBカメラ
    • Logicool C270m

 * Jetson Nanoでもほぼ同じ結果でした。

測定方法

カメラはカバーせずに、明るいシーンを撮影(←重要)。
これをやらないと、シャッタースピードの影響を受けるためか、キャプチャ時間(cap.read() )が遅くなることがあります。

フォーマット指定と速度

フォーマットの指定方法

VideoCapture を開いた後に、CAP_PROP_FOURCC プロパティにfourcc形式でフォーマットを指定できます。
Pythonだと以下のようになります。C++でも同様です。

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H', '2', '6', '4'));

処理時間の比較

各カメラで、フォーマットと処理時間(cap.read() )の関係を比較しました。

PiCamera V2.1 (1920x1080)

フォーマット BGR3 MJPG H264
Capture時間 [msec] 249 72 34
CPU使用率 [%] 5 27 15

PiCamera V2.1の場合、デフォルトはBGR3フォーマットになります。1920x1080で読み込もうとするとめちゃくちゃ遅くなります。
MJPGだと少し速くなり、H264にすると、ほぼリアルタイムな30fpsが出ます。
CPU使用率はFPSが異なるので、あまり意味はない数字です。

PiCamera V2.1 (1280x720)

フォーマット BGR3 MJPG H264
Capture時間 [msec] 33 33 33
CPU使用率 [%] 13 27 8

1280x720だとどのフォーマットでもほぼリアルタイムな30fpsが出ます。
1920x1080に比べて速くなったのは、おそらくイメージセンサの動作モードが違うためです (https://picamera.readthedocs.io/en/release-1.12/fov.html )

CPU使用率を見ると、H264が最も少ないという面白い結果になりました。おそらくエンコード処理はハードウェアで行い、データ転送量もBGRに比べて少ないためだと思われます。

USB Camera (1280x720)

フォーマット YUYV MJPG H264
Capture時間 [msec] 135 33 -
CPU使用率 [%] 10 23 -

今回使ったLogicool C270mだと、デフォルトフォーマットはMJPGでした。なので、意識しないと遅いことには気づかないのですが、CPU使用率が高いです。あえてYUYVフォーマットを使うと当然遅くなります。H264は非サポートでした。

確か、上位機種のC920rはデフォルトがYUYVだったので、デフォルトのままだと遅いので注意が必要です。
C270mでも、他のアプリやコマンドからフォーマットを変えたら、その設定は残るので、コード内でフォーマット指定した方が無難です。
(実は本記事は、これに気付かずに遅いな~とハマったのが発端です。。。)

ちなみに、対応フォーマットはv4l2-ctl -d /dev/video1 --list-formats-ext で確認可能です。

注意

圧縮フォーマットを利用したら速度は向上しますが、当然画質は下がります。

テストコード

# -*- coding: utf-8 -*-
import sys
import time
import cv2

def decode_fourcc(v):
    # https://amdkkj.blogspot.com/2017/06/opencv-python-for-windows-playing-videos_17.html
    v = int(v)
    return "".join([chr((v >> 8 * i) & 0xFF) for i in range(4)])

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
# cap = cv2.VideoCapture(1)
# cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
# cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

# cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('B', 'G', 'R', '3'));
# cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'));
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H', '2', '6', '4'));
# cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y', 'U', 'Y', 'V'));
print(decode_fourcc(cap.get(cv2.CAP_PROP_FOURCC)))

if cap.isOpened() == False:
    print("cannot open")
    sys.exit(1)
_, img = cap.read() # dummy

cnt = 0
time_start = time.time()

try:
    while True:
        cnt += 1
        print(cnt)
        _, img = cap.read()
        # cv2.imshow('image', img)
        # key = cv2.waitKey(1)
        # if key == 27: # ESC
            # break
except KeyboardInterrupt:
    print("Exit loop by ctrl-c")

cap.release()
cv2.destroyAllWindows()
time_end = time.time()
print ("Capture time: {0}".format((time_end - time_start) * 1000 / cnt) + "[msec]")

[おまけ]シャッタースピードとキャプチャ処理時間

おそらくOpenCVの仕様として、露光時間(≒ 1/シャッタースピード)だけcap.read() で待たされます。
例えば、上記コードで、

_, img = cap.read()

のかわりに

_, img = cap.read()
time.sleep(0.01)

として、cap.read() の処理時間(キャプチャ処理時間)だけを測定したら20msecになりました。

さらに、time.sleep(0.1) として待ち時間をさらに増やしたら、キャプチャ処理時間は10~20msecにバラついたので、cap.read() を呼んだタイミングの露光が完了するのを待っているのだと思います。

待たされるのが嫌だという場合には、キャプチャ処理は別スレッドでやった方がよさそうですね。
↓の記事のような感じ。
https://qiita.com/PINTO/items/dd6ba67643bdd3a0e595

[おまけ]遅延時間も短くしたい

通常、「①カメラ画像キャプチャ(入力) ⇒ ②メイン処理 ⇒ ③出力」というループになると思います。
この時、ここまでの内容で、①キャプチャ処理時間は高速にすることが出来ました。
しかし、②メイン処理の処理時間が長くなると、遅延(Delay)が発生することがあります。

CAP_PROP_BUFFERSIZE の設定値を小さくすることで、この遅延を短くすることが出来ます。
(例. cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) )

CAP_PROP_BUFFERSIZE = 4(初期値)のとき、遅延 = 46msec
CAP_PROP_BUFFERSIZE = 1のとき、遅延 = 19msec
※ Raspberry Pi4, PiCamera v2.1, 1280x720, BGR3

image.png
CAP_PROP_BUFFERSIZE = 4の結果 (ストップウォッチを撮影し、HDMI出力)

image.png
CAP_PROP_BUFFERSIZE = 1の結果 (ストップウォッチを撮影し、HDMI出力)

iwatake2222
クソコード、放置するのも、同罪です (自分への戒め)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした