前言
最近在功能性测试的过程中,需要在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实际上控制着两个线程的运行状态,因为在上述的处理流程中,两个线程都拥有终止运行流程的话语权,故这样的处理是合理的。
结语
实际上使用多线程并行处理任务,最大程度地利用资源早已是老生常谈的技巧,例如在服务器端,会开辟有专门的线程池用于处理随时可能到来的请求,而在嵌入式通信终端上,也通常采用线程池的方式来处理收到的消息包,以尽可能提升实时性。虽然多线程的处理方式相较单线程而言要稍微复杂一些,但带来的性能提升确是实打实的,所以还是很值得一试。
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 天前
所有评论(0)