OpenCV/Python: read specific frame using VideoCapture - python

Is there a way to get a specific frame using VideoCapture() method?
My current code is:
import numpy as np
import cv2
cap = cv2.VideoCapture('video.avi')
This is my reference tutorial.

The following code can accomplish that:
import cv2
cap = cv2.VideoCapture(videopath)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number-1)
res, frame = cap.read()
frame_number is an integer in the range 0 to the number of frames in the video.
Notice: you should set frame_number-1 to force reading frame frame_number. It's not documented well but that is how the VideoCapture module behaves.
One may obtain amount of frames by:
amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)

Thank you GPPK.
The video parameters should be given as integers. Each flag has its own value. See here for the code.
The correct solution is:
import numpy as np
import cv2
#Get video name from user
#Ginen video name must be in quotes, e.g. "pirkagia.avi" or "plaque.avi"
video_name = input("Please give the video name including its extension. E.g. \"pirkagia.avi\":\n")
#Open the video file
cap = cv2.VideoCapture(video_name)
#Set frame_no in range 0.0-1.0
#In this example we have a video of 30 seconds having 25 frames per seconds, thus we have 750 frames.
#The examined frame must get a value from 0 to 749.
#For more info about the video flags see here: https://stackoverflow.com/questions/11420748/setting-camera-parameters-in-opencv-python
#Here we select the last frame as frame sequence=749. In case you want to select other frame change value 749.
#BE CAREFUL! Each video has different time length and frame rate.
#So make sure that you have the right parameters for the right video!
time_length = 30.0
fps=25
frame_seq = 749
frame_no = (frame_seq /(time_length*fps))
#The first argument of cap.set(), number 2 defines that parameter for setting the frame selection.
#Number 2 defines flag CV_CAP_PROP_POS_FRAMES which is a 0-based index of the frame to be decoded/captured next.
#The second argument defines the frame number in range 0.0-1.0
cap.set(2,frame_no);
#Read the next frame from the video. If you set frame 749 above then the code will return the last frame.
ret, frame = cap.read()
#Set grayscale colorspace for the frame.
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#Cut the video extension to have the name of the video
my_video_name = video_name.split(".")[0]
#Display the resulting frame
cv2.imshow(my_video_name+' frame '+ str(frame_seq),gray)
#Set waitKey
cv2.waitKey()
#Store this frame to an image
cv2.imwrite(my_video_name+'_frame_'+str(frame_seq)+'.jpg',gray)
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()

If you want an exact frame, you could just set the VideoCapture session to that frame. It's much more intuitive to automatically call on that frame. The "correct" solution requires you to input known data: like fps, length, and whatnot. All you need to know with the code below is the frame you want to call.
import numpy as np
import cv2
cap = cv2.VideoCapture(video_name) # video_name is the video being called
cap.set(1,frame_no) # Where frame_no is the frame you want
ret, frame = cap.read() # Read the frame
cv2.imshow('window_name', frame) # show frame on window
If you want to hold the window, until you press exit:
while True:
ch = 0xFF & cv2.waitKey(1) # Wait for a second
if ch == 27:
break

Set a specific frame
From the documentation of the VideoCaptureProperties (docs) is possible to see that the way to set the frame in the VideoCapture is:
frame = 30
cap.set(cv2.CAP_PROP_POS_FRAMES, frame)
Notice that you don't have to pass to the function frame - 1 because, as the documentation says, the flag CAP_PROP_POS_FRAMES rapresent the "0-based index of the frame to be decoded/captured next".
Concluding a full example where i want to read a frame at each second is:
import cv2
cap = cv2.VideoCapture('video.avi')
# Get the frames per second
fps = cap.get(cv2.CAP_PROP_FPS)
# Get the total numer of frames in the video.
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
frame_number = 0
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) # optional
success, image = cap.read()
while success and frame_number <= frame_count:
# do stuff
frame_number += fps
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, image = cap.read()
Set a specific time
In the documentation linked above is possible to see that the way to set a specific time in the VideoCapture is:
milliseconds = 1000
cap.set(cv2.CAP_PROP_POS_MSEC, milliseconds)
And like before a full example that read a frame each second che be achieved in this way:
import cv2
cap = cv2.VideoCapture('video.avi')
# Get the frames per second
fps = cap.get(cv2.CAP_PROP_FPS)
# Get the total numer of frames in the video.
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
# Calculate the duration of the video in seconds
duration = frame_count / fps
second = 0
cap.set(cv2.CAP_PROP_POS_MSEC, second * 1000) # optional
success, image = cap.read()
while success and second <= duration:
# do stuff
second += 1
cap.set(cv2.CAP_PROP_POS_MSEC, second * 1000)
success, image = cap.read()

