使用 Qt Gui 执行慢速实时视频源的 Opencv 人脸检测
Posted
技术标签:
【中文标题】使用 Qt Gui 执行慢速实时视频源的 Opencv 人脸检测【英文标题】:Opencv face detection on live video feed using Qt Gui performing slow 【发布时间】:2020-03-30 05:46:00 【问题描述】:我有一个项目需要在qt
中设计一个gui
。此设计包含一个小部件,其中将使用 opencv
从 USB 网络摄像头显示 live video feed
。该项目将检测到faces
并识别它们,这意味着每个帧都会发生大量处理。
为此,我创建了一个线程来初始化相机并使用 opencv 从中获取帧。然后它将所有帧放入队列中,然后由函数update_frame
读取该队列,该函数基本上在qt 小部件上显示帧。这工作正常,没有延迟。
在update_frame
函数中,我添加了face detection
,因为它的执行速度非常慢。所以我创建了另一个线程start_inferencing
,它基本上从queue
读取帧,在检测到面部后,它再次将帧放入另一个队列q2
,然后由update_frame
读取,它显示但仍然响应非常慢。下面是代码:
q = queue.Queue()
q2 = queue.Queue()
def grab(cam, qu, width, height):
global running
capture = cv2.VideoCapture(cam)
capture.set(cv2.CAP_PROP_FRAME_WIDTH, width)
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, height)
while running:
frame =
capture.grab()
ret_val, img = capture.retrieve(0)
frame["img"] = img
if qu.qsize() < 100:
qu.put(frame)
else:
print(qu.qsize())
class Logic(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
QtWidgets.QMainWindow.__init__(self, parent)
self.setupUi(self)
set_initial_alert_temp()
self.window_width = self.ImgWidget.frameSize().width()
self.window_height = self.ImgWidget.frameSize().height()
self.ImgWidget = OwnImageWidget(self.ImgWidget)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.update_frame)
self.timer.start(1)
self.outside_temp_text_box.setText(str(curr_temp_cel))
def update_frame(self):
if not q2.empty():
frame1 = q2.get()
img = frame1["img"]
img_height, img_width, img_colors = img.shape
scale_w = float(self.window_width) / float(img_width)
scale_h = float(self.window_height) / float(img_height)
scale = min([scale_w, scale_h])
if scale == 0:
scale = 1
img = cv2.resize(img, None, fx=scale, fy=scale, interpolation=cv2.INTER_CUBIC)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
height, width, bpc = img.shape
bpl = bpc * width
image = QtGui.QImage(img.data, width, height, bpl, QtGui.QImage.Format_RGB888)
self.ImgWidget.setImage(image)
def start_inferencing():
while True:
if not q.empty():
frame = q.get()
img = frame["img"]
face_bbox = face.detect_face(img)
if face_bbox is not None:
(f_startX, f_startY, f_endX, f_endY) = face_bbox.astype("int")
f_startX = f_startX + 10
f_startY = f_startY + 10
f_endX = f_endX - 10
f_endY = f_endY - 10
cv2.rectangle(img, (f_startX, f_startY), (f_endX, f_endY), (0, 255, 0), 2)
frame1 = "img": img
if q2.qsize() < 100:
q2.put(frame1)
else:
print(q2.qsize())
def main():
capture_thread = threading.Thread(target=grab, args=(0, q, 640, 480))
capture_thread.start()
infer_thread = threading.Thread(target=start_inferencing)
infer_thread.start()
app = QtWidgets.QApplication(sys.argv)
w = Logic(None)
w.setWindowTitle('Test')
w.show()
app.exec_()
main()
以下是代码中发生的事情的摘要:
camera -> frame -> queue.put # (reading frame from camera and putting it in queue)
queue.get -> frame -> detect face -> queue2.put # (getting frame from queue, detecting face in it and putting the updated frames in queue2)
queue2.get -> frame -> display it on qt widget # (getting frame from queue2 and display it on qt widget)
直播视频慢的主要原因是grab
函数中读取的帧不能更快地处理,因此queue
的大小不断增加,因此整体变得非常慢.我可以使用什么好的方法来检测面部并立即显示它。请帮忙。谢谢
【问题讨论】:
可能使用事件(例如'onFrame'、'onFaceDetected'等)? @AndreySmorodov 您能否添加更多描述 您的基本设计出现在生产者/消费者系列中(或者可能应该这样对待)。那么为什么要从`毫秒计时器触发Logic::update_frame
,而不是简单地阻塞直到相关队列有数据?
【参考方案1】:
队列会累积线程无法处理的帧。所以,根本没有机会处理它们。这就是为什么队列在这里没用的原因。这里的工作时钟由到达的帧定义,每个帧都会产生事件,它可以在帧处理完成后在它自己的线程中工作(比如说在处理线程中),处理线程产生另一个事件并在另一个线程中处理,比如说在 GUI 线程中并将结果显示给用户。
如果您强制需要一些缓冲区,请检查环形缓冲区的长度是否有限。
【讨论】:
谢谢,我不擅长环形缓冲区或任何缓冲区,所以现在会坚持排队,但将来一定会尝试【参考方案2】:你有一个生产者/消费者序列...
-
抓取框架并推入 queue1
从 queue1 中取出帧,在 queue2 上处理和入队结果
从 queue2 中取出结果并显示
从您所说的第 2 阶段来看,是瓶颈。在这种情况下,您可以尝试为该阶段分配更多资源(即线程),以便 2. 有多个线程从 queue1 读取,处理并将结果推送到 queue2。您只需要确保从 queue2 弹出的已处理数据的排序正确——大概是通过为每个初始帧分配一个序列号或 id。
【讨论】:
你能给我一个你在回答中提到的带有 id 的队列或出队的例子吗?此外,我认为您在评论中提到的内容值得尝试,因为每毫秒运行更新帧是没有用的。我会尝试更新,但你认为这个管道是否并且可以产生良好的结果以上是关于使用 Qt Gui 执行慢速实时视频源的 Opencv 人脸检测的主要内容,如果未能解决你的问题,请参考以下文章