See this post for a code description, and usage information: some code changes may be required.
# USB camera display using PyQt and OpenCV, from iosoft.blog
# Copyright (c) Jeremy P Bentham 2019
# Please credit iosoft.blog if you use the information or software in it
VERSION = "Cam_display v0.10"
import sys, time, threading, cv2
try:
from PyQt5.QtCore import Qt
pyqt5 = True
except:
pyqt5 = False
if pyqt5:
from PyQt5.QtCore import QTimer, QPoint, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QLabel
from PyQt5.QtWidgets import QWidget, QAction, QVBoxLayout, QHBoxLayout
from PyQt5.QtGui import QFont, QPainter, QImage, QTextCursor
else:
from PyQt4.QtCore import Qt, pyqtSignal, QTimer, QPoint
from PyQt4.QtGui import QApplication, QMainWindow, QTextEdit, QLabel
from PyQt4.QtGui import QWidget, QAction, QVBoxLayout, QHBoxLayout
from PyQt4.QtGui import QFont, QPainter, QImage, QTextCursor
try:
import Queue as Queue
except:
import queue as Queue
IMG_SIZE = 1280,720 # 640,480 or 1280,720 or 1920,1080
IMG_FORMAT = QImage.Format_RGB888
DISP_SCALE = 2 # Scaling factor for display image
DISP_MSEC = 50 # Delay between display cycles
CAP_API = cv2.CAP_ANY # API: CAP_ANY or CAP_DSHOW etc...
EXPOSURE = 0 # Zero for automatic exposure
TEXT_FONT = QFont("Courier", 10)
camera_num = 1 # Default camera (first in list)
image_queue = Queue.Queue() # Queue to hold images
capturing = True # Flag to indicate capturing
# Grab images from the camera (separate thread)
def grab_images(cam_num, queue):
cap = cv2.VideoCapture(cam_num-1 + CAP_API)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, IMG_SIZE[0])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, IMG_SIZE[1])
if EXPOSURE:
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 0)
cap.set(cv2.CAP_PROP_EXPOSURE, EXPOSURE)
else:
cap.set(cv2.CAP_PROP_AUTO_EXPOSURE, 1)
while capturing:
if cap.grab():
retval, image = cap.retrieve(0)
if image is not None and queue.qsize() < 2:
queue.put(image)
else:
time.sleep(DISP_MSEC / 1000.0)
else:
print("Error: can't grab camera image")
break
cap.release()
# Image widget
class ImageWidget(QWidget):
def __init__(self, parent=None):
super(ImageWidget, self).__init__(parent)
self.image = None
def setImage(self, image):
self.image = image
self.setMinimumSize(image.size())
self.update()
def paintEvent(self, event):
qp = QPainter()
qp.begin(self)
if self.image:
qp.drawImage(QPoint(0, 0), self.image)
qp.end()
# Main window
class MyWindow(QMainWindow):
text_update = pyqtSignal(str)
# Create main window
def __init__(self, parent=None):
QMainWindow.__init__(self, parent)
self.central = QWidget(self)
self.textbox = QTextEdit(self.central)
self.textbox.setFont(TEXT_FONT)
self.textbox.setMinimumSize(300, 100)
self.text_update.connect(self.append_text)
sys.stdout = self
print("Camera number %u" % camera_num)
print("Image size %u x %u" % IMG_SIZE)
if DISP_SCALE > 1:
print("Display scale %u:1" % DISP_SCALE)
self.vlayout = QVBoxLayout() # Window layout
self.displays = QHBoxLayout()
self.disp = ImageWidget(self)
self.displays.addWidget(self.disp)
self.vlayout.addLayout(self.displays)
self.label = QLabel(self)
self.vlayout.addWidget(self.label)
self.vlayout.addWidget(self.textbox)
self.central.setLayout(self.vlayout)
self.setCentralWidget(self.central)
self.mainMenu = self.menuBar() # Menu bar
exitAction = QAction('&Exit', self)
exitAction.setShortcut('Ctrl+Q')
exitAction.triggered.connect(self.close)
self.fileMenu = self.mainMenu.addMenu('&File')
self.fileMenu.addAction(exitAction)
# Start image capture & display
def start(self):
self.timer = QTimer(self) # Timer to trigger display
self.timer.timeout.connect(lambda:
self.show_image(image_queue, self.disp, DISP_SCALE))
self.timer.start(DISP_MSEC)
self.capture_thread = threading.Thread(target=grab_images,
args=(camera_num, image_queue))
self.capture_thread.start() # Thread to grab images
# Fetch camera image from queue, and display it
def show_image(self, imageq, display, scale):
if not imageq.empty():
image = imageq.get()
if image is not None and len(image) > 0:
img = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
self.display_image(img, display, scale)
# Display an image, reduce size if required
def display_image(self, img, display, scale=1):
disp_size = img.shape[1]//scale, img.shape[0]//scale
disp_bpl = disp_size[0] * 3
if scale > 1:
img = cv2.resize(img, disp_size,
interpolation=cv2.INTER_CUBIC)
qimg = QImage(img.data, disp_size[0], disp_size[1],
disp_bpl, IMG_FORMAT)
display.setImage(qimg)
# Handle sys.stdout.write: update text display
def write(self, text):
self.text_update.emit(str(text))
def flush(self):
pass
# Append to text display
def append_text(self, text):
cur = self.textbox.textCursor() # Move cursor to end of text
cur.movePosition(QTextCursor.End)
s = str(text)
while s:
head,sep,s = s.partition("\n") # Split line at LF
cur.insertText(head) # Insert text at cursor
if sep: # New line if LF
cur.insertBlock()
self.textbox.setTextCursor(cur) # Update visible cursor
# Window is closing: stop video capture
def closeEvent(self, event):
global capturing
capturing = False
self.capture_thread.join()
if __name__ == '__main__':
if len(sys.argv) > 1:
try:
camera_num = int(sys.argv[1])
except:
camera_num = 0
if camera_num < 1:
print("Invalid camera number '%s'" % sys.argv[1])
else:
app = QApplication(sys.argv)
win = MyWindow()
win.show()
win.setWindowTitle(VERSION)
win.start()
sys.exit(app.exec_())
#EOF