PythonでUSBカメラの映像を取り込み、ヒストグラム(RGB)をリアルタイムに表示する方法

今日はUSBカメラから映像を取り込み、ヒストグラム(RGB)をリアルタイムに表示させる方法を紹介します。

この記事で分かること

1.USBカメラから映像を取り込む方法
2.映像のヒストグラム(RGB)をリアルタイムに表示させる方法
3.グラフの表示位置やサイズを指定する方法
4.実行結果(YouTube)

ソースコード

以下にソースコードを示します。

import cv2
import time
from matplotlib import pyplot as plt

# USBカメラのID番号
no = 1

# USBカメラから映像を取り込む
cap_0 = cv2.VideoCapture(no)

cap_0.set(3, 1920)
cap_0.set(4, 1080)
cap_0.set(5, 30)

if cap_0.isOpened() is False:
    raise("IO Error")

colors = ("r", "g", "b")

while True:
    # 時間測定開始
    t1 = time.time()

    is_ok_0, img_0 = cap_0.read()

    print(cap_0.get(3), cap_0.get(4), cap_0.get(5))
    #print(is_ok_0)

    if is_ok_0 == False:
        continue

    # 画像の切り抜き(取り込み画像を1:1にする, Y:X)
    img_a = img_0[90:990, 510:1410]

    # プロットの初期化
    plt.clf()

    # RGBごとにヒストグラムを計算しプロット
    for i, channel in enumerate(colors):
        histgram = cv2.calcHist([img_a], [i], None, [256], [0, 256])
        plt.plot(histgram, color = channel)
        plt.xlim([0, 256])

    # プロットの更新間隔。カッコの中は時間(msec)
    plt.pause(0.01)

    # 時間計測終了
    elapsedTime = time.time() - t1

    # フレームレートの計算
    fps = "{:.0f}FPS".format(1/elapsedTime)

    # 画面にfpsを表示させる
    cv2.putText(img_a, fps, (50, 50), cv2.FONT_HERSHEY_PLAIN, 2, (255, 255, 0), 2, cv2.LINE_AA)
    
    # USBカメラから取り込んだ映像を表示
    cv2.imshow('cap_test_a', img_a)

    # 画面表示位置の指定(表示画面の名前, x座標, y座標)
    cv2.moveWindow('cap_test_a', 200, 50)  

    # グラフの表示位置を指定
    fig_place = plt.get_current_fig_manager()

    # カッコの中(windows画面のx座標,y座標,表示させる図の幅,図の高さ)
    fig_place.window.setGeometry(1112, 85, 640, 480)  

    k = cv2.waitKey(10)
    
    if k == ord('q'):
        break

cap_0.release()
cv2.destroyAllWindows()

内容を順に説明します。

USBカメラから映像を取り込む

以下のコードで実行します。

cap_0 = cv2.VideoCapture(no)

カッコ内には、カメラのID番号が入ります。

通常は、no = 0 か 1 とすれば問題なく映像が取り込めます。

なお、パソコンに内蔵カメラがある場合やUSBカメラをたくさん繋いでいる場合は、IDが 0 や 1 以外になる場合があります。その際は、2 以降の番号も試してみてください。

映像の取り込み設定

以下のコードで設定します。

cap_0.set(3, 1920)
cap_0.set(4, 1080)
cap_0.set(5, 30)

カッコ内の引数について説明します。

第一引数は設定値の番号です。

第二引数は上から順に、映像の横サイズ(ピクセル)、縦サイズ(ピクセル)、映像取り込み時のフレームレート(fps) です。

RGBごとにヒストグラムを計算しグラフにプロットする

以下のコードで実行します。

# プロットの初期化
plt.clf()

# RGBごとのヒストグラム計算とプロット
for i, channel in enumerate(colors):
    histgram = cv2.calcHist([img_a], [i], None, [256], [0, 256])
    plt.plot(histgram, color = channel)
    plt.xlim([0, 256])

# プロットの更新間隔。カッコの中は時間(msec)
plt.pause(0.01)

