最开始使用opencv_python捕获摄像头的时候,都是简单粗暴的while死循环,然后在同一个线程里面进行图像处理。这种方法有点莽。
后来使用线程,这样实现:
import threading
import cv2
class OpcvCapture(threading.Thread):
def __init__(self, win_name, cam_name):
super().__init__()
self.cam_name = cam_name
self.win_name = win_name
def run(self):
capture = cv2.VideoCapture(self.cam_name)
while (True):
# 获取一帧
ret, frame = capture.read()
cv2.imshow(self.win_name, frame)
cv2.waitKey(1)
if __name__ == "__main__":
camera0 = OpcvCapture("camera0", 0)
camera0.start()
camera1 = OpcvCapture("camera1", 1)
camera1.start()
run_code = 0
对于每一帧图像的处理,都是把处理方法方式注入到摄像头捕获的线程中执行,跨线程的时候就很麻烦,有些时候会无法运行。
所以现在改用队列的方式,将图像的获取与处理完全分开,各干各的,这样摄像头捕获程序就可以作为一个类,可以不用再去修改它了。
对于每一帧的处理作为独立的线程从图像队列中去取图像就好,至于如何处理,都不用修改摄像头帧捕获的相关代码。
整理以下,方便以后取用。
import time
import cv2
from queue import Queue
from threading import Thread
"""
Camera类只负责从摄像头获取图像
对图像的处理(包括显示)由外部定义
"""
class Camera:
def __init__(self, device_id, frame_queue):
self.device_id = device_id # 摄像头id
self.cam = cv2.VideoCapture(self.device_id) # 获取摄像头
self.frame_queue = frame_queue # 帧队列
self.is_running = False # 状态标签
self.fps = 0.0 # 实时帧率
self._t_last = time.time() * 1000
self._data = {}
def capture_queue(self):
# 捕获图像
self._t_last = time.time() * 1000
while self.is_running and self.cam.isOpened():
ret, frame = self.cam.read()
if not ret:
break
if self.frame_queue.qsize() < 1:
# 当队列中的图像都被消耗完后,再压如新的图像
t = time.time() * 1000
t_span = t - self._t_last
self.fps = int(1000.0 / t_span)
self._data["image"] = frame.copy()
self._data["fps"] = self.fps
self.frame_queue.put(self._data)
self._t_last = t
def run(self):
self.is_running = True
self.thread_capture = Thread(target=self.capture_queue)
self.thread_capture.start()
def stop(self):
self.is_running = False
self.cam.release()
# 对于图像的处理方法
def show_frame(frame):
while True:
# 根据实际需求,设置跳出循环(结束线程)的方法
data = frame.get()
image = data["image"]
cv2.putText(image, "fps:{fps}".format(fps=data["fps"]), (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0 ,0))
cv2.namedWindow("camera", cv2.WINDOW_AUTOSIZE)
cv2.imshow("camera", image)
if cv2.waitKey(1)& 0xFF == ord('q'):
break
frame_queue.task_done()
if __name__ == "__main__":
# 启动 获取摄像头画面的 线程
frame_queue = Queue()
cam = Camera(0, frame_queue)
cam.run()
# 启动处理(显示)摄像头画面的线程
thread_show = Thread(target=show_frame, args=(frame_queue,))
thread_show.start()
time.sleep(60)
cam.stop()
# TO DO 修改图像处理的死循环(while True)确保可正常结束。