OpenCV単体でもGUIが作れますが、ここではpySide2との連携について。
環境
macOS + vscode
macOSとvscodeの組み合わせでWebカメラを使うにはTerminalから起動しないとcv2.VideoCapture(0)で失敗してうまくいかない。権限不足でiSight(MacのWebカメラ)が動作しないので注意。
zsh
% sw_vers
ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H114
% uname -m
x86_64
% python --version
Python 3.8.6
% code --version
1.53.0
% code . # vscodeはTerminalから起動しないと権限不足でiSight(MacのWebカメラ)が動作しない
セットアップ
zsh
pip install opencv-python-headless # headless (GUIなし)でないとpySide2のQtと被る
pip install PySide2
サンプルコード
pySide2でWebカメラ表示するだけのプログラムをいくつかの方法で書いてみた。
サンプル1 (シンプル版)
webcam.py
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import cv2
def displayFrame():
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w = frame.shape[:2]
image = QImage(frame.flatten(), w, h, QImage.Format_RGB888) # cv::Mat -> Qt(numpy.array)
label.setPixmap(QPixmap.fromImage(image))
app = QApplication()
window = QWidget()
# Webカメラ起動
cap = cv2.VideoCapture(0)
# 画面表示タイミング
timer = QTimer()
timer.timeout.connect(displayFrame)
timer.start(33) # [msec]
# 画面構成
label = QLabel('No Camera Feed')
layout = QVBoxLayout()
layout.addWidget(label)
window.setLayout(layout)
window.setWindowTitle("Camera")
window.show()
app.exec_()
サンプル2 (QGraphicsScene版、マウスでクリックした位置(画像座標)を取得)
画像の座標を得るにはQGraphicsSceneでmousePressEventを取ってscenePosを得る。
webcam_qgraphicsscene.py
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *
import cv2
class myImage(QGraphicsScene):
mScene = None
def __init__(self, parent=None):
super(myImage, self).__init__()
def mousePressEvent(self, event):
print(event.type())
p = event.scenePos() # relative to widget
print(p)
class webCam(QWidget):
cap = None
view = None
scene = None
timer = None
def __init__(self, parent=None):
super(webCam, self).__init__()
self.left = 70
self.top = 70
self.width = 640
self.height = 480
self.setGeometry(self.left, self.top, self.width, self.height)
self.init()
def cv_to_pixmap(self, cv_img):
frame = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
h, w = frame.shape[:2]
qimage = QImage(frame.flatten(), w, h, QImage.Format_RGB888) # cv::Mat -> Qt(numpy.array)
pixmap = QPixmap.fromImage(qimage)
return pixmap
def displayFrame(self):
ret, frame = self.cap.read()
pixmap = self.cv_to_pixmap(frame)
self.scene.clear()
self.scene.addPixmap(pixmap)
self.view.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) # 原寸表示したい時はこの行をコメントアウト
def init(self):
# Webカメラ起動
self.cap = cv2.VideoCapture(0)
# 画面構成
self.scene = myImage('No camera frame')
self.view = QGraphicsView()
self.view.setScene(self.scene)
self.view.setMouseTracking(True)
layout = QVBoxLayout()
layout.addWidget(self.view)
scrollArea = QScrollArea() # 原寸表示時のスクロール用
scrollArea.setLayout(layout)
super(webCam, self).setLayout(layout)
super(webCam, self).setWindowTitle("Camera")
# 画面表示タイミング
self.timer = QTimer()
self.timer.timeout.connect(self.displayFrame)
self.timer.start(33) # [msec]
def main():
app = QApplication()
window = webCam()
window.show()
app.exec_()
if __name__ == '__main__':
main()