Here's a minimal working example with optional FPS control. If you need to extract frames from your process back to the main program, you can use multiprocessing.Queue() to transfer frames since multiprocesses have an independent memory stack.

import multiprocessing as mp
import cv2, time

def capture_frames():
    src = 'test.mp4'
    capture = cv2.VideoCapture(src)
    capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)

    # FPS = 1/X, X = desired FPS
    FPS = 1/120
    FPS_MS = int(FPS * 1000)

    while True:
        # Ensure camera is connected
        if capture.isOpened():
            (status, frame) = capture.read()
            
            # Ensure valid frame
            if status:
                cv2.imshow('frame', frame)
            else:
                break
            if cv2.waitKey(1) & 0xFF == ord('q'):
                break
            time.sleep(FPS)

    capture.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    print('Starting video stream')
    capture_process = mp.Process(target=capture_frames, args=())
    capture_process.start()

Related camera/IP/RTSP/streaming, FPS, video, threading, and multiprocessing posts

  1. Python OpenCV streaming from camera - multithreading, timestamps

  2. Video Streaming from IP Camera in Python Using OpenCV cv2.VideoCapture

  3. How to capture multiple camera streams with OpenCV?

  4. OpenCV real time streaming video capture is slow. How to drop frames or get synced with real time?

  5. Storing RTSP stream as video file with OpenCV VideoWriter

  6. OpenCV video saving

  7. Python OpenCV multiprocessing cv2.VideoCapture mp4

Answer from nathancy on Stack Overflow
🌐
GitHub
github.com › mahdizynali › OpenCV-video-multiprocessing
GitHub - mahdizynali/OpenCV-video-multiprocessing: opencv python video multiprocessing besides tkinter video multiprocessing
cap = cv.VideoCapture("input.mp4") output = cv.VideoWriter("output.mp4",cv.VideoWriter_fourcc(*"mp4v"), fps, (width, height)) # or maybe cap = cv.VideoCapture("input.mp4") output = cv.VideoWriter("output.avi",cv.VideoWriter_fourcc('M','J','P','G'), ...
Author: mahdizynali
Discussions