始めに、plt.clf( ) グラフのプロットを初期化します。ここがポイントです。

これを書かないとプロットが重ね書きされます。

次に、for i, channel in enumerate(colors) の部分で、取り込んだ映像のヒストグラムを計算します。

ヒストグラムは、取り込んだ映像一枚につき、 " r " 、 " g " 、 " b " (赤、緑、青)の各色についてそれぞれ計算します。

cv2.calcHist( ) のカッコ内の引数について説明します。

cv2.calcHist(images, channels, mask, histSize, ranges)

第一引数は、USBカメラから取り込んだ映像です。今回は、取り込んだ映像をスライス処理して 900×900ピクセルサイズとしています。なお、以下のコードでスライス処理を実行しています。

# 画像の切り抜き(取り込み画像を1:1にする, [Y方向:X方向] の意味)
img_a = img_0[90:990, 510:1410]

第二引数は、色のチャンネル(r, g, b)です。0番目が r 、1番目が g 、2番目が b の色チャンネルになります。

第三引数は、マスクの有無です。今回はマスクはないので None としています。

第四引数は、ヒストグラムのサイズです。今回は [ 256 ] としています。

第五引数は、色のレンジです。今回は [ 0, 256 ] としています。ちなみに、[ 0, 255 ] とするとグラフの255の値がグラフに表示されないため、あえて256としています。

また、for 文の後に plt.pause(0.01) を記載します。ここがポイントです。

これを記載しないとグラフが表示されません。

また、カッコの中の数字はプロットの更新間隔です。単位は msec (ミリ秒)です。

30fps と設定した場合、1秒間に30回の映像が取り込めます。つまり、1秒間に30回グラフがプロットされます。

1回の映像は 1/30 ≒ 0.033 msec で取り込んでいることになるため、0.03 以下の数字を記載しておけば十分高速にグラフがプロットされます。今回は余裕を見て0.01としています。

グラフの表示位置やサイズを指定する

以下のコードで実行します。

# グラフの表示位置を指定
fig_place = plt.get_current_fig_manager()

# カッコ内の意味(windows画面のx座標,y座標,表示させるグラフの幅,グラフの高さ)
fig_place.window.setGeometry(1112, 85, 640, 480)  

これにより、毎回同じ位置にグラフを表示させることができます。

また、グラフサイズも変更できます。

実行結果(YouTube)

YouTube動画をご覧ください。

プログラムを実行すると、動画のようにヒストグラムがリアルタイムに表示されます。

なお、グラフは Matplotlib で描画しています。

グラフ上部にフロッピーディスクのマークがあります。このボタンを押すと、リアルタイム表示させているヒストグラムを好きなタイミングでキャプチャして、好きなフォルダに保存することができます。

プログラムの実行環境

実行環境は以下です。

カテゴリー製品名
CPUIntel Core i9-9900K
マザーボードASUSTek ROG STRIX Z390-F GAMING
メモリCORSAIR DDR4-2666MHz
VENGEANCE LPX Series 16GB×2枚キット CMK32GX4M2A2666C16
ビデオカードASUS NVIDIA RTX2080Ti 搭載 トリプルファンモデル 11GB
SSDSamsung SSD 1TB 970 EVO Plus M.2 Type2280 PCIe3.0×4 NVMe1.3
電源Cooler Master V1200 Platinum 1200W
CPUクーラーCooler Master MasterLiquid ML240L RGB 水冷

今回使用したマシンは、Deep Learning 用のマシンのため、CPUとビデオカードにはそれなりのものを積んでいます。

ですが、今回のプログラムはCPU負荷が小さいので、ここまでのマシンでなくとも十分に実行可能だと思います。

まとめ

  • plt.clf( ) でグラフのプロットを初期化しておく。
  • plt.pause( )でグラフの更新頻度を調整する。
  • Matplotlib でグラフを描画し繰り返し更新させることでリアルタイムプロットができる。
  • 実行時のCPU負荷は13~18%程度であり低負荷で実行することができる。