多线程在图像处理中的应用 - 使用Python和OpenCV进行视频处理

3

我正在使用Python的OpenCV库开发实时视频目标检测。目前程序只运行在一个线程上,导致屏幕显示的视频看起来不像真正的视频,因为检测处理时间有延迟。因此,我尝试使用多线程重新实现该程序。我使用一个线程用于读取视频帧,另一个线程用于显示检测结果,并且同时使用大约5个线程对多个帧进行检测。我已经编写了以下代码,但结果与单线程程序没有区别。由于我是Python新手,所以希望得到任何帮助。

import threading, time
import cv2
import queue


def detect_object():
    while True:
        print("get")
        frame = input_buffer.get()
        if frame is not None:
            time.sleep(1)
            detection_buffer.put(frame)
        else:
            break
    return


def show():
    while True:
        print("show")
        frame = detection_buffer.get()
        if frame is not None:
            cv2.imshow("Video", frame)
        else:
            break
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    return


if __name__ == "__main__":

    input_buffer = queue.Queue()
    detection_buffer = queue.Queue()

    cap = cv2.VideoCapture(0)

    for i in range(5):
        t = threading.Thread(target=detect_object)
        t.start()

    t1 = threading.Thread(target=show)
    t1.start()

    while True:
        ret, frame = cap.read()
        if ret:
            input_buffer.put(frame)
            time.sleep(0.025)
        else:
            break

    print("program ended")


1
好的,你的detect_object线程从队列中获取一帧后,第一件(事实上也是唯一的)要做的就是睡眠1秒钟......因此5个线程每秒不会比5帧更好。 - Dan Mašek
@DanMašek 感谢您的回复。是的,我明白了。我这样做是为了简化代码。我拥有的检测算法大约需要0.7秒钟。因此,我认为睡眠1秒钟可以模拟那里的延迟。 - Haha
2个回答

4

假定检测算法对CPU消耗较大,因此需要使用多进程而非多线程,因为多个线程由于争用全局解释器锁而无法并行运行Python字节码。您还应当摒弃所有sleep调用。此外,以您目前的方式运行多线程或多进程时,不清楚保证输出的帧顺序是否正确,换言之,第二帧处理可能会在第一帧处理完成之前就被写入到detection_buffer中。

以下代码将使用6个进程的进程池(现在无需隐式输入队列)。

from multiprocessing import Pool, Queue
import time
import cv2

# intialize global variables for the pool processes:
def init_pool(d_b):
    global detection_buffer
    detection_buffer = d_b


def detect_object(frame):
    time.sleep(1)
    detection_buffer.put(frame)


def show():
    while True:
        print("show")
        frame = detection_buffer.get()
        if frame is not None:
            cv2.imshow("Video", frame)
        else:
            break
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    return


# required for Windows:
if __name__ == "__main__":

    detection_buffer = Queue()
    # 6 workers: 1 for the show task and 5 to process frames:
    pool = Pool(6, initializer=init_pool, initargs=(detection_buffer,))
    # run the "show" task:
    show_future = pool.apply_async(show)

    cap = cv2.VideoCapture(0)

    futures = []
    while True:
        ret, frame = cap.read()
        if ret:
            f = pool.apply_async(detect_object, args=(frame,))
            futures.append(f)
            time.sleep(0.025)
        else:
            break
    # wait for all the frame-putting tasks to complete:
    for f in futures:
        f.get()
    # signal the "show" task to end by placing None in the queue
    detection_buffer.put(None)
    show_future.get()
    print("program ended")

1

对我来说,我所做的是为两个函数构建了两个线程,并使用一个队列:

  1. 获取帧并处理
  2. 显示

cap变量在我的处理函数内部。

def process:
     cap = cv2.VideoCapture(filename)
     ret, frame = cap.read()

      while ret:
          ret, frame = cap.read()
#detection part in my case I use tensorflow then 
     # end of detection part 
          q.put(result_of_detection)

def Display():
  while True:
    if q.empty() != True:
        frame = q.get()
        cv2.imshow("frame1", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

if __name__ == '__main__':
#  start threads
p1 = threading.Thread(target=process)
p2 = threading.Thread(target=Display)
p1.start()
p2.start()

对我来说它完全有效

希望我有所帮助:D

此外,我认为这个页面可能会有所帮助: https://pyimagesearch.com/2015/12/21/increasing-webcam-fps-with-python-and-opencv/


你的回答可以通过添加更多支持性信息来改进。请点击[编辑]以添加进一步的细节,例如引用或文档,以便其他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
1
如果我需要在帧上进行处理,这该怎么办?我的想法是使用3个线程:一个用于获取帧,另一个用于处理它,最后一个用于显示图像。这不会创建并发问题吗?“显示线程”与“图像处理线程”不在同一帧上。 - Angel Ortiz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接