问题2及解决
-
存在问题02-OpenCV获取的图像有滞后
因为使用了YOLO去识别图像,会导致两次读取摄像头图像的时间间隔比较大,在飞腾派上大约是900ms,而因为OpenCV的特性是使用了缓冲区,提前读入摄像头的图像并存入缓冲区,在两次读取时间间隔过长时,缓冲区中会存入多张图像,这会导致后面读取的图像还是之前位置拍摄的,并不是实时的,因此会出现滞后。
-
改进方案02-使用多线程单独管理摄像头
网上查阅的结果应该是也可以改变缓冲区大小,或者在读一次图像之前先多次读取图片直到将缓冲区读取完。但是这两种方式仍然是在主线程中运行,考虑到读取摄像头如果在主线程的话也会延长两次识别的时间间隔,因此可以使用多线程优化摄像头读取。
单独开一个线程,以一个较短的时间间隔去读取摄像头,并将最新画面存入一个缓冲区,主线程读取识别图像只从这个缓冲区读取,并不直接调用摄像头。
import threading import time class CapThread(threading.Thread): #继承父类threading.Thread def __init__(self): # 父类初始化 threading.Thread.__init__(self) # 用于储存最新图像的缓冲区 self.frame=None # 用于结束运行的标志位 self.runFlag=True # 对外获取最新图像的调用函数 def getFrame(self): return self.frame # 对外用于停止运行的调用函数 def stop_run(self): self.runFlag=False # 父类的方法,线程要执行的代码放在这个函数中 def run(self): # 可以指定线程固定使用的核心 # 但是因为是线程,所以使用的核心是和主进程一样的 # 因此这里指定使用的核心用处不大 # p=psutil.Process(os.getpid()) # p.cpu_affinity([0]) cap = cv2.VideoCapture(0) # 指定摄像头获取到的图像的长宽大小 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) # 长 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # 宽 # 在飞腾派上直接读取摄像头时候会因为速度过慢报一个错误 # 网上查阅后需要将读取的格式转换一下,就是以下的代码 cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M','J','P','G')) if not cap.isOpened(): print("无法打开摄像头") exit() while self.runFlag: # 读取摄像头画面,ret为bool类型,表示是否成功,frame为Numpy ret, frame = cap.read() # 翻转图像,跟摄像头实际安装有关,flip中第二个参数表示翻转方式 # 1:水平翻转,即可理解为数学上关于x轴对称 # 0:垂直翻转,即可理解为数学上关于y轴对称 # -1:水平垂直翻转,即可理解为数学上关于原点对称 self.frame = cv2.flip(frame, -1) # 这里判断非None不能直接用=,因为Numpy的性质会直接不进行判断,并报错 # 因此要判断非None需要使用is # 并且要通过cv2.imshow现实图像只能在这里使用 # 因为根据cv2的要求显示画面要和获取图像在同一个线程中,因此不能在主线程显示 if self.frame is not None: cv2.imshow('frame',self.frame) # 如果摄像头读取图像失败会停止运行 if not ret: self.runFlag = False # 休眠一会再进行下次读取,time.sleep的单位是秒,可以用小数来精确到毫秒 time.sleep(0.050) # 与cv2.imshow同理,这个函数也只能和读取摄像头在同一个进程中使用 if cv2.waitKey(1) & 0xFF == ord("q"): self.runFlag=False break
按照如上的代码定义一个新的线程,并在主线程中开启这个线程,之后就可以通过
getFrame()
获取到最新的图像了。# 生成一个线程对象 cap = CapThread() # 开启线程 cap.start() # 摄像头的开启可能需要一点时间 # 为了防止后面开始识别时候缓冲区是空 # 这里先阻塞一会,直到缓冲区不为None再继续执行 while cap.getFrame() is None: time.sleep(0.05) # 之后就可以通过cap.getFrame()正常获取图像了
经过测试,上面的代码可以获取到实时的图像,并且在识别的时间上会稍微快一点。