I am using opencv to read frames from a video output from a Vivotek Camera using the mpeg compression. I am trying to use the function to start the video from a particular position., shown below where start is the number of frames I want to skip.
inputVideo.set(CV_CAP_PROP_POS_FRAMES, start);
However I am having a problem with this as the incorrect frame is being captured which happens before the frame at start.
I am using opencv version 2.4.2
Can someone please help with this issue?
If you're willing to upgrade to OpenCV 3.0 the following 2 use cases may be help you. This may work for earlier versions, but I haven't tried. If you want it to capture all the frames between frame a and frame b, refer to the 2nd use case and replace "desired_frames" with:
desired_frames = range(a,b)
import cv2
import math
import numpy as np
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success,image = vidcap.read()
#################### Setting up parameters ################
seconds = 5
fps = vidcap.get(cv2.CAP_PROP_FPS) # Gets the frames per second
multiplier = fps * seconds
#################### Initiate Process ################
while success:
frameId = int(round(vidcap.get(1))) #current frame number, rounded b/c sometimes you get frame intervals which aren't integers...this adds a little imprecision but is likely good enough
success, image = vidcap.read()
if frameId % multiplier == 0:
cv2.imwrite("FolderSeconds/frame%d.jpg" % frameId, image)
vidcap.release()
print "Complete"
#################### Setting up the file ################
videoFile = "Jumanji.mp4"
vidcap = cv2.VideoCapture(videoFile)
success,image = vidcap.read()
#################### Setting up parameters ################
#OpenCV is notorious for not being able to good to
# predict how many frames are in a video. The point here is just to
# populate the "desired_frames" list for all the individual frames
# you'd like to capture.
fps = vidcap.get(cv2.CAP_PROP_FPS)
est_video_length_minutes = 3 # Round up if not sure.
est_tot_frames = est_video_length_minutes * 60 * fps # Sets an upper bound # of frames in video clip
n = 5 # Desired interval of frames to include
desired_frames = n * np.arange(est_tot_frames)
#################### Initiate Process ################
for i in desired_frames:
vidcap.set(1,i-1)
success,image = vidcap.read(1) # image is an array of array of [R,G,B] values
frameId = vidcap.get(1) # The 0th frame is often a throw-away
cv2.imwrite("FolderFrames/frame%d.jpg" % frameId, image)
vidcap.release()
print "Complete"
That's pretty much it.
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
major_ver
A bit too late but searching on same topic (not specific to Vivotek Camera but more on mpeg problem with openCV) :
see similar question :
Getting individual frames using CV_CAP_PROP_POS_FRAMES in cvSetCaptureProperty
How can I get one single frame from a video file?
Problem with CV_CAP_PROP_POS_FRAMES setting next frame number
desired position key frame (this is where the cursor will stop)
| | | |
| > | |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Sample code using openCV 2.4.8 / VS2013 with trackbar :
tested with MPG format [MPEG1/MPEG2] : setting frame position works fine
double currentPos = capture.get(CV_CAP_PROP_POS_FRAMES);
std::cout << "CV_CAP_PROP_POS_FRAMES = " << currentPos << std::endl;
// position_slider 0 - 100
double noFrame = position_slider*nbFrames / 100;
// solution 1
bool success = capture.set(CV_CAP_PROP_POS_FRAMES, noFrame);
// solution 2
double frameRate = capture.get(CV_CAP_PROP_FPS);
double frameTime = 1000.0 * noFrame / frameRate;
bool success = capture.set(CV_CAP_PROP_POS_MSEC, frameTime);
if (!success) {
std::cout << "Cannot set frame position from video file at " << noFrame << std::endl;
return;
}
currentPos = capture.get(CV_CAP_PROP_POS_FRAMES);
if (currentPos != noFrame) {
std::cout << "Requesting frame " << noFrame << " but current position == " << currentPos << std::endl;
}
success = capture.read(frame_aux);
if (!success) {
std::cout << "Cannot get frame from video file " << std::endl;
return;
}
imshow("test", frame_aux);
Just as BlouBlou said, when you read some high compression video format with FFMPEG, it used "key frame" technology to decode video. So set CV_CAP_PROP_POS_FRAMES
property will not work as you want.
I tested 5 mpeg videos to read all frames from them. Get total frame count using long totalFrameNumber = capture.get(CV_CAP_PROP_FRAME_COUNT);
Then I read each video twice, first time not set CV_CAP_PROP_POS_FRAMES
to 0, second time set it.
It did drop some frames when you set CV_CAP_PROP_POS_FRAMES
to 0, about 10 frames each video. I guess because the key frame is not in the first frame of the video, so FFMPEG skip some frames to the first key frame. But some video format such as .avi won't appear such problem. I hope my real experience will save other people much time (well I spent to find this page not too long but enough).
Anyway, highly recommend not to using CV_CAP_PROP_POS_FRAMES
to get a specific frame when you face mpeg format video. It is a smart choice to set CV_CAP_PROP_POS_MSEC
:)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With