|
@@ -0,0 +1,54 @@
|
|
|
|
+from PyQt5.QtCore import pyqtSignal
|
|
|
|
+from PyQt5.QtCore import QObject
|
|
|
|
+from PyQt5.QtCore import QTimer
|
|
|
|
+from PyQt5.QtGui import QImage
|
|
|
|
+import cv2
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+class CameraDevice(QObject):
|
|
|
|
+
|
|
|
|
+ frame_ready = pyqtSignal(QImage)
|
|
|
|
+
|
|
|
|
+ def __init__(self, device_id=0):
|
|
|
|
+ super().__init__()
|
|
|
|
+ self.capture = cv2.VideoCapture(device_id)
|
|
|
|
+ self.timer = QTimer()
|
|
|
|
+
|
|
|
|
+ if not self.capture.isOpened():
|
|
|
|
+ raise ValueError("Device not found")
|
|
|
|
+
|
|
|
|
+ self.timer.timeout.connect(self.read_frame)
|
|
|
|
+ self.timer.setInterval(1000 / (self.fps or 30))
|
|
|
|
+ self.timer.start()
|
|
|
|
+
|
|
|
|
+ def __del__(self):
|
|
|
|
+ self.timer.stop()
|
|
|
|
+ self.capture.release()
|
|
|
|
+
|
|
|
|
+ @property
|
|
|
|
+ def fps(self):
|
|
|
|
+ """Frames per second."""
|
|
|
|
+ return int(self.capture.get(cv2.CAP_PROP_FPS))
|
|
|
|
+
|
|
|
|
+ @property
|
|
|
|
+ def size(self):
|
|
|
|
+ """Returns the size of the video frames: (width, height)."""
|
|
|
|
+ width = int(self.capture.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
|
|
+ height = int(self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
|
|
+ return (width, height)
|
|
|
|
+
|
|
|
|
+ def read_frame(self):
|
|
|
|
+ """Read frame into QImage and emit it."""
|
|
|
|
+ success, frame = self.capture.read()
|
|
|
|
+ if success:
|
|
|
|
+ img = _convert_array_to_qimage(frame)
|
|
|
|
+ self.frame_ready.emit(img)
|
|
|
|
+ else:
|
|
|
|
+ raise ValueError("Failed to read frame")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def _convert_array_to_qimage(a):
|
|
|
|
+ height, width, channels = a.shape
|
|
|
|
+ bytes_per_line = channels * width
|
|
|
|
+ cv2.cvtColor(a, cv2.COLOR_BGR2RGB, a)
|
|
|
|
+ return QImage(a.data, width, height, bytes_per_line, QImage.Format_RGB888)
|