[Question] How does multiprocessing work in python-opencv?
OpenCV uses ffmpeg libraries under the hood, and their implementation does not expose controls for how that additional processing is handled. If you really need to control the number of cores used or similar video processing optimizations you'll need to work within the ffmpeg libraries themselves. I have a c++ ffmpeg playback library I wrote specifically for this purpose. There are two github projects, one for the ffmpeg playback library and one for a win10 wxWidgets Desktop Application hosting the library, demonstrating it's capabilities. The library is intended for video security and ML/DL training, so their is no audio handling and all video processing occurs as fast as possible on a single CPU core. You may find it useful: https://github.com/bsenftner/ffvideo A variant of this library is used within one of the top 5 global enterprise facial recognition systems; I wrote enterprise FR for about a decade before leaving that industry. More on reddit.com
🌐 r/opencv
6
1
November 14, 2022
opencv - Read webcam in python with multiprocessing - Stack Overflow
I have a simple program for reading webcams, but the reading results are very slow, so I lower the quality of reading images from the webcam, but the reading is still slow, so I try to use More on stackoverflow.com
🌐 stackoverflow.com
How many parallel VideoCapture's can one camera handle? - Python - OpenCV
I want to read from one camera in many parallel processes. Here is what I’ve tried. from multiprocessing import Process import cv2 import time import random def get_capture(): time.sleep(random.randint(0, 3)) cap = cv2.VideoCapture(0) w, h = cap.get(cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2... More on forum.opencv.org
🌐 forum.opencv.org
0
February 9, 2024
VideoCapture frame is different when INSIDE and OUTSIDE a Multiprocessing Job
Using VideoCapture's set() method for moving the read-head in a video file, returns different locations/images for the same frame-number when called inside/outside a multiprocessing job. Okay, so... This only happens using Variable Frame Rate (VFR) video. And VFR is the bane of all video processing, so maybe there isn't anything to be done except feed OpenCV ... More on github.com
🌐 github.com
3
November 17, 2020
🌐
Nrsyed
nrsyed.com › 2018 › 07 › 05 › multithreading-with-opencv-python-to-improve-video-processing-performance
Multithreading with OpenCV-Python to improve video processing performance • Najam R. Syed
July 6, 2018 - We import the threading module, which will allow us to spawn new threads. In the class __init__() method, we initialize an OpenCV VideoCapture object and read the first frame.
🌐
OpenCV Q&A Forum
answers.opencv.org › question › 100839 › video-streaming-from-webcam-and-image-processing-with-python
Video Streaming from webcam and image processing with Python - OpenCV Q&A Forum
August 22, 2016 - class Consumer(multiprocessing.Process): def __init__(self, task_queue, result_queue): multiprocessing.Process.__init__(self) self.task_queue = task_queue self.result_queue = result_queue #other initialization stuff def run(self): while True: image = self.task_queue.get() #Do computations on image self.result_queue.put("text") return import cv2 tasks = multiprocessing.Queue() results = multiprocessing.Queue() consumer = Consumer(tasks,results) consumer.start() #Creating window and starting video capturer from camera cv2.namedWindow("preview") vc = cv2.VideoCapture(0) #Try to get the first fram
🌐
Reddit
reddit.com › r/opencv › [question] how does multiprocessing work in python-opencv?
r/opencv on Reddit: [Question] How does multiprocessing work in python-opencv?
November 14, 2022 -

Hi. When I run a python VideoCapture loop, there are processes launched for each CPU core that show the python script running. How does this work under the hood in regard to the OpenCV library? Does it split the incoming video frames so they can be processed in parallel? It shows 15% resource use for each core (out of 20 cores) for a 30 FPS webcam showing simple video without processing using imshow. Can it be fit into a single process if it's only showing the frames? I couldn't find documentation on it.

I'm trying to optimize video processing without using a video card and thought the first step would be to not use all 20 CPU cores when showing video without processing, so that later the code can be used on a smaller device like a Raspberry Pi.

🌐
Stack Overflow
stackoverflow.com › questions › 59960938 › read-webcam-in-python-with-multiprocessing
opencv - Read webcam in python with multiprocessing - Stack Overflow
import cv2 import numpy as np import multiprocessing def get(): global cap cap = cv2.VideoCapture(0) return cap def video(cap): _, frame = cap.read() frame = cv2.flip(frame, 1) return frame if __name__ == "__main__": p1 = multiprocessing.Process(target = get) p1.start() p1.join() while True: frame = video(cap) cv2.imshow("frame", frame) key = cv2.waitKey(1) if key == 27: #Key 'S' break cv2.waitKey(0) cv2.destroyAllWindows() python · opencv ·
🌐
PyImageSearch
pyimagesearch.com › home › blog › multiprocessing with opencv and python
Multiprocessing with OpenCV and Python - PyImageSearch
December 29, 2022 - Now, let’s assume we launch our Python script. The operating system will assign the process to a single one of those cores: Figure 2: Without multiprocessing, your OpenCV program may not be efficiently using all cores or processors available on your machine.
🌐
OpenCV
forum.opencv.org › python
How many parallel VideoCapture's can one camera handle? - Python - OpenCV
February 9, 2024 - I want to read from one camera in many parallel processes. Here is what I’ve tried. from multiprocessing import Process import cv2 import time import random def get_capture(): time.sleep(random.randint(0, 3)) cap = cv2.VideoCapture(0) w, h = cap.get(cv2.CAP_PROP_FRAME_WIDTH), cap.get(cv2.CAP_PROP_FRAME_HEIGHT) if (w, h) == (0, 0): print("Seemingly failed to construct the capture.") print(f"Feed dimensions are {w}x{h}.") success, _ = cap.read() if not s...
Find elsewhere
🌐
Medium
yakhyo.medium.com › multiprocessing-in-python-reading-from-webcam-video-using-multiprocessing-d78c15d1a7e6
Multiprocessing in Python. Reading from webcam/video using multiprocessing. | by Yakhyokhuja Valikhujaev | Medium
April 16, 2021 - from multiprocessing import Pool import os import cv2 def webcam(src): cap = cv2.VideoCapture(src) # webcam:0 ,ip camera: 'link address' fps = cap.get(cv2.CAP_PROP_FPS) while True: ret, frame = cap.read() if ret: frame = cv2.flip(frame, 1) frame = cv2.putText(frame, f'FPS: {str(fps)}', (0, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255)) cv2.imshow('multiprocessing', frame) if cv2.waitKey(1) == ord('q'): break else: break if __name__ == "__main__": with Pool(os.cpu_count()) as p: p.map(webcam, [0]) Reference ·
🌐
GitHub
github.com › opencv › opencv › issues › 18844
VideoCapture frame is different when INSIDE and OUTSIDE a Multiprocessing Job · Issue #18844 · opencv/opencv
November 17, 2020 - Using VideoCapture's set() method for moving the read-head in a video file, returns different locations/images for the same frame-number when called inside/outside a multiprocessing job. Okay, so... This only happens using Variable Frame Rate (VFR) video. And VFR is the bane of all video processing, so maybe there isn't anything to be done except feed OpenCV ...
Author: opencv
🌐
Medium
medium.com › @fanzongshaoxing › asynchronous-cache-of-video-frame-using-opencv-and-multithreading-b98589f036a3
Asynchronous cache of video frame using opencv and multithreading | by zong fan | Medium
March 14, 2019 - When using Process and importing cv2.VideoCapture() as input argument, you may suffer reading stuck during retrieving frames from video stream. It seems to be related to the multi-processing mechanism in Python. Here we want to control the time when to start caching so that unnecessary snippet of frames could be skipped. According to Python multiprocessing mechanism where local variable cannot be passed to sub-process, we need to use synchronization primitives which could be referenced in both processes.
Top answer
1 of 3
11

The simplest approach is to use the newer cv2 module that's based on NumPy arrays. That way you don't have to mess with manual pickling. Here's the fix (I just changed 4 lines of code):

import multiprocessing
import cv2

queue_from_cam = multiprocessing.Queue()

def cam_loop(queue_from_cam):
    print 'initializing cam'
    cap = cv2.VideoCapture(0)
    print 'querying frame'
    hello, img = cap.read()
    print 'queueing image'
    queue_from_cam.put(img)
    print 'cam_loop done'

cam_process = multiprocessing.Process(target=cam_loop,args=(queue_from_cam,))
cam_process.start()

while queue_from_cam.empty():
    pass

print 'getting image'
from_queue = queue_from_cam.get()
print 'saving image'
cv2.imwrite('temp.png', from_queue)
print 'image saved'
2 of 3
6

It appears that the solution was to convert the opencv iplimage object to string, then pickle it before adding it to the queue:

import multiprocessing
import cv
import Image
import pickle
import time

queue_from_cam = multiprocessing.Queue()

def cam_loop(queue_from_cam):
    print 'initializing cam'
    cam = cv.CaptureFromCAM(-1)
    print 'querying frame'
    img = cv.QueryFrame(cam)
    print 'converting image'
    pimg = img.tostring()
    print 'pickling image'
    pimg2 = pickle.dumps(pimg,-1)
    print 'queueing image'
    queue_from_cam.put([pimg2,cv.GetSize(img)])
    print 'cam_loop done'


cam_process = multiprocessing.Process(target=cam_loop,args=(queue_from_cam,))
cam_process.start()

while queue_from_cam.empty():
    pass

print 'getting pickled image'
from_queue = queue_from_cam.get()
print 'unpickling image'
pimg = pickle.loads(from_queue[0])
print 'unconverting image'
cv_im = cv.CreateImageHeader(from_queue[1], cv.IPL_DEPTH_8U, 3)
cv.SetData(cv_im, pimg)
print 'saving image'
cv.SaveImage('temp.png',cv_im)
print 'image saved'
🌐
OpenCV Q&A Forum
answers.opencv.org › question › 229866 › how-to-capture-multi-camera-with-opencv-multi-threading-python
How to capture Multi camera with Opencv Multi threading (python) [closed] - OpenCV Q&A Forum
May 5, 2020 - import cv2 import os import time import threading os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "rtsp_transport;udp" rtsp1 = 'rtsp://userid:[email protected]:8080/h264_ulaw.sdp' rtsp='rtsp://userid:[email protected]:10554/udp/av0_0' def getPicture(url): cam = cv2.VideoCapture(url) a=cam.get(cv2.CAP_PROP_BUFFERSIZE) cam.set(cv2.CAP_PROP_BUFFERSIZE,3) start_frame_number = 20 cam.set(cv2.CAP_PROP_POS_FRAMES, start_frame_number) print("buffer"+str(a)) while True: ret, frame = cam.read() small_frame = cv2.resize(frame, (0, 0), fx=.50, fy=.50) small_frame = cv2.cvtColor(small_frame,cv2.COLOR_BGR2GRA
Top answer
1 of 3
15

Based on the comments on the original question I've done some testing and thought it worth sharing the (interesting) results. Big savings potential for anyone using OpenCV's VideoCapture.set(CAP_PROP_POS_MSEC) or VideoCapture.set(CAP_PROP_POS_FRAMES).

I've done some profiling comparing three options:

1. GET FRAMES BY SEEKING TO TIME:

frames = {}
def get_all_frames_by_ms(time):
    while True:
        video_capture.set(cv2.CAP_PROP_POS_MSEC, time)
        capture_success, frames[time] = video_capture.read()
        if not capture_success:
            break
        time += 1000

2. GET FRAMES BY SEEKING TO FRAME NUMBER:

frames = {}
def get_all_frames_by_frame(time):
    while True:
        # Note my test video is 12.333 FPS, and time is in milliseconds
        video_capture.set(cv2.CAP_PROP_POS_FRAMES, int(time/1000*12.333))
        capture_success, frames[time] = video_capture.read()
        if not capture_success:
            break
        time += 1000

3. GET FRAMES BY GRABBING ALL, BUT RETRIEVING ONLY ONES I WANT:

def get_all_frames_in_order():
    prev_time = -1
    while True:
        grabbed = video_capture.grab()
        if grabbed:
            time_s = video_capture.get(cv2.CAP_PROP_POS_MSEC) / 1000
            if int(time_s) > int(prev_time):
                # Only retrieve and save the first frame in each new second
                self.frames[int(time_s)] = video_capture.retrieve()
            prev_time = time_s
        else:
            break

Running through those three approaches, the timings (from three runs of each) are as follows:

  1. 33.78s 29.65s 29.24s
  2. 31.95s 29.16s 28.35s
  3. 11.81s 10.76s 11.73s

In each case it's saving 100 frames at 1sec intervals into a dictionary, where each frame is a 3072x1728 image, from a .mp4 video file. All on a 2015 MacBookPro with 2.9 GHz Intel Core i5 and 8GB RAM.

Conclusions so far... if you're interested in retrieving only some frames from a video, then very worth looking at running through all frames in order and grabbing them all, but only retrieving those you're interested in - as an alternative to reading (which grabs and retrieves in one go). Gave me an almost 3x speedup.

I've also re-looked at multi-threading on this basis. I've got two test processes - one that gets the frames, and another that processes them once they're available:

frames = {}

def get_all_frames_in_order():
    prev_time = -1
    while True:
        grabbed = video_capture.grab()
        if grabbed:
            time_s = video_capture.get(cv2.CAP_PROP_POS_MSEC) / 1000
            if int(time_s) > int(prev_time):
                # Only retrieve and save the first frame in each new second
                frames[int(time_s)] = video_capture.retrieve()
            prev_time = time_s
        else:
            break

def process_all_frames_as_available(processing_time):
    prev_time = 0
    while True:
        this_time = prev_time + 1000
        if this_time in frames and prev_time in frames:
            # Dummy processing loop - just sleeps for specified time
            sleep(processing_time)
            prev_time += self.time_increment
            if prev_time + self.time_increment > video_duration:
                break
        else:
            # If the frames aren't ready yet, wait a short time before trying again
            sleep(0.02)

For this testing, I then called them either one after the other (sequentially, single threaded), or with the following muti-threaded code:

get_frames_thread = Thread(target=get_all_frames_in_order)
get_frames_thread.start()
process_frames_thread = Thread(target=process_all_frames_as_available, args=(0.02,))
process_frames_thread.start()
get_frames_thread.join()
process_frames_thread.join()

Based on that, I'm now happy that multi-threading is working effectively and saving a significant amount of time. I generated timings for the two functions above separately, and then together in both single-threaded and multi-threaded modes. The results are below (number in bracket is the time in seconds that the 'processing' for each frame takes, which in this case is just a dummy / delay):

get_all_frames_in_order - 2.99s

Process time = 0.02s per frame:
process_all_frames_as_available - 0.97s
single-threaded - 3.99s
multi-threaded - 3.28s

Process time = 0.1s per frame:
process_all_frames_as_available - 4.31s
single-threaded - 7.35s
multi-threaded - 4.46s

Process time = 0.2s per frame:
process_all_frames_as_available - 8.52s
single-threaded - 11.58s
multi-threaded - 8.62s

As you can hopefully see, the multi-threading results are very good. Essentially, it takes just ~0.2s longer to do both functions in parallel than the slower of the two functions running entirely separately.

Hope that helps someone!

2 of 3
2

Coincidentally, I've worked on a similar problem, and I have created a python library (more of a thin wrapper) for reading videos. The library is called mydia.

The library does not use OpenCV. It uses FFmpeg as the backend for reading and processing videos.

mydia supports custom frame selection, frame resizing, grayscale conversion and much more. The documentation can be viewed here

So, if you want to select N frames per second (where N = 1 in your case), the following code would do it:

import numpy as np
from mydia import Videos

video_path = "path/to/video"

def select_frames(total_frames, num_frames, fps, *args):
    """This function will return the indices of the frames to be captured"""
    N = 1
    t = np.arange(total_frames)
    f = np.arange(num_frames)
    mask = np.resize(f, total_frames)

    return t[mask < N][:num_frames].tolist()

# Let's assume that the duration of your video is 120 seconds
# and you want 1 frame for each second 
# (therefore, setting `num_frames` to 120)
reader = Videos(num_frames=120, mode=select_frames)

video = reader.read(video_path)  # A video tensor/array

The best part is that internally, only those frames that are required are read, and therefore the process is much faster (which is what I believe you are looking for).

The installation of mydia is extremely simple and can be viewed here.

This might have a slight learning curve, but I believe that it is exactly what you are looking for.

Moreover, if you have multiple videos, you could use multiple workers for reading them in parallel. For instance:

from mydia import Videos

path = "path/to/video"
reader = Videos()
video = reader.read(path, workers=4)

Depending on your CPU, this could give you a significant speed-up.

Hope this helps !!

🌐
OpenCV
forum.opencv.org › python
Unable to use cv2.VideoCapture in 2 different programs parallely - Python - OpenCV
January 22, 2022 - I am using cv2.VideoCapture(0) to open the camera and read it’s frames in 2 different neural networks namely Object detection and Semantic segmentation. I tried passing the frames to both of the networks at the same time, but due to a bug in tensorflow, I am now forced to run these network ...
🌐
Medium
sihabsahariar.medium.com › a-multi-threading-approach-for-faster-opencv-video-streaming-in-python-7324f11dbd2f
A Multi-Threading Approach for Faster OpenCV Video Streaming in Python | by Sihab Sahariar | Medium
September 20, 2022 - A Multi-Threading Approach for Faster OpenCV Video Streaming in Python Overview In this article we will see two Python code examples, with without multi-threading, for reading video frames from a …
🌐
Xailient
xailient.com › blog › parallel-processing-using-python-for-faster-video-processing
Parallel Processing Using Python for Faster Video Processing | Xailient
Let’s import the required Python libraries. ... subprocess: to start new processes, connect to their input/output/error pipes, and obtain their return codes. multiprocessing: to parallelize the execution of a function across multiple input values, distributing the input data across processes
🌐
OpenCV
forum.opencv.org › python
VideoCapture Opens video sources by multi Thread - Python - OpenCV
March 22, 2022 - I try to open two rtsp streams with two seperate threads,each thread execute the same function of connecting a rtsp stream ,and calculating the consuming time . I think I could get roughly equivalent spending time , but…
🌐
GitHub
gist.github.com › yinguobing › 7d1318e31f6e3455f30527a39361512a
OpenCV minimal example showing multithreaded video processing. · GitHub
Isn't this just a near straight up copy from the official documentation? https://github.com/opencv/opencv/blob/master/samples/python/video_threaded.py
🌐
Stack Overflow
stackoverflow.com › questions › 62126858 › python-multiprocessing-and-opencv-videocapture-read-error
python multiprocessing and opencv VideoCapture read() error - Stack Overflow
June 1, 2020 - def read_frames(video_path, frame_q, use_webcam, shape): allframes = int(reader.get(cv2.CAP_PROP_FRAME_COUNT)) resize_height = shape[1] resize_width = shape[2] cap = cv2.VideoCapture(video_path) for ii in range(allframes): if reader.isOpened(): print("[INFO] reader is opended") # while frame_q.qsize() > 500: # time.sleep(1) ret, frame = cap.read() print("[INFO] ret : ", ret) resize_img = cv2.resize(frame, dsize=(resize_width, resize_height), interpolation=cv2.INTER_LINEAR) cur_img = resize_img[:, :, ::-1] frame_q.put(cur_img)