不显示 PyQt 的视频帧的坐标

Coordinates of a video frame not of display PyQt

我正在尝试使用 PyQt 制作视频流应用程序,我希望在单击视频帧而不是显示器时获取视频帧点的坐标。我的意思是如果我的相机分辨率是 640*480 而我的显示器分辨率是 1920*1080 那么当我点击显示器右下角时我应该得到 (640, 480)

相机代码:

from PyQt4 import QtCore, QtGui
from threading import Thread
from collections import deque
from datetime import datetime
import time
import sys
import cv2
import imutils


class CameraWidget(QtGui.QWidget):
    """Independent camera feed
    Uses threading to grab IP camera frames in the background

    @param width - Width of the video frame
    @param height - Height of the video frame
    @param stream_link - IP/RTSP/Webcam link
    @param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
    """

    def __init__(self, width, height, stream_link=0, aspect_ratio=False, parent=None, deque_size=1):
        super(CameraWidget, self).__init__(parent)

        # Initialize deque used to store frames read from the stream
        self.deque = deque(maxlen=deque_size)

        self.screen_width = width
        self.screen_height = height
        self.maintain_aspect_ratio = aspect_ratio

        self.camera_stream_link = stream_link

        # Flag to check if camera is valid/working
        self.online = False
        self.capture = None
        self.video_frame = QtGui.QLabel()

        self.load_network_stream()

        # Start background frame grabbing
        self.get_frame_thread = Thread(target=self.get_frame, args=())
        self.get_frame_thread.daemon = True
        self.get_frame_thread.start()

        # Periodically set video frame to display
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.set_frame)
        self.timer.start(.5)

        print('Started camera: {}'.format(self.camera_stream_link))

    def load_network_stream(self):
        """Verifies stream link and open new stream if valid"""

        def load_network_stream_thread():
            if self.verify_network_stream(self.camera_stream_link):
                self.capture = cv2.VideoCapture(self.camera_stream_link)
                self.online = True
        self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
        self.load_stream_thread.daemon = True
        self.load_stream_thread.start()

    def verify_network_stream(self, link):
        """Attempts to receive a frame from given link"""

        cap = cv2.VideoCapture(link)
        if not cap.isOpened():
            return False
        cap.release()
        return True

    def get_frame(self):
        """Reads frame, resizes, and converts image to pixmap"""

        while True:
            try:
                if self.capture.isOpened() and self.online:
                    # Read next frame from stream and insert into deque
                    status, frame = self.capture.read()
                    if status:
                        self.deque.append(frame)
                    else:
                        self.capture.release()
                        self.online = False
                else:
                    # Attempt to reconnect
                    print('attempting to reconnect', self.camera_stream_link)
                    self.load_network_stream()
                    self.spin(2)
                self.spin(.001)
            except AttributeError:
                pass

    def spin(self, seconds):
        """Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""

        time_end = time.time() + seconds
        while time.time() < time_end:
            QtGui.QApplication.processEvents()

    def set_frame(self):
        """Sets pixmap image to video frame"""

        if not self.online:
            self.spin(1)
            return

        if self.deque and self.online:
            # Grab latest frame
            frame = self.deque[-1]

            # Keep frame aspect ratio
            if self.maintain_aspect_ratio:
                self.frame = imutils.resize(frame, width=self.screen_width)
            # Force resize
            else:
                self.frame = cv2.resize(frame, (self.screen_width, self.screen_height))
                self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
                h, w, ch = self.frame.shape
                bytesPerLine = ch * w

            # Convert to pixmap and set to video frame
            self.img = QtGui.QImage(self.frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
            self.pix = QtGui.QPixmap.fromImage(self.img)
            self.video_frame.setPixmap(self.pix)

    def get_video_frame(self):
        return self.video_frame

这是我的 window 代码:

class Window(QtGui.QWidget):
    def __init__(self, cam=None, parent=None):
        super(Window, self).__init__(parent)

        self.showMaximized()

        self.screen_width = self.width()
        self.screen_height = self.height()

        # Create camera widget
        print('Creating Camera Widget...')
        self.camera = CameraWidget(self.screen_width, self.screen_height, cam)

        self.layout = QtGui.QVBoxLayout()
        self.layout.setContentsMargins(0,0,0,0)
        self.layout.setSpacing(0)
        self.layout.addWidget(self.camera.get_video_frame())
        self.setLayout(self.layout)

        self.camera.get_video_frame().mousePressEvent = self.getPos   

    def getPos(self , event):
        x = event.pos().x()
        y = event.pos().y()
        print('x,y: ', x, y)


camera = 'rtsp://admin:vaaan@123@192.168.1.51/Streaming/Channels/2'

if __name__=='__main__':
    app = QtGui.QApplication([])
    win = Window(camera)
    sys.exit(app.exec_())

不要使用 QLabel 来显示图像,因为很难映射坐标,而是使用 QGraphicsPixmapItem:

from collections import deque
from datetime import datetime
import sys
from threading import Thread
import time

import cv2

from PyQt4 import QtCore, QtGui


class CameraWidget(QtGui.QGraphicsView):
    """Independent camera feed
    Uses threading to grab IP camera frames in the background

    @param width - Width of the video frame
    @param height - Height of the video frame
    @param stream_link - IP/RTSP/Webcam link
    @param aspect_ratio - Whether to maintain frame aspect ratio or force into fraame
    """

    def __init__(
        self,
        width,
        height,
        stream_link=0,
        aspect_ratio=False,
        parent=None,
        deque_size=1,
    ):
        super(CameraWidget, self).__init__(parent)

        # Initialize deque used to store frames read from the stream
        self.deque = deque(maxlen=deque_size)

        self.screen_width = width
        self.screen_height = height
        self.maintain_aspect_ratio = aspect_ratio

        self.camera_stream_link = stream_link

        # Flag to check if camera is valid/working
        self.online = False
        self.capture = None

        self.setScene(QtGui.QGraphicsScene(self))

        self._pixmap_item = self.scene().addPixmap(QtGui.QPixmap())

        self.load_network_stream()

        # Start background frame grabbing
        self.get_frame_thread = Thread(target=self.get_frame, args=())
        self.get_frame_thread.daemon = True
        self.get_frame_thread.start()

        # Periodically set video frame to display
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.set_frame)
        self.timer.start(0.5)

        print("Started camera: {}".format(self.camera_stream_link))

    def load_network_stream(self):
        """Verifies stream link and open new stream if valid"""

        def load_network_stream_thread():
            if self.verify_network_stream(self.camera_stream_link):
                self.capture = cv2.VideoCapture(self.camera_stream_link)
                self.online = True

        self.load_stream_thread = Thread(target=load_network_stream_thread, args=())
        self.load_stream_thread.daemon = True
        self.load_stream_thread.start()

    def verify_network_stream(self, link):
        """Attempts to receive a frame from given link"""

        cap = cv2.VideoCapture(link)
        if not cap.isOpened():
            return False
        cap.release()
        return True

    def get_frame(self):
        """Reads frame, resizes, and converts image to pixmap"""

        while True:
            try:
                if self.capture.isOpened() and self.online:
                    # Read next frame from stream and insert into deque
                    status, frame = self.capture.read()
                    if status:
                        self.deque.append(frame)
                    else:
                        self.capture.release()
                        self.online = False
                else:
                    # Attempt to reconnect
                    print("attempting to reconnect", self.camera_stream_link)
                    self.load_network_stream()
                    self.spin(2)
                self.spin(0.001)
            except AttributeError:
                pass

    def spin(self, seconds):
        """Pause for set amount of seconds, replaces time.sleep so program doesnt stall"""

        time_end = time.time() + seconds
        while time.time() < time_end:
            QtGui.QApplication.processEvents()

    def set_frame(self):
        """Sets pixmap image to video frame"""

        if not self.online:
            self.spin(1)
            return

        if self.deque and self.online:
            # Grab latest frame
            frame = self.deque[-1]

            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = frame.shape
            bytesPerLine = ch * w

            # Convert to pixmap and set to video frame
            image = QtGui.QImage(frame, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
            pixmap = QtGui.QPixmap.fromImage(image.copy())
            self._pixmap_item.setPixmap(pixmap)
        self.fix_size()

    def mousePressEvent(self, event):
        super(CameraWidget, self).mousePressEvent(event)
        vp = event.pos()
        it = self.itemAt(vp)
        if it == self._pixmap_item:
            sp = self.mapToScene(vp)
            lp = it.mapFromScene(sp)
            self.clicked.emit(lp.toPoint())

    clicked = QtCore.pyqtSignal(QtCore.QPoint)

    def resizeEvent(self, event):
        self.fix_size()
        super().resizeEvent(event)

    def fix_size(self):
        self.fitInView(
            self._pixmap_item,
            QtCore.Qt.KeepAspectRatio
            if self.maintain_aspect_ratio
            else QtCore.Qt.IgnoreAspectRatio,
        )


class Window(QtGui.QWidget):
    def __init__(self, cam=None, parent=None):
        super(Window, self).__init__(parent)

        self.showMaximized()

        self.screen_width = self.width()
        self.screen_height = self.height()

        # Create camera widget
        print("Creating Camera Widget...")
        self.camera = CameraWidget(self.screen_width, self.screen_height, cam)

        lay = QtGui.QVBoxLayout(self)
        lay.setContentsMargins(0, 0, 0, 0)
        lay.setSpacing(0)
        lay.addWidget(self.camera)

        self.camera.clicked.connect(print)


camera = 0  # 'rtsp://admin:vaaan@123@192.168.1.51/Streaming/Channels/2'

if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = Window(camera)
    sys.exit(app.exec_())