• 一种在 Python 中实现更快 OpenCV 视频流的多线程方法


    e65c17077560cdad5e035db137dad9ba.png

    概述

    在本文中,我们将看到两个没有多线程的 Python 代码示例,用于从摄像头读取视频帧。我们将看到使用/不使用多线程获得的 FPS 的差异。

    什么是多线程?

    线程是进程中的一个执行单元。多线程是指通过在线程之间快速切换对 CPU 的控制(称为上下文切换)来并发执行多个线程。在我们的示例中,我们将看到多线程通过提高 FPS(每秒帧数)实现更快的实时视频处理。

    Python中的线程基础

    以下代码片段显示了如何使用python 中的threading模块创建线程

    1. # importing the threading module 
    2. import threading 
    3. # importing the time module 
    4. import time
    5. # Function to print "Hello", however, the function sleeps
    6. for 2 seconds at the 11th iteration
    7. def print_hello(): 
    8.   for i in range(20):
    9.     if i == 10:
    10.       time.sleep(2)
    11.     print("Hello")
    12. # Function to print numbers till a given number
    13. def print_numbers(num): 
    14.   for i in range(num+1):
    15.     print(i)
    16. # Creating the threads. Target is set to the name of the
    17. # function that neeeds to be executed inside the thread and
    18. # args are the arguments to be supplied to the function that
    19. # needs to be executed.
    20. print("Greetings from the main thread.")
    21. thread1 = threading.Thread(target = print_hello, args = ())
    22. thread2 = threading.Thread(target = print_numbers, args = (10,))  
    23. # Starting the two threads
    24. thread1.start() 
    25. thread2.start() 
    26. print("It's the main thread again!")

    让我们通过跟踪代码的执行来尝试理解输出:

    1. 主线程执行。打印“Greetings from the main thread”,创建thread1thread2并启动线程。

    2. 发生上下文切换,开始执行thread1

    3. 在前十次迭代之后,thread1进入睡眠状态,thread2开始执行,在下一次上下文切换之前完成。

    4. 现在,主线程获得了 CPU 的控制权并打印出“It’s the main thread again!”

    5. 另一个上下文切换发生,thread2恢复执行并完成。

    6. 由于主线程没有更多指令要执行,因此程序终止。

    使用thread.join()

    如果需要阻塞主线程,直到thread1和thread2完成执行,该怎么办?

    thread.join()会派上用场,因为它会阻塞调用线程,直到调用其 join() 方法的线程终止:

    1. # importing the threading module 
    2. import threading 
    3. # importing the time module 
    4. import time
    5. # Function to print "Hello", however, the function sleeps
    6. for 2 seconds at the 11th iteration
    7. def print_hello(): 
    8.   for i in range(20):
    9.     if i == 10:
    10.       time.sleep(2)
    11.     print("Hello")
    12. # Function to print numbers till a given number
    13. def print_numbers(num): 
    14.   for i in range(num+1):
    15.     print(i)
    16. # Creating the threads. Target is set to the name of the
    17. # function that neeeds to be executed inside the thread and
    18. # args are the arguments to be supplied to the function that
    19. # needs to be executed.
    20. print("Greetings from the main thread.")
    21. thread1 = threading.Thread(target = print_hello, args = ())
    22. thread2 = threading.Thread(target = print_numbers, args = (10,))  
    23. # Starting the two threads
    24. thread1.start() 
    25. thread2.start() 
    26. thread1.join()
    27. thread2.join()
    28. print("It's the main thread again!")
    29. print("Threads 1 and 2 have finished executing.")

    原因多线程有助于更快的处理

    视频处理代码分为两部分:从摄像头读取下一个可用帧并对帧进行视频处理,例如运行深度学习模型进行人脸识别等。

    读取下一帧并在没有多线程的程序中按顺序进行处理。程序等待下一帧可用,然后再对其进行必要的处理。读取帧所需的时间主要与请求、等待和将下一个视频帧从相机传输到内存所需的时间有关。对视频帧进行计算所花费的时间,无论是在 CPU 还是 GPU 上,占据了视频处理所花费的大部分时间。

    在具有多线程的程序中,读取下一帧并处理它不需要是顺序的。当一个线程执行读取下一帧的任务时,主线程可以使用 CPU 或 GPU 来处理最后读取的帧。这样,通过重叠两个任务,可以减少读取和处理帧的总时间。

    OpenCV 代码——没有多线程

    1. # importing required libraries 
    2. import cv2 
    3. import time
    4. # opening video capture stream
    5. vcap = cv2.VideoCapture(0)
    6. if vcap.isOpened() is False :
    7.     print("[Exiting]: Error accessing webcam stream.")
    8.     exit(0)
    9. fps_input_stream = int(vcap.get(5))
    10. print("FPS of webcam hardware/input stream: {}".format(fps_input_stream))
    11. grabbed, frame = vcap.read() # reading single frame for initialization/ hardware warm-up
    12. # processing frames in input stream
    13. num_frames_processed = 0 
    14. start = time.time()
    15. while True :
    16.     grabbed, frame = vcap.read()
    17.     if grabbed is False :
    18.         print('[Exiting] No more frames to read')
    19.         break
    20. # adding a delay for simulating time taken for processing a frame 
    21.     delay = 0.03 # delay value in seconds. so, delay=1 is equivalent to 1 second 
    22.     time.sleep(delay) 
    23.     num_frames_processed += 1
    24. cv2.imshow('frame' , frame)
    25.     key = cv2.waitKey(1)
    26.     if key == ord('q'):
    27.         break
    28. end = time.time()
    29. # printing time elapsed and fps 
    30. elapsed = end-start
    31. fps = num_frames_processed/elapsed 
    32. print("FPS: {} , Elapsed Time: {} , Frames Processed: {}".format(fps, elapsed, num_frames_processed))
    33. # releasing input stream , closing all windows 
    34. vcap.release()
    35. cv2.destroyAllWindows()

    OpenCV 代码——多线程

    1. # importing required libraries 
    2. import cv2 
    3. import time 
    4. from threading import Thread # library for implementing multi-threaded processing
    5. # defining a helper class for implementing multi-threaded processing 
    6. class WebcamStream :
    7.     def __init__(self, stream_id=0):
    8.         self.stream_id = stream_id   # default is 0 for primary camera 
    9.         
    10.         # opening video capture stream 
    11.         self.vcap      = cv2.VideoCapture(self.stream_id)
    12.         if self.vcap.isOpened() is False :
    13.             print("[Exiting]: Error accessing webcam stream.")
    14.             exit(0)
    15.         fps_input_stream = int(self.vcap.get(5))
    16.         print("FPS of webcam hardware/input stream: {}".format(fps_input_stream))
    17.             
    18.         # reading a single frame from vcap stream for initializing 
    19.         self.grabbed , self.frame = self.vcap.read()
    20.         if self.grabbed is False :
    21.             print('[Exiting] No more frames to read')
    22.             exit(0)
    23. # self.stopped is set to False when frames are being read from self.vcap stream 
    24.         self.stopped = True
    25. # reference to the thread for reading next available frame from input stream 
    26.         self.t = Thread(target=self.update, args=())
    27.         self.t.daemon = True # daemon threads keep running in the background while the program is executing 
    28.         
    29.     # method for starting the thread for grabbing next available frame in input stream 
    30.     def start(self):
    31.         self.stopped = False
    32.         self.t.start()
    33. # method for reading next frame 
    34.     def update(self):
    35.         while True :
    36.             if self.stopped is True :
    37.                 break
    38.             self.grabbed , self.frame = self.vcap.read()
    39.             if self.grabbed is False :
    40.                 print('[Exiting] No more frames to read')
    41.                 self.stopped = True
    42.                 break 
    43.         self.vcap.release()
    44. # method for returning latest read frame 
    45.     def read(self):
    46.         return self.frame
    47. # method called to stop reading frames 
    48.     def stop(self):
    49.         self.stopped = True
    50. # initializing and starting multi-threaded webcam capture input stream 
    51. webcam_stream = WebcamStream(stream_id=0) #  stream_id = 0 is for primary camera 
    52. webcam_stream.start()
    53. # processing frames in input stream
    54. num_frames_processed = 0 
    55. start = time.time()
    56. while True :
    57.     if webcam_stream.stopped is True :
    58.         break
    59.     else :
    60.         frame = webcam_stream.read()
    61. # adding a delay for simulating time taken for processing a frame 
    62.     delay = 0.03 # delay value in seconds. so, delay=1 is equivalent to 1 second 
    63.     time.sleep(delay) 
    64.     num_frames_processed += 1
    65. cv2.imshow('frame' , frame)
    66.     key = cv2.waitKey(1)
    67.     if key == ord('q'):
    68.         break
    69. end = time.time()
    70. webcam_stream.stop() # stop the webcam stream
    71. # printing time elapsed and fps 
    72. elapsed = end-start
    73. fps = num_frames_processed/elapsed 
    74. print("FPS: {} , Elapsed Time: {} , Frames Processed: {}".format(fps, elapsed, num_frames_processed))
    75. # closing all windows 
    76. cv2.destroyAllWindows()

    下载所有代码

    https://github.com/SihabSahariar/Multi-threading-OpenCV-

    资源

    1. https://stackoverflow.com/questions/55099413/python-opencv-streaming-from-camera-multithreading-timestamps

    2. https://stackoverflow.com/questions/55828451/video-streaming-from-ip-camera-in-python-using-opencv-cv2-videocapture

    3. https://stackoverflow.com/questions/58592291/how-to-capture-multiple-camera-streams-with-opencv

    4. https://stackoverflow.com/questions/58293187/opencv-real-time-streaming-video-capture-is-slow-how-to-drop-frames-or-get-sync

    5. https://stackoverflow.com/questions/55141315/storing-rtsp-stream-as-video-file-with-opencv-videowriter

    6. https://stackoverflow.com/questions/29317262/opencv-video-saving-in-python/71624807#71624807

    7. https://stackoverflow.com/questions/72120491/python-opencv-multiprocessing-cv2-videocapture-mp4

    参考

    1. https://github.com/PyImageSearch/imutils/tree/master/imutils/video

    2. https://www.pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/

    3. https://forum.opencv.org/t/videocapture-opens-video-sources-by-multi-thread/8045

    ☆ END ☆

    如果看到这里,说明你喜欢这篇文章,请转发、点赞。微信搜索「uncle_pn」,欢迎添加小编微信「 woshicver」,每日朋友圈更新一篇高质量博文。

    扫描二维码添加小编↓

    890f36c879949f453b110b6885e2b9a2.jpeg

  • 相关阅读:
    数据中心不间断电源蓄电池智能预维系统及技术研究
    Spring Boot 打印日志文件
    Google Authenticator 算法
    Acwing第76场周赛
    web3.0链游农民世界开发搭建0撸狼人杀玩法模式定制开发
    使用drawio的图层构建更强大的图表
    基于Python的朋友圈关系数据分析与实现
    LLC 三相移相PWM产生原理分析
    IDEA类和方法注释模板配置
    数据结构与算法----栈和队列(Stack & Queue)
  • 原文地址:https://blog.csdn.net/woshicver/article/details/128168684