2つのカメラで取得した画像の輝度値を平均してバーに表示する
概要
このプログラムはPCに接続された2台のカメラ映像を同時に表示しながら画像処理(輝度値を平均してバーに表示する)方法について記載しています。このプログラムではGUIアプリケーションを作成するためQtと呼ばれるライブラリを使用しています。
サンプルプログラム
サンプル(Python) | qt_triggering_python.zip |
---|
pip install opencv-python
pip install numpy
pip install pyqt5
使用するライブラリはOpenCV,Numpy,Qtです。ライブラリのインストールがまだの場合はコンソール画面にて上記のコマンドを実行し、インストールしてください。
サンプルの出力
コード全体
import os
import sys
import ctypes
# GUI表記のために必要
import PyQt5.QtCore
import PyQt5.QtWidgets as qt5
from PyQt5.QtCore import pyqtSignal, QTimer, QObject
# 画像処理・画像保存に使用
import cv2 as cv2
import numpy as np
import tisgrabber as tis
#tisgrabber_x64.dllをインポートする
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
class WorkerSignals(QObject):
newimage = pyqtSignal(object)
class CallbackUserdata(ctypes.Structure):
""" コールバック関数に渡されるユーザーデータの例"""
_namespace__ = "userdata"
signals = WorkerSignals()
def __init__(self, index):
self.index = index
self.cvMat = None
def frameReadyCallback(hGrabber, pBuffer, framenumber, pData):
"""
OpenCVを使用した画像処理のコールバック関数の例
pBufferの画像データはcv Matrixに変換され、cv.mean()を使用して画像の平均輝度が測定されます。
:param: hGrabber:グラバーオブジェクトへのポインター
:param: pBuffer : 最初のピクセルの最初のバイトへのポインタ
:param: framenumber:ストリームが開始されてからのフレーム数
:param: pData:追加のユーザーデータ構造へのポインター
"""
# print("camera {}". format(pData.index))
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# 画像の値を取得する
ic.IC_GetImageDescription(hGrabber, Width, Height, BitsPerPixel, colorformat)
# バッファサイズを計算
bpp = int(BitsPerPixel.value/8.0)
buffer_size = Width.value * Height.value * bpp
if buffer_size > 0:
image = ctypes.cast(pBuffer,
ctypes.POINTER(
ctypes.c_ubyte * buffer_size))
pData.cvMat = np.ndarray(buffer=image.contents,
dtype=np.uint8,
shape=(Height.value,
Width.value,
bpp))
pData.signals.newimage.emit(pData)
# コールバック関数のポインタを作成
frameReadyCallbackfunc = ic.FRAMEREADYCALLBACK(frameReadyCallback)
class Camera():
def __init__(self, index, winID, OnNewImageHandler):
"""
新しいカメラオブジェクトを作成
:param index:カメラを識別するために使用されます。
:param winID:QTウィジェットウィンドウID
"""
self.hGrabber = ic.IC_CreateGrabber()
self.userdata = CallbackUserdata(index)
self.userdata.signals.newimage.connect(OnNewImageHandler)
self.winID = winID
self.loadDeviceState()
def showDeviceSelectionDlg(self):
ic.IC_StopLive(self.hGrabber)
self.hGrabber = ic.IC_ShowDeviceSelectionDialog(None)
if ic.IC_IsDevValid(self.hGrabber):
self.startCamera()
ic.IC_SaveDeviceStateToFile(self.hGrabber,
tis.T("camera{}.xml".format(self.userdata.index)))
def showPropertyDlg(self):
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_ShowPropertyDialog(self.hGrabber)
ic.IC_SaveDeviceStateToFile(self.hGrabber,
tis.T("camera{}.xml".format(self.userdata.index)))
def loadDeviceState(self):
if os.path.exists("camera{}.xml".format(self.userdata.index)):
ic.IC_LoadDeviceStateFromFile(self.hGrabber, tis.T("camera{}.xml".format(self.userdata.index)))
self.startCamera()
def startCamera(self):
"""カメラを起動しライブスタート
:paramUserDataカメラに接続されているユーザーデータ
:paramCamera開始するカメラ
"""
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_SetHWnd(self.hGrabber, self.winID)
ic.IC_SetFrameReadyCallback(self.hGrabber, frameReadyCallbackfunc, self.userdata)
ic.IC_SetContinuousMode(self.hGrabber, 0)
ic.IC_StartLive(self.hGrabber, 1)
def stopCamera(self):
"""カメラを停止
:paramUserDataカメラに接続されているユーザーデータ
:paramCamera停止するカメラ
"""
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_StopLive(self.hGrabber)
##########################################################################
def selectDevice(cam):
cameras[cam].showDeviceSelectionDlg()
def showProperties(cam):
cameras[cam].showPropertyDlg()
def Close():
for camera in cameras:
camera.stopCamera()
app.quit()
def OnNewImage(userdata):
print("camera {}".format(userdata.index))
# 画像の平均輝度を計算する
gray = cv2.cvtColor(userdata.cvMat, cv2.COLOR_BGR2GRAY)
mean = cv2.mean(gray)
brightnessbars[userdata.index].setValue(int(mean[0]))
#######################################################
# Qtを使ってGUI作成
# コンソール画面にて「pip install pyqt5」でPyQt5をインストールしてください。
#接続しているすべてのカメラのオブジェクトを作成
cameracount = ic.IC_GetDeviceCount() # カメラの数を取得
videowindows = []
brightnessbars = []
cameras = []
# Qtを使用するのに必ず作らなければいけないオブジェクト
app = PyQt5.QtWidgets.QApplication(sys.argv)
# ウインドウを作る
w = PyQt5.QtWidgets.QMainWindow()
w.resize(1280, 480)
w.move(300, 300)
w.setWindowTitle('2つのカメラで取得した画像の輝度値を平均してバーに表示する')
# メニューバーの作成
mainMenu = w.menuBar()
#fileメニュー
fileMenu = mainMenu.addMenu('&ファイル')
#exitメニュー
exitAct = PyQt5.QtWidgets.QAction("&終了", app)
exitAct.setStatusTip("終了")
exitAct.triggered.connect(Close)
fileMenu.addAction(exitAct)
##接続しているすべてのカメラのカメラメニュー
cameraMenu = mainMenu.addMenu('&カメラ')
for i in range(0, cameracount):
devselAct = PyQt5.QtWidgets.QAction("&デバイス選択 {}".format(i+1), app)
devselAct.triggered.connect(lambda checked, index=i: selectDevice(index))
cameraMenu.addAction(devselAct)
#接続しているすべてのカメラのプロパティメニュー
propertiesMenu = mainMenu.addMenu('&プロパティ')
for i in range(0, cameracount):
devselAct = PyQt5.QtWidgets.QAction("&カメラ {}".format(i+1), app)
devselAct.triggered.connect(lambda checked, index=i: showProperties(index))
propertiesMenu.addAction(devselAct)
# ビデオウィンドウでウィンドウレイアウトを作成する
MainWindow = PyQt5.QtWidgets.QWidget()
#縦方向に画面を配置する
vboxlayout = qt5.QVBoxLayout()
#横方向に画面を配置する
hboxlayout = qt5.QHBoxLayout()
# 接続しているカメラと同じ数のビデオウィンドウを追加します。
for i in range(0, cameracount):
vvboxlayout = qt5.QVBoxLayout()
videowindow = qt5.QWidget()
brightnessbar = qt5.QProgressBar()
brightnessbar.setRange(0, 256)
brightnessbar.setOrientation(PyQt5.QtCore.Qt.Horizontal)
brightnessbar.setValue(25)
vvboxlayout.addWidget(videowindow)
vvboxlayout.addWidget(brightnessbar)
hboxlayout.addLayout(vvboxlayout)
videowindows.append(videowindow)
brightnessbars.append(brightnessbar)
MainWindow.setLayout(hboxlayout)
w.setCentralWidget(MainWindow)
w.show()
# カメラオブジェクトを作成
for i in range(0, cameracount):
cameras.append(Camera(i, videowindows[i].winId(), OnNewImage))
app.exec()
解説
カメラクラスの定義
class Camera():
def __init__(self, index, winID, OnNewImageHandler):
"""
新しいカメラオブジェクトを作成
:param index:カメラを識別するために使用されます。
:param winID:QTウィジェットウィンドウID
"""
self.hGrabber = ic.IC_CreateGrabber()
self.userdata = CallbackUserdata(index)
self.userdata.signals.newimage.connect(OnNewImageHandler)
self.winID = winID
self.loadDeviceState()
def showDeviceSelectionDlg(self):
ic.IC_StopLive(self.hGrabber)
self.hGrabber = ic.IC_ShowDeviceSelectionDialog(None)
if ic.IC_IsDevValid(self.hGrabber):
self.startCamera()
ic.IC_SaveDeviceStateToFile(self.hGrabber,
tis.T("camera{}.xml".format(self.userdata.index)))
def showPropertyDlg(self):
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_ShowPropertyDialog(self.hGrabber)
ic.IC_SaveDeviceStateToFile(self.hGrabber,
tis.T("camera{}.xml".format(self.userdata.index)))
def loadDeviceState(self):
if os.path.exists("camera{}.xml".format(self.userdata.index)):
ic.IC_LoadDeviceStateFromFile(self.hGrabber, tis.T("camera{}.xml".format(self.userdata.index)))
self.startCamera()
def startCamera(self):
"""カメラを起動しライブスタート
:paramUserDataカメラに接続されているユーザーデータ
:paramCamera開始するカメラ
"""
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_SetHWnd(self.hGrabber, self.winID)
ic.IC_SetFrameReadyCallback(self.hGrabber, frameReadyCallbackfunc, self.userdata)
ic.IC_SetContinuousMode(self.hGrabber, 0)
ic.IC_StartLive(self.hGrabber, 1)
def stopCamera(self):
"""カメラを停止
:paramUserDataカメラに接続されているユーザーデータ
:paramCamera停止するカメラ
"""
if ic.IC_IsDevValid(self.hGrabber):
ic.IC_StopLive(self.hGrabber)
上記のようにクラスを定義すると簡単に複数のカメラ制御を行うことができます。
# カメラオブジェクトを作成
for i in range(0, cameracount):
cameras.append(Camera(i, videowindows[i].winId(), OnNewImage))
OnNewImageコールバック関数をもったカメラの台数分(cameracount)のCameraオブジェクトを使用することができます。
コールバック関数(OpenCVの処理)
def OnNewImage(userdata):
print("camera {}".format(userdata.index))
# 画像の平均輝度を計算する
gray = cv2.cvtColor(userdata.cvMat, cv2.COLOR_BGR2GRAY)
mean = cv2.mean(gray)
brightnessbars[userdata.index].setValue(int(mean[0]))
フレームを受けたタイミングで発火するコールバック関数は上記の通りです。受け取った画像データはユーザーデータに保存されており、ユーザーデータをOpenCVでモノクロ化(cvtColorを使用)し、mean変数にて輝度値の平均値を取得します。