with() statement to read from VideoCapture in opencv?

Question:

I like using a with statement for accessing files and database connections because it automatically tears down the connection for me, in the event of error or file close.

f = open('file.txt', 'r')
for i in f():
   print(i)
f.close()

versus

with open('file.txt', 'r') as f:
   for i in f:
       print(i)

Is there an equivalent rephrasing for reading from the camera buffer in the following?:

c = cv.VideoCapture(0)    
while(1):
    _,f = c.read()
    cv.imshow('e2',f)
    if cv.waitKey(5)==27:
        cv.waitKey()
        break
c.release()

I’ve tried:

c = cv.VideoCapture(0)    
while(1):
   with c.read() as _,f:
       cv.imshow('e2',f)
       if cv.waitKey(5)==27:
           cv.waitKey()
           break

—with no luck. It looks like the tear-down/release is a different kind of function. Is this idiom possible here?

Asked By: Mittenchops

||

Answers:

I don’t know opencv, so there may be a better answer – but you can always implement the context manager yourself by defining the __enter__ and __exit__ hooks:

class MyVideoCapture(cv.VideoCapture):
    def __enter__(self):
        return self
    def __exit__(self, *args):
        self.release()

The usage would then look like:

with MyVideoCapture(0) as c:    
    while(True):
        _, f = c.read()
        cv.imshow('e2', f)
        if cv.waitKey(5) == 27:
            cv.waitKey()
            break

and the resource will be released after you hit the break statement.

Based on your comment, it looks like opencv is doing something funky here. You can also create a custom class to wrap the VideoCapture instance. In today’s world, I’d probably use contextlib

@contextlib.contextmanager
def video_capture_wrapper(*args, **kwargs):
    try:
        vid_stream = VideoCapture(*args, **kwargs)
        yield vid_stream
    finally:
         vid_stream.release()

and the usage here would be:

with video_capture_wrapper(0) as vid_stream:    
    while(True):
        _, f = vid_stream.read()
        cv.imshow('e2', f)
        if cv.waitKey(5) == 27:
            cv.waitKey()
            break
Answered By: mgilson

Another way using contextlib.contextmanager:

from contextlib import contextmanager

@contextmanager
def VideoCapture(*args, **kwargs):
    cap = cv2.VideoCapture(*args, **kwargs)
    try:
        yield cap
    finally:
        cap.release()

(note: the accepted answer has been edited to include this suggestion)

Answered By: Eric
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.