相机工作正常,当我使用软件验证相机时,我没有看到损坏的帧,所以我认为这是来自 OpenCV 的问题。
我注意到随机帧(10-20 帧中的一帧)已损坏,并且其中一个通道发生了移位。下面的例子。
我将相机代码作为在后台运行的服务运行,以便任何其他应用程序都可以获取最新的帧并使用它,而无需运行读取帧循环并使用异步代码。
import threading
import time
import cv2 as cv
import numpy as np
class CameraCU81():
def __init__(self, W=1920, H=1080, hz=30):
self.cap = cv.VideoCapture(0)
self.last_frame = None
# if recording mjgp the frames hangs...
#self.cap.set(cv.CAP_PROP_FOURCC, cv.VideoWriter_fourcc('M', 'J', 'P', 'G'))
print(str(cv.VideoWriter_fourcc('M', 'J', 'P', 'G')))
self.cap.set(cv.CAP_PROP_FRAME_WIDTH, W)
self.cap.set(cv.CAP_PROP_FRAME_HEIGHT, H)
self.cap.set(cv.CAP_PROP_FPS, hz)
print('Starting camera 81 fps at: ' + str(self.cap.get(cv.CAP_PROP_FPS)))
w = str(self.cap.get(cv.CAP_PROP_FRAME_WIDTH))
h = str(self.cap.get(cv.CAP_PROP_FRAME_HEIGHT))
print('Starting camera 81 resolution at: ' + w + ' x ' + h)
format = str(self.cap.get(cv.CAP_PROP_FOURCC))
print('Starting camera 81 format: ' + format)
def __capture_frames(self):
error_f = False
while True:
start_time = time.time()
ret, frame = self.cap.read()
if not ret:
timeout_time = (time.time() - start_time)
print('Frame could not be read ... is camera connected?')
print(timeout_time)
error_f = True
else:
self.last_frame = frame
if error_f:
timeout_time = (time.time() - start_time)
print(timeout_time)
def get_data(self):
return self.last_frame
def destroy(self):
self.cap.release()
def run(self):
t1 = threading.Thread(target=self.__capture_frames)
t1.daemon = True
t1.start()
正如我在评论中提到的,我非常确信这是竞争条件的情况,尽管我无法复制该问题。
问题是,当您尝试绘制图像时,您可能会传递对
self.last_frame
的引用,同时绘制到一半时 self.last_frame 图像将会发生变化,因为收到了新图像。
您必须注意到,阅读
self.last_frame
正在进入所谓的关键区域,您应该使用Mutex
或类似的方法来保护它。
一个不相关的注释是,
VideoCapture.read()
是一个相当昂贵的调用方法,如果你不确定你正在阅读的frame
是否会被使用,你最好使用VideoCapture.grab
。
我碰巧实现了这样的代码,但它使用了
QThread
和信号。可能会有帮助。
class AsyncVideoCapture(QThread):
finished = pyqtSignal()
new_data = pyqtSignal([np.ndarray])
def __init__(self, id: str):
super().__init__()
self.data_requested = False
self.running = False
self.camera = cv.VideoCapture(int(id) if id.isdigit() else id)
def run(self):
self.running = True
while self.running:
if self.data_requested:
ret_val, frame = self.camera.read()
if not ret_val:
break
self.new_data.emit(frame)
else:
ret_val = self.camera.grab()
if not ret_val:
break
self.finished.emit()
self.camera.release()
def request_data(self):
self.data_requested = True
def stop(self):
self.running = False