はじめに
前回WSL2でPyQt6が使えるようになりました。WSL2上のUbuntu20.04でPyQt6を使う(ファイル選択ダイアログも使用できました) - パソコン関連もろもろ
以前にはWSL2でWebカメラが使えるようになりました。
【更新記事】WSL2でカメラを使うには(3) - パソコン関連もろもろ
そうすると当然同時に使いたくなります。
ということで、Webカメラからの映像をPyQt6で表示することに挑戦しました。
はっきり言って後述する「参考にさせて頂いたサイト」をほぼそのまま使わせて頂きました。詳細に解説もしてくれているので非常にわかりやすいです。
今回はWSL2で動作確認ができたという記事になります。
Pythonスクリプト
import cv2 from PyQt6.QtCore import QSize, pyqtSignal, pyqtSlot, QThread from PyQt6.QtWidgets import * from PyQt6.QtGui import QImage, QPixmap class VideoThread(QThread): change_pixmap_signal = pyqtSignal(QImage) playing = True def run(self): cap = cv2.VideoCapture(0) while self.playing: ret, frame = cap.read() if ret: h, w, ch = frame.shape bytesPerLine = ch * w image = QImage(frame, w, h, bytesPerLine, QImage.Format.Format_BGR888) self.change_pixmap_signal.emit(image) cap.release() def stop(self): self.playing = False self.wait() class Window(QWidget): video_size = QSize(640, 480) def __init__(self): super().__init__() self.initUI() self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) self.thread.start() def initUI(self): self.setWindowTitle("OpenCV-Python sample") self.img_label1 = QLabel() self.img_label1.setFixedSize(self.video_size) self.main_layout = QVBoxLayout() self.main_layout.addWidget(self.img_label1) self.setLayout(self.main_layout) def closeEvent(self, e): self.thread.stop() e.accept() @pyqtSlot(QImage) def update_image(self, image): self.img_label1.setPixmap(QPixmap.fromImage(image)) if __name__ == "__main__": app = QApplication([]) ex =Window() ex.show() app.exec()
環境
既定のディストリビューション: Ubuntu-20.04 既定のバージョン: 2 WSL1 は、現在のマシン構成ではサポートされていません。 WSL1 を使用するには、"Linux 用 Windows サブシステム" オプション コンポーネントを有効にしてください。 WSL バージョン: 0.51.3.0 カーネル バージョン: 5.10.93.2 WSLg バージョン: 1.0.30 Windows バージョン: 10.0.22000.556
python 3.8.10 numpy==1.22.3 opencv-python==4.5.5.64 pkg_resources==0.0.0 PyQt6==6.2.3 PyQt6-Qt6==6.2.3 PyQt6-sip==13.2.1
参考にさせて頂いたサイト
note.comstackoverflow.com
2022年5月12日追記
応用してカメラアプリを作りました。touch-sp.hatenablog.com
2022年9月29日追記
PySide6用のスクリプトはこちらです。「pyqtSignal」「pyqtSlot」がそれぞれ「Signal」「Slot」になります。
import cv2 from PySide6.QtCore import QSize, Signal, Slot, QThread from PySide6.QtWidgets import * from PySide6.QtGui import QImage, QPixmap class VideoThread(QThread): change_pixmap_signal = Signal(QImage) playing = True def run(self): cap = cv2.VideoCapture(1, cv2.CAP_DSHOW) while self.playing: ret, frame = cap.read() if ret: h, w, ch = frame.shape bytesPerLine = ch * w image = QImage(frame, w, h, bytesPerLine, QImage.Format.Format_BGR888) self.change_pixmap_signal.emit(image) cap.release() def stop(self): self.playing = False self.wait() class Window(QWidget): video_size = QSize(640, 480) def __init__(self): super().__init__() self.initUI() self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) self.thread.start() def initUI(self): self.setWindowTitle("OpenCV-Python sample") self.img_label1 = QLabel() self.img_label1.setFixedSize(self.video_size) self.main_layout = QVBoxLayout() self.main_layout.addWidget(self.img_label1) self.setLayout(self.main_layout) def closeEvent(self, e): self.thread.stop() e.accept() @Slot(QImage) def update_image(self, image): self.img_label1.setPixmap(QPixmap.fromImage(image)) if __name__ == "__main__": app = QApplication([]) ex =Window() ex.show() app.exec()