For example, to start reading 15th frame of the video you can use:
frame = 15
cap.set(cv2.CAP_PROP_POS_FRAMES, frame-1)

In addition, I want to say, that using of CAP_PROP_POS_FRAMES property does not always give you the correct result. Especially when you deal with compressed files like mp4 (H.264).
In my case when I call cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) for .mp4 file, it returns False, but when I call it for .avi file, it returns True.
Take into consideration when decide using this 'feature'.
very-hit recommends using CV_CAP_PROP_POS_MSEC property.
Read this thread for additional info.

Related

Compress video in Python using OpenCV and algorithm

so I want to create a program in which I will compress a video using this method:
Save a frame for a video each second, and for the rest of the frames in that second, save only the changes from that main frame. And then combine all that info to create a smaller-sized video.
So my idea is to iterate through each frame, save that frame in an array, and when I reach the main frame, find the differences and rewrite all the other frames. However I'm not sure how I can create a video using differences and a main frame afterward... Should I use cv2.VideoWriter or that only works with frames that haven't been altered?
This is what I have for now (I haven't saved the frames yet because I'm not sure of the format that I need):
import cv2
import time
imcap = cv2.VideoCapture('test2.mp4') # test 1
sample_rate = 30
success, img = imcap.read() # get the next frame
frame_no = 0
fps = imcap.get(cv2.CAP_PROP_FPS)
print("Frames per second: {0}".format(fps))
while success:
frame_no += 1
if frame_no % sample_rate == 0:
cv2.imshow('frame', img)
cv2.waitKey()
print(frame_no)
# read next frame
success, img = imcap.read()
imcap.release()

Get Frame at half of video with opencv

is there a way to get a frame form opencv, but around half of the video (for example if the video is 10 minutes I want to save a frame at 5:00 minutes). I have already seen something on the internet but everyone just goes ahead frame by frame. Thank you for your help
I believe you are looking for something like this:
import cv2
# Read the Video (Change the filename as per your file)
cap = cv2.VideoCapture("video.mp4")
# Get the total number of frames
length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
half_point = length//2 # Approximately half if number of frames are odd
# Set the reader to the given frame number (half_point)
cap.set(cv2.CAP_PROP_POS_FRAMES, half_point)
# Read the frame
ret, frame = cap.read()
# Release the file pointer
cap.release()
And you should get the half-point frame in the variable frame.

How does time work for .read() command? Extracting a frame from certain time with OpenCV? [duplicate]

