はじめに
以前カメラアプリを作りました。カメラからの映像はOpneCVとPyTorchで加工してフレームのないWindowに表示しました。【続】【PySide6】【WSL2】カメラアプリを作る
問題点
フレームのないWindowが動かせないことに気付きました。解決方法
from PySide6.QtCore import QPointF def mousePressEvent(self, event: QMouseEvent) -> None: self.oldPosition = event.globalPosition() def mouseMoveEvent(self, event: QMouseEvent) -> None: delta = QPointF(event.globalPosition() - self.oldPosition) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPosition = event.globalPosition()
これだけで解決しました。
最終的なスクリプトは最後にのせておきます。
200行程度の1ファイルだけでカメラアプリは動作します。
さらなる問題点
Windows11では問題なく動作するのですがWSL2で実行した時はうまくいきません。解決方法は今のところわかりません。参考にさせて頂いたサイト
今回は海外のYouTubeを参考にさせて頂きました。How To Move A Frameless Window With A Mouse | PyQt5 - YouTube
動作環境
Windows 11 Python 3.10.5
certifi==2022.5.18.1 charset-normalizer==2.0.12 idna==3.3 numpy==1.22.4 opencv-python==4.6.0.66 Pillow==9.1.1 PySide6==6.3.0 PySide6-Addons==6.3.0 PySide6-Essentials==6.3.0 requests==2.28.0 shiboken6==6.3.0 torch==1.11.0+cu113 torchvision==0.12.0+cu113 typing_extensions==4.2.0 urllib3==1.26.9
Pythonスクリプト
import cv2 import numpy as np from PySide6.QtCore import Qt, Signal, Slot, QThread, QSize, QPointF from PySide6.QtWidgets import QMainWindow, QApplication, QWidget, QLabel, QGridLayout, QSlider from PySide6.QtGui import QImage, QPixmap, QMouseEvent import torch from torchvision import transforms QSS = """ QSlider::groove:horizontal { border: 1px solid; height: 10px; /*margin: 30px;*/ background-color: rgb(238, 238, 238); /*position: absolute;*/ left: 20px; right: 20px; } QSlider::handle:horizontal { background-color: rgb(80, 80, 232); height: 40px; width: 40px; border-radius: 20px; margin: -15px -20px; } QSlider::handle:horizontal:pressed { background-color: rgb(148, 148, 254); } QSlider::add-page:horizontal { background: rgb(183, 183, 183); } QSlider::sub-page:horizontal { background: rgb(80, 80, 232); } """ device = 'cuda' if torch.cuda.is_available() else 'cpu' transform_fn = transforms.Compose([ transforms.ToTensor(), transforms.Normalize([.485, .456, .406], [.229, .224, .225]), ]) model = torch.hub.load('pytorch/vision:v0.10.0', 'deeplabv3_resnet50', pretrained=True) model.eval().to(device) class VideoThread(QThread): change_pixmap_signal = Signal(QImage) playing = True cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G')) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) crop = 0 leftside = 280 bokashi = 0 def run(self): while self.playing: ret, frame = self.cap.read() frame = frame[:, self.leftside:self.leftside+720, :] if not self.crop == 0: frame = cv2.resize(frame[self.crop:-self.crop, self.crop:-self.crop, :], dsize = (720, 720)) if not self.bokashi == 0: frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) input_tensor = transform_fn(frame_rgb) input_batch = input_tensor.unsqueeze(0) with torch.no_grad(): output = model(input_batch.to(device))['out'][0] predict = output.argmax(0).to('cpu').numpy() mask_1 = np.where(predict == 15, 1, 0)[...,np.newaxis] mask_2 = np.where(predict == 15, 0, 1)[...,np.newaxis] if self.bokashi ==0: blurred_img = cv2.blur(frame, (1, 1)) else: blurred_img = cv2.blur(frame, (self.bokashi, self.bokashi)) frame = (frame * mask_1 + blurred_img * mask_2).astype('uint8') h, w, ch = frame.shape bytesPerLine = ch * w image = QImage(frame.copy(), w, h, bytesPerLine, QImage.Format.Format_BGR888) self.change_pixmap_signal.emit(image) self.cap.release() def stop(self): self.playing = False self.wait() def set_zoom_scale(self, x): self.crop = x def set_move_scale(self, x): self.leftside = x def set_blur_scale(self, x): self.bokashi = x class ImageWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('video capture') self.setFixedSize(QSize(720, 720)) self.setWindowFlags(Qt.FramelessWindowHint) self.label1 = QLabel() self.setCentralWidget(self.label1) def mousePressEvent(self, event: QMouseEvent) -> None: self.oldPosition = event.globalPosition() def mouseMoveEvent(self, event: QMouseEvent) -> None: delta = QPointF(event.globalPosition() - self.oldPosition) self.move(self.x() + delta.x(), self.y() + delta.y()) self.oldPosition = event.globalPosition() class ControlWindow(QMainWindow): def __init__(self): super().__init__() self.subWindow = ImageWindow() self.subWindow.show() self.windowflags = False self.thread = VideoThread() self.thread.change_pixmap_signal.connect(self.update_image) self.thread.start() self.initUI() def initUI(self): zoom_label = QLabel('ズーム') zoom_label.setStyleSheet('font: 20px; font-weight') move_label = QLabel('首振り') move_label.setStyleSheet('font: 20px; font-weight') blur_label = QLabel('背景ぼかし') blur_label.setStyleSheet('font: 20px; font-weight') self.zoom_slider = QSlider(Qt.Orientation.Horizontal) self.zoom_slider.setFixedSize(QSize(400, 100)) self.zoom_slider.setStyleSheet(QSS) self.zoom_slider.setMaximum(6) self.zoom_slider.setMinimum(0) self.zoom_slider.setValue(0) self.zoom_slider.valueChanged.connect(self.zoom_slider_change) self.move_slider = QSlider(Qt.Orientation.Horizontal) self.move_slider.setFixedSize(QSize(400, 100)) self.move_slider.setStyleSheet(QSS) self.move_slider.setMaximum(8) self.move_slider.setMinimum(0) self.move_slider.setValue(4) self.move_slider.valueChanged.connect(self.move_slider_change) self.blur_slider = QSlider(Qt.Orientation.Horizontal) self.blur_slider.setFixedSize(QSize(400, 100)) self.blur_slider.setStyleSheet(QSS) self.blur_slider.setMaximum(3) self.blur_slider.setMinimum(0) self.blur_slider.setValue(0) self.blur_slider.valueChanged.connect(self.blur_slider_change) layout = QGridLayout() layout.addWidget(zoom_label, 0, 0) layout.addWidget(move_label, 1, 0) layout.addWidget(blur_label, 2, 0) layout.addWidget(self.zoom_slider, 0, 1, 1, 4) layout.addWidget(self.move_slider, 1, 1, 1, 4) layout.addWidget(self.blur_slider, 2, 1, 1, 4) mainWidget = QWidget() mainWidget.setLayout(layout) self.setCentralWidget(mainWidget) @Slot(QImage) def update_image(self, image): self.subWindow.label1.setPixmap(QPixmap.fromImage(image)) def closeEvent(self, e): self.thread.stop() self.subWindow.close() e.accept() def zoom_slider_change(self): self.thread.set_zoom_scale(self.zoom_slider.value() * 30) def move_slider_change(self): self.thread.set_move_scale(self.move_slider.value() * 70) def blur_slider_change(self): self.thread.set_blur_scale(self.blur_slider.value() * 5) if __name__ == "__main__": app = QApplication([]) ex =ControlWindow() ex.show() app.exec()
2022年7月15日追記
以下の環境でも動作確認できました。Windows 11 CUDA 11.6 Python 3.10.5
certifi==2022.6.15 charset-normalizer==2.1.0 idna==3.3 numpy==1.23.1 opencv-python==4.6.0.66 Pillow==9.2.0 PySide6==6.3.1 PySide6-Addons==6.3.1 PySide6-Essentials==6.3.1 requests==2.28.1 shiboken6==6.3.1 torch==1.12.0+cu116 torchvision==0.13.0+cu116 typing_extensions==4.3.0 urllib3==1.26.10
,