小哀小白 2023-03-01 22:18 采纳率: 37.5%
浏览 247
已结题

opencv拉取视频流帧时,cv2.capture.read()导致程序卡住

这是我定义的函数,当读取电脑摄像头视频流时,会导致前端卡住阻塞。直接放视频没问题,一些rtsp的视频流有时好有时坏。请问如何解决。我了解到可能原因是电脑摄像头有打开时间,然后read读不到就卡死了,如何解决呢

    def button_camera_open(self):
        if not self.timer_video.isActive():
            # 默认使用第一个本地camera
            flag1 = self.cap.open(0)
            self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'))
            if flag1 == False:
                QtWidgets.QMessageBox.warning(
                    self, u"Warning", u"打开摄像头失败", buttons=QtWidgets.QMessageBox.Ok,
                    defaultButton=QtWidgets.QMessageBox.Ok)
            else:
                self.out = cv2.VideoWriter(r'D:\videos\prediction1.avi', cv2.VideoWriter_fourcc(
                    *'MJPG'), 30, (int(self.cap.get(3)), int(self.cap.get(4))))
                self.timer_video.start(100)
                #self.pushButton_3.setDisabled(True)
                #self.pushButton.setDisabled(True)
                self.pushButton.setText(u"关闭摄像头")
        else:
            self.timer_video.stop()
            self.cap.release()
            self.out.release()
            self.label.clear()
            self.init_logo()
            #self.pushButton_3.setDisabled(False)
            #self.pushButton.setDisabled(False)
            self.pushButton.setText(u"摄像头检测"def show_video_frame(self):
        name_list1 = []
        if self.cap.isOpened():

            flag1, img1 = self.cap.read()
            if flag1:
                showimg1 = img1
                with torch.no_grad():
                    img1 = letterbox(img1, new_shape=self.opt.img_size)[0]
                    img1[0:320,0:640]=[0,0,0]
                # Convert
                # BGR to RGB, to 3x416x416
                    img1 = img1[:, :, ::-1].transpose(2, 0, 1)
                    img1 = np.ascontiguousarray(img1)
                    img1 = torch.from_numpy(img1).to(self.device)
                    img1 = img1.half() if self.half else img1.float()  # uint8 to fp16/32
                    img1 /= 255.0  # 0 - 255 to 0.0 - 1.0
                    if img1.ndimension() == 3:
                        img1 = img1.unsqueeze(0)
                # Inference
                    pred1 = self.model(img1, augment=self.opt.augment)[0]

                # Apply NMS
                    pred1 = non_max_suppression(pred1, self.opt.conf_thres, self.opt.iou_thres, classes=self.opt.classes,
                                            agnostic=self.opt.agnostic_nms)
                # Process detections
                    for i, det1 in enumerate(pred1):  # detections per image
                        if det1 is not None and len(det1):

                        # Rescale boxes from img_size to im0 size
                            det1[:, :4] = scale_boxes(
                                img1.shape[2:], det1[:, :4], showimg1.shape).round()
                        # Write results
                            for *xyxy1, conf1, cls1 in reversed(det1):
                                label1 = '%s %.2f' % (self.names[int(cls1)], conf1)
                                name_list1.append(self.names[int(cls1)])
                                print(label1)
                                plot_one_box(
                                    xyxy1, showimg1, label=label1, color=self.colors[int(cls1)], line_thickness=2)

                self.out.write(showimg1)
                show1 = cv2.resize(showimg1, (640, 480))
                self.result1 = cv2.cvtColor(show1, cv2.COLOR_BGR2RGB)
                showImage1 = QtGui.QImage(self.result1.data, self.result1.shape[1], self.result1.shape[0],
                                            QtGui.QImage.Format_RGB888)
                self.label.setPixmap(QtGui.QPixmap.fromImage(showImage1))
            else:
                self.timer_video.stop()
                self.cap.release()
                self.out.release()
                self.label.clear()

  • 写回答

1条回答 默认 最新

  • Taylor 淡定哥 2023-03-02 04:24
    关注

    单线程当然卡死呀,你可以用多线程来解决阻塞。创建两个线程,一个用于读取视频帧并将其放入队列中,另一个用于从队列中读取视频帧并进行显示。你参考一下

    import threading
    import queue
    
    class VideoCaptureThread(threading.Thread):
        def __init__(self, cap, frame_queue):
            super().__init__()
            self.cap = cap
            self.frame_queue = frame_queue
            self._stop_event = threading.Event()
    
        def run(self):
            while not self._stop_event.is_set():
                ret, frame = self.cap.read()
                if ret:
                    self.frame_queue.put(frame)
                else:
                    self.stop()
    
        def stop(self):
            self._stop_event.set()
    
    class VideoDisplayThread(threading.Thread):
        def __init__(self, frame_queue, label):
            super().__init__()
            self.frame_queue = frame_queue
            self.label = label
            self._stop_event = threading.Event()
    
        def run(self):
            while not self._stop_event.is_set():
                try:
                    frame = self.frame_queue.get(timeout=0.1)
                except queue.Empty:
                    continue
    
                # 进行图片处理
                # ...
    
                # 显示图片
                # ...
    
        def stop(self):
            self._stop_event.set()
    
    
    

    在button_camera_open方法中,创建队列和两个线程:

    from queue import Queue
    
    self.frame_queue = Queue()
    self.capture_thread = VideoCaptureThread(self.cap, self.frame_queue)
    self.display_thread = VideoDisplayThread(self.frame_queue, self.label)
    self.capture_thread.start()
    self.display_thread.start()
    
    
    

    在show_video_frame方法中,不再调用cap.read()方法,而是从队列中读取视频帧:

    def show_video_frame(self):
        name_list1 = []
        try:
            img1 = self.frame_queue.get(timeout=0.1)
        except queue.Empty:
            return
    
        # 进行图片处理
        # ...
    
        # 显示图片
        # ...
    
    
    

    在button_camera_close方法中,停止两个线程:

    self.capture_thread.stop()
    self.display_thread.stop()
    
    
    

    最后注意一下队列的缓冲区有限,如果处理图片的速度比读取视频帧的速度慢,则队列可能会被填满,导致读取线程阻塞。你可以在队列中保留最近的几帧视频。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论 编辑记录

报告相同问题?

问题事件

  • 系统已结题 3月12日
  • 已采纳回答 3月4日
  • 创建了问题 3月1日

悬赏问题

  • ¥15 matlab答疑 关于海上风电的爬坡事件检测
  • ¥88 python部署量化回测异常问题
  • ¥30 酬劳2w元求合作写文章
  • ¥15 在现有系统基础上增加功能
  • ¥15 远程桌面文档内容复制粘贴,格式会变化
  • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
  • ¥15 这种微信登录授权 谁可以做啊
  • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
  • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”
  • ¥15 网络设备配置与管理这个该怎么弄