Kumaran
Kumaran

Reputation: 3650

Python3 parallel process opencv video frames

I have a video file which I need to process frame by frame and need to show the results in the frames afterwards. Currently I am doing the processing sequentially and showing the frames one by one.

Now I would like to process the frames parallely instead of sequentially. Once X number of frames has been processed then the cv2.imshow has to appear and has to show the processed frames in correct order.

Currently my sequential code looks like this

import cv2
import requests


def process_frame(bgr_image, jpg_as_text):
    try:
        # Post to api for processing and get the results
        # result = requests.post("example.com", data={"jpg": jpg_as_text})

        # Add results to bgr_image
        # cv2.putText()
    except Exception as e:
        print(e)
        pass
    # Show the frame
    cv2.imshow("frame", bgr_image)


video = cv2.VideoCapture("video.mp4")
i = 0


while video.isOpened():
    ret, bgr_image = video.read()
    if ret == True:
        img_height, img_width, _ = bgr_image.shape
        jpg_as_text = cv2.imencode(".jpg", bgr_image)[1].tostring()
        process_frame(bgr_image, jpg_as_text)
        print(i)
        i += 1
    else:
        break
    if cv2.waitKey(1) & 0xFF == ord("q"):
        break

video.release()
cv2.destroyAllWindows()

Now what should I refactor to do parallel processing and preview the frames once X number of frames are processed.

Upvotes: 3

Views: 9652

Answers (1)

Michael James
Michael James

Reputation: 96

OpenCV has an example for multithreaded video processing in their github repo.

https://github.com/opencv/opencv/blob/master/samples/python/video_threaded.py

Import ThreadPool from multiprocessing.pool and kick off a new thread for every cpu core. OpenCV has a function called getNumberOfCPUs()

Example:

from __future__ import print_function

import numpy as np
import cv2 as cv

from multiprocessing.pool import ThreadPool
from collections import deque

from common import clock, draw_str, StatValue
import video


class DummyTask:
    def __init__(self, data):
    self.data = data
def ready(self):
    return True
def get(self):
    return self.data

if __name__ == '__main__':
    import sys

    print(__doc__)

    try:
        fn = sys.argv[1]
    except:
        fn = 0
    cap = video.create_capture(fn)


    def process_frame(frame, t0):
    # some intensive computation...
        frame = cv.medianBlur(frame, 19)
        frame = cv.medianBlur(frame, 19)
        return frame, t0

    threadn = cv.getNumberOfCPUs()
    pool = ThreadPool(processes = threadn)
    pending = deque()

    threaded_mode = True

    latency = StatValue()
    frame_interval = StatValue()
    last_frame_time = clock()
    while True:
        while len(pending) > 0 and pending[0].ready():
            res, t0 = pending.popleft().get()
            latency.update(clock() - t0)
            draw_str(res, (20, 20), "threaded      :  " +             str(threaded_mode))
            draw_str(res, (20, 40), "latency        :  %.1f ms" %     (latency.value*1000))
            draw_str(res, (20, 60), "frame interval :  %.1f ms" % (frame_interval.value*1000))
            cv.imshow('threaded video', res)
        if len(pending) < threadn:
            ret, frame = cap.read()
            t = clock()
            frame_interval.update(t - last_frame_time)
            last_frame_time = t
            if threaded_mode:
                task = pool.apply_async(process_frame, (frame.copy(), t))
            else:
                task = DummyTask(process_frame(frame, t))
            pending.append(task)
        ch = cv.waitKey(1)
        if ch == ord(' '):
            threaded_mode = not threaded_mode
        if ch == 27:
            break
    cv.destroyAllWindows()

You should be able to use that example code and place your image processing in the process_frame function.

Add a counter to in your loop and call cv2.imshow when count == X

Upvotes: 8

Related Questions