Is there a way to get a specific frame using VideoCapture() method?
My current code is:
import numpy as np
import cv2
cap = cv2.VideoCapture('video.avi')
This is my reference tutorial.
The following code can accomplish that:
import cv2
cap = cv2.VideoCapture(videopath)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number-1)
res, frame = cap.read()
frame_number is an integer in the range 0 to the number of frames in the video.
Notice: you should set frame_number-1 to force reading frame frame_number. It's not documented well but that is how the VideoCapture module behaves.
One may obtain amount of frames by:
amount_of_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
Thank you GPPK.
The video parameters should be given as integers. Each flag has its own value. See here for the code.
The correct solution is:
import numpy as np
import cv2
#Get video name from user
#Ginen video name must be in quotes, e.g. "pirkagia.avi" or "plaque.avi"
video_name = input("Please give the video name including its extension. E.g. \"pirkagia.avi\":\n")
#Open the video file
cap = cv2.VideoCapture(video_name)
#Set frame_no in range 0.0-1.0
#In this example we have a video of 30 seconds having 25 frames per seconds, thus we have 750 frames.
#The examined frame must get a value from 0 to 749.
#For more info about the video flags see here: https://stackoverflow.com/questions/11420748/setting-camera-parameters-in-opencv-python
#Here we select the last frame as frame sequence=749. In case you want to select other frame change value 749.
#BE CAREFUL! Each video has different time length and frame rate.
#So make sure that you have the right parameters for the right video!
time_length = 30.0
fps=25
frame_seq = 749
frame_no = (frame_seq /(time_length*fps))
#The first argument of cap.set(), number 2 defines that parameter for setting the frame selection.
#Number 2 defines flag CV_CAP_PROP_POS_FRAMES which is a 0-based index of the frame to be decoded/captured next.
#The second argument defines the frame number in range 0.0-1.0
cap.set(2,frame_no);
#Read the next frame from the video. If you set frame 749 above then the code will return the last frame.
ret, frame = cap.read()
#Set grayscale colorspace for the frame.
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#Cut the video extension to have the name of the video
my_video_name = video_name.split(".")[0]
#Display the resulting frame
cv2.imshow(my_video_name+' frame '+ str(frame_seq),gray)
#Set waitKey
cv2.waitKey()
#Store this frame to an image
cv2.imwrite(my_video_name+'_frame_'+str(frame_seq)+'.jpg',gray)
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
If you want an exact frame, you could just set the VideoCapture session to that frame. It's much more intuitive to automatically call on that frame. The "correct" solution requires you to input known data: like fps, length, and whatnot. All you need to know with the code below is the frame you want to call.
import numpy as np
import cv2
cap = cv2.VideoCapture(video_name) # video_name is the video being called
cap.set(1,frame_no) # Where frame_no is the frame you want
ret, frame = cap.read() # Read the frame
cv2.imshow('window_name', frame) # show frame on window
If you want to hold the window, until you press exit:
while True:
ch = 0xFF & cv2.waitKey(1) # Wait for a second
if ch == 27:
break
Set a specific frame
From the documentation of the VideoCaptureProperties (docs) is possible to see that the way to set the frame in the VideoCapture is:
frame = 30
cap.set(cv2.CAP_PROP_POS_FRAMES, frame)
Notice that you don't have to pass to the function frame - 1 because, as the documentation says, the flag CAP_PROP_POS_FRAMES rapresent the "0-based index of the frame to be decoded/captured next".
Concluding a full example where i want to read a frame at each second is:
import cv2
cap = cv2.VideoCapture('video.avi')
# Get the frames per second
fps = cap.get(cv2.CAP_PROP_FPS)
# Get the total numer of frames in the video.
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
frame_number = 0
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) # optional
success, image = cap.read()
while success and frame_number <= frame_count:
# do stuff
frame_number += fps
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, image = cap.read()
Set a specific time
In the documentation linked above is possible to see that the way to set a specific time in the VideoCapture is:
milliseconds = 1000
cap.set(cv2.CAP_PROP_POS_MSEC, milliseconds)
And like before a full example that read a frame each second che be achieved in this way:
import cv2
cap = cv2.VideoCapture('video.avi')
# Get the frames per second
fps = cap.get(cv2.CAP_PROP_FPS)
# Get the total numer of frames in the video.
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
# Calculate the duration of the video in seconds
duration = frame_count / fps
second = 0
cap.set(cv2.CAP_PROP_POS_MSEC, second * 1000) # optional
success, image = cap.read()
while success and second <= duration:
# do stuff
second += 1
cap.set(cv2.CAP_PROP_POS_MSEC, second * 1000)
success, image = cap.read()
For example, to start reading 15th frame of the video you can use:
frame = 15
cap.set(cv2.CAP_PROP_POS_FRAMES, frame-1)
In addition, I want to say, that using of CAP_PROP_POS_FRAMES property does not always give you the correct result. Especially when you deal with compressed files like mp4 (H.264).
In my case when I call cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number) for .mp4 file, it returns False, but when I call it for .avi file, it returns True.
Take into consideration when decide using this 'feature'.
very-hit recommends using CV_CAP_PROP_POS_MSEC property.
Read this thread for additional info.

OPENCV: Extract the lifetime for a frame in a video with variable frame rate

I'm using python OpenCV to read frames from a video file that has a variable frame rate.
I need to understand how the wall time changes between frames (i.e. I want to be able to write the timestamp on every frame). My understanding is that the underlying video format stores how long each frame lasts i.e. the video file contains the information to tell the video player how long it should display the given frame before moving to the next one.
I would like to programmatically access this data using the python OpenCV interface. An example (that does not work) of what this might look like is:
import numpy as np
import cv2 as cv
cap = cv.VideoCapture('my_variable_frame_rate_video.mp4')
while cap.isOpened():
ret, frame = cap.read()
frame_length_ms = ret.frame_length() # obviously doesn't work
print("The length of the frame in ms is {}".format(frame_length_ms))
# Should print: The length of the frame in ms is 23
Things that don't work
Reading the source frame rate from the capture device:
fps = cap.get(cv2.cv.CV_CAP_PROP_FPS)
Counting the total number of frames:
fps = cap.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT)/total_length
Both of these don't work because we are dealing with a variable frame rate
An idea could be extract the amount of time and write it in each frame
import cv2
import time
path="video.mp4"
video = cv2.VideoCapture(path);
start = time.time()
ret, frame = video.read()
font = cv2.FONT_HERSHEY_SIMPLEX
i = 0
while ret:
start = time.time()
ret, frame = video.read()
end = time.time()
seconds = end - start
cv2.putText(frame,str(seconds),(10,500), font, 4,(255,255,255),2,cv2.LINE_AA)
cv2.imwrite(str(i)+".jpg", frame)
i+=1
video.release()

OpenCV VideoCapture on Large files looping back to the start frame after 4000+ frames

import matplotlib.pyploy as plt
import cv2
def getFrame(cap):
frameRate = 25
frameId = cap.get(cv2.CAP_PROP_POS_FRAMES)
ret, frame = cap.read()
if not ret:
return None
print frameId
if frameId < 0:
return None
if not (frameId % int(frameRate)):
cap.set(cv2.CAP_PROP_POS_FRAMES, frameId + frameRate)
return frame
return None
videoFile = 'filename.webm'
cap = cv2.VideoCapture(videoFile,cv2.CAP_FFMPEG)
image = getFrame(cap)
plt.imshow(image)
cap.set(cv2.CAP_PROP_POS_FRAMES,4000.0)
image = getFrame(cap)
while(image is not None):
plt.imshow(image)
image = getFrame(cap)
cap.release()
I'm running the above code in a while loop and displaying the image. It seems that after around 4250 frames, the frame returned will be the same as the frame at the start. This loop continue after another 4250 frames.
The file I'm reading is mp4 and webm file. The behavior is the same for both types of file. videoCapture is using FFMPEG backend. Opencv version = 3.4.1, python 2.7 .
Another notable behaviour is the reading of the frame slows down as the frame increases and then went back to fast again after 4250 frames.
Edit:
I edited the code. I think you can try running this with mp4/webm file longer than 5 minutes. I can't upload the video due to privacy reason because it is recorded in office.
When I modified the code with only cap.read(), the error disappears. I suspect this is something to do with cap.set()
Use cap.set(cv2.cv.CV_CAP_PROP_FPS, 25) instead of skipping frames and setting the frame number in code.
Your code does not check the size of the video before setting cv2.CAP_PROP_POS_FRAMES, frameId + frameRate.
You can check the size in frames with cap.get(cv2.CAP_PROP_FRAME_COUNT).
def getNextFrame(cap):
frameRate = 25
for i in range(frameRate-1):
cap.grab()
ret, frame = cap.read()
return ret, frame
I've found a workaround. I use grab() for the 24 frames to be skipped and then only read() for the frame I need.
I'm not sure if this is the best approach but I'll put it out here. I'll still be accepting other answers.

Categories

Resources