前言

最近在功能性测试的过程中,需要在Python环境下用OpenCV读取网络摄像头的视频流,接着用目标检测器进行视屏帧的后续处理。在测试过程中发现如果是单线程的情况,会出现比较严重的时延,如果目标检测模型稍微大一点,像YOLOv4这类的,那么情况更加严重。
后面考虑到演示效果,从单线程改为了多线程,即单独用一个线程实时捕获视频帧,主线程在需要时从子线程拷贝最近的帧使用即可。通过这样的修改,不仅时延基本消失,整个流程的实时性也有相对的提升,可以说是非常实用的技巧。

Python多线程编程

使用Python进行多线程编程是较为简单的,Python的threading模块封装了相关的操作,通过编写功能类继承threading.Thread即可实现自己的逻辑。简单的代码示例如下所示:

class myThread(threading.Thread):
    def __init__(self, name=None):
        super(myThread, self).__init__(name=name)

    def run(self):
        print('=> Thread %s is running ...' % self.name)

thread = myThread()
thread.start()
thread.join()

上面的代码简单展示了如何使用线程类:通过调用start()方法,线程实例开始在单独的线程上下文中运行自己的run()函数处理任务,直到线程退出。在此期间,主线程可以继续执行任务。当主线程任务执行结束时,主线程可通过设置全局状态变量告知子线程退出,同时调用join()方法等待子线程运行结束。

OpenCV视屏流的多线程处理

在上面例子的基础上,可对简单的单线程处理流程进行优化,即将读取视频帧的部分单独放在一个线程执行,同时提供线程间同步、数据交互的支持,在主线程中运行目标检测模型和后续处理流程,在需要时从读取视频帧的子线程获取最近的帧进行预处理、推理、后处理和可视化等操作。相关的示例代码如下:

import numpy as np
import cv2
import threading
from copy import deepcopy

thread_lock = threading.Lock()
thread_exit = False

class myThread(threading.Thread):
    def __init__(self, camera_id, img_height, img_width):
        super(myThread, self).__init__()
        self.camera_id = camera_id
        self.img_height = img_height
        self.img_width = img_width
        self.frame = np.zeros((img_height, img_width, 3), dtype=np.uint8)

    def get_frame(self):
        return deepcopy(self.frame)

    def run(self):
        global thread_exit
        cap = cv2.VideoCapture(self.camera_id)
        while not thread_exit:
            ret, frame = cap.read()
            if ret:
                frame = cv2.resize(frame, (self.img_width, self.img_height))
                thread_lock.acquire()
                self.frame = frame
                thread_lock.release()
            else:
                thread_exit = True
        cap.release()

def main():
    global thread_exit
    camera_id = 0
    img_height = 480
    img_width = 640
    thread = myThread(camera_id, img_height, img_width)
    thread.start()

    while not thread_exit:
        thread_lock.acquire()
        frame = thread.get_frame()
        thread_lock.release()

        cv2.imshow('Video', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            thread_exit = True
    thread.join()

if __name__ == "__main__":
    main()

在上面的代码中,为确保资源访问不受冲突,使用threading.Lock进行保护;主线程使用thread_exit全局状态变量控制子线程的运行状态。稍微特别一点的是,thread_exit实际上控制着两个线程的运行状态,因为在上述的处理流程中,两个线程都拥有终止运行流程的话语权,故这样的处理是合理的。

结语

实际上使用多线程并行处理任务,最大程度地利用资源早已是老生常谈的技巧,例如在服务器端,会开辟有专门的线程池用于处理随时可能到来的请求,而在嵌入式通信终端上,也通常采用线程池的方式来处理收到的消息包,以尽可能提升实时性。虽然多线程的处理方式相较单线程而言要稍微复杂一些,但带来的性能提升确是实打实的,所以还是很值得一试。

GitHub 加速计划 / opencv31 / opencv
162
15
下载
OpenCV: 开源计算机视觉库
最近提交(Master分支:4 个月前 )
e29a70c1 Bug fix for #25546 - Updating inliers for homography estimation #26742 Fixes #25546 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake 1 天前
2c2866a7 Fix bugs in GIF decoding #26738 ### Pull Request Readiness Checklist this is related to #25691 i solved two bugs here: 1. the decoding setting: according to [https://www.w3.org/Graphics/GIF/spec-gif89a.txt](https://www.w3.org/Graphics/GIF/spec-gif89a.txt) ``` DEFERRED CLEAR CODE IN LZW COMPRESSION There has been confusion about where clear codes can be found in the data stream. As the specification says, they may appear at anytime. There is not a requirement to send a clear code when the string table is full. It is the encoder's decision as to when the table should be cleared. When the table is full, the encoder can chose to use the table as is, making no changes to it until the encoder chooses to clear it. The encoder during this time sends out codes that are of the maximum Code Size. As we can see from the above, when the decoder's table is full, it must not change the table until a clear code is received. The Code Size is that of the maximum Code Size. Processing other than this is done normally. Because of a large base of decoders that do not handle the decompression in this manner, we ask developers of GIF encoding software to NOT implement this feature until at least January 1991 and later if they see that their particular market is not ready for it. This will give developers of GIF decoding software time to implement this feature and to get it into the hands of their clients before the decoders start "breaking" on the new GIF's. It is not required that encoders change their software to take advantage of the deferred clear code, but it is for decoders. ``` at first i didn't consider this case, thus leads to a bug discussed in #25691. the changes made in function lzwDecode() is aiming at solving this. 2. the fetch method of loopCount: in the codes at https://github.com/opencv/opencv/blob/4.x/modules/imgcodecs/src/grfmt_gif.cpp#L410, if the branch is taken, 3 more bytes will be taken, leading to unpredictable behavior. See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake 1 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