我试图通过设置CV_CAP_PROP_POS_FRAMES
属性然后像这样读取框架来跳转到特定的框架:
cvSetCaptureProperty( input_video, CV_CAP_PROP_POS_FRAMES, current_frame );
frame = cvQueryFrame( input_video );
我面临的问题是,OpenCV 2.1为current_frame
的12个连续值返回相同的帧,而我想要读取每个单独的帧,而不仅仅是关键帧。谁能告诉我什么错了?
我做了一些研究,发现问题是由解压缩算法引起的。
MPEG类算法(包括HD等)不会单独压缩每一帧,而是不时保存一个关键帧,然后只保存最后一帧和后续帧之间的差异。
您报告的问题是由于,当您选择一个帧时,解码器(可能是ffmpeg)会自动前进到下一个关键帧。
那么,有没有办法解决这个问题?我不只想要关键帧,而是每个帧。
答案 0 :(得分:4)
我不知道这对于你的目的是否足够精确,但我已经成功通过抓取帧速率,将帧数转换为时间来获得MPEG视频中的特定点,然后推进到时间。像这样:
cv::VideoCapture sourceVideo("/some/file/name.mpg");
double frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
答案 1 :(得分:3)
由于OpenCV中的这种限制,使用FFMPEG可能是明智之举。 Moviepy是一个不错的包装库。
# Get nth frame from a video
from moviepy.video.io.ffmpeg_reader import FFMPEG_VideoReader
cap = FFMPEG_VideoReader("movie.mov",True)
cap.initialize()
cap.get_frame(n/FPS)
表现也很棒。使用get_frame
寻找第n帧是O(1),并且如果请求(几乎)连续帧,则使用加速。我已经获得了比同时加载三个720p视频更好的实时结果。
答案 2 :(得分:2)
CV_CAP_PROP_POS_FRAMES跳转到关键帧。我有同样的问题,并使用此(python-)代码解决它。这可能不是完全有效的,但是完成了工作:
def seekTo(cap, position):
positiontoset = position
pos = -1
cap.set(cv.CV_CAP_PROP_POS_FRAMES, position)
while pos < position:
ret, image = cap.read()
pos = cap.get(cv.CV_CAP_PROP_POS_FRAMES)
if pos == position:
return image
elif pos > position:
positiontoset -= 1
cap.set(cv.CV_CAP_PROP_POS_FRAMES, positiontoset)
pos = -1
答案 3 :(得分:1)
我在OpenCV 3 / Python 3上成功使用了以下内容:
# Skip to 150 frame then read the 151th frame
cap.set(cv2.CAP_PROP_POS_FRAMES, 150))
ret, frame = cap.read()
答案 4 :(得分:0)
经过几年的假设,这是一个不可靠的错误,我想我已经找到了一种在速度和正确性之间取得良好平衡的方法。
之前的解决方案建议在阅读框架之前使用CV_CAP_PROP_POS_MSEC
属性:
cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
void readFrame(int frameNumber, cv::Mat& image) {
const double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
sourceVideo.read(image);
}
它会返回预期的帧,但问题是使用CV_CAP_PROP_POS_MSEC
可能非常慢,例如视频转换。
注意:为简单起见,使用全局变量。
另一方面,如果你只想按顺序阅读视频,那么在不寻求的情况下读取帧就足够了。
for (int frameNumber = 0; frameNumber < nFrames; ++frameNumber) {
sourceVideo.read(image);
}
解决方案来自两者的结合:使用变量来记住最后查询的帧lastFrameNumber
,并且仅在请求的帧不是下一帧时进行搜索。通过这种方式,可以在连续读取时提高速度,同时在必要时允许随机搜索。
cv::VideoCapture sourceVideo("/some/file/name.mpg");
const auto frameRate = sourceVideo.get(CV_CAP_PROP_FPS);
const int lastFrameNumber = -2; // guarantee seeking the first time
void readFrame(int frameNumber, cv::Mat& image) {
if (lastFrameNumber + 1 != frameNumber) { // not the next frame? seek
const double frameTime = 1000.0 * frameNumber / frameRate;
sourceVideo.set(CV_CAP_PROP_POS_MSEC, frameTime);
}
sourceVideo.read(image);
lastFrameNumber = frameNumber;
}