【发布时间】:2017-11-13 07:00:51
【问题描述】:
(我将在此问题符合条件时立即为其提供 500 声望奖励 - 除非该问题已关闭。)
一句话的问题
从VideoCapture 读取帧比预期的要快得多。
说明
我需要在特定时间间隔之间读取和分析 100 fps(根据 cv2 和 VLC 媒体播放器)视频的帧。在下面的最小示例中,我试图读取三分钟视频的前十秒的所有帧。
我正在创建一个cv2.VideoCapture 对象,我从中读取帧,直到达到所需的毫秒位置。在我的实际代码中,每一帧都会被分析,但为了展示错误,这一事实是无关紧要的。
在读取帧后检查 VideoCapture 的当前帧和毫秒位置会产生正确的值,因此 VideoCapture 认为它位于正确的位置 - 但事实并非如此。保存最后一个读取帧的图像表明我的迭代严重超过了目标时间超过两分钟。
更奇怪的是,如果我手动将捕获的毫秒位置VideoCapture.set 设置为 10 秒(读取帧后返回相同的值VideoCapture.get)并保存图像,视频在(几乎) 正确的位置!
演示视频文件
如果您想运行 MCVE,您需要 demo.avi 视频文件。 可以HERE下载。
MCVE
这个 MCVE 是经过精心设计和评论的。如果有任何不清楚的地方,请在问题下方发表评论。
如果您使用的是 OpenCV 3,则必须将所有 cv2.cv.CV_ 实例替换为 cv2.。 (对我来说,这两个版本都会出现问题。)
import cv2
# set up capture and print properties
print 'cv2 version = {}'.format(cv2.__version__)
cap = cv2.VideoCapture('demo.avi')
fps = cap.get(cv2.cv.CV_CAP_PROP_FPS)
pos_msec = cap.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
pos_frames = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
print ('initial attributes: fps = {}, pos_msec = {}, pos_frames = {}'
.format(fps, pos_msec, pos_frames))
# get first frame and save as picture
_, frame = cap.read()
cv2.imwrite('first_frame.png', frame)
# advance 10 seconds, that's 100*10 = 1000 frames at 100 fps
for _ in range(1000):
_, frame = cap.read()
# in the actual code, the frame is now analyzed
# save a picture of the current frame
cv2.imwrite('after_iteration.png', frame)
# print properties after iteration
pos_msec = cap.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
pos_frames = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
print ('attributes after iteration: pos_msec = {}, pos_frames = {}'
.format(pos_msec, pos_frames))
# assert that the capture (thinks it) is where it is supposed to be
# (assertions succeed)
assert pos_frames == 1000 + 1 # (+1: iteration started with second frame)
assert pos_msec == 10000 + 10
# manually set the capture to msec position 10010
# note that this should change absolutely nothing in theory
cap.set(cv2.cv.CV_CAP_PROP_POS_MSEC, 10010)
# print properties again to be extra sure
pos_msec = cap.get(cv2.cv.CV_CAP_PROP_POS_MSEC)
pos_frames = cap.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
print ('attributes after setting msec pos manually: pos_msec = {}, pos_frames = {}'
.format(pos_msec, pos_frames))
# save a picture of the next frame, should show the same clock as
# previously taken image - but does not
_, frame = cap.read()
cv2.imwrite('after_setting.png', frame)
MCVE 输出
print 语句产生以下输出。
cv2 版本 = 2.4.9.1
初始属性:fps = 100.0, pos_msec = 0.0, pos_frames = 0.0
读后属性:pos_msec = 10010.0, pos_frames = 1001.0
手动设置毫秒 pos 后的属性:pos_msec = 10010.0, pos_frames = 1001.0
如您所见,所有属性都有预期值。
imwrite保存以下图片。
您可以在第二张图片中看到问题。 9:26:15 的目标(图中的实时时钟)错过了两分钟多。手动设置目标时间(第三张图片)将视频设置到(几乎)正确的位置。
我做错了什么,我该如何解决?
目前尝试过
cv2 2.4.9.1 @ Ubuntu 16.04
cv2 2.4.13@Scientific Linux 7.3(三台电脑)
cv2 3.1.0 @ Scientific Linux 7.3(三台电脑)
使用
创建捕获cap = cv2.VideoCapture('demo.avi', apiPreference=cv2.CAP_FFMPEG)
和
cap = cv2.VideoCapture('demo.avi', apiPreference=cv2.CAP_GSTREAMER)
在 OpenCV 3 中(版本 2 似乎没有 apiPreference 参数)。
使用cv2.CAP_GSTREAMER 需要很长时间(运行 MCVE 大约需要 2-3 分钟),但两种 api-preferences 都会产生相同的错误图像。
当直接使用ffmpeg 读取帧时(归功于this 教程),会生成正确的输出图像。
import numpy as np
import subprocess as sp
import pylab
# video properties
path = './demo.avi'
resolution = (593, 792)
framesize = resolution[0]*resolution[1]*3
# set up pipe
FFMPEG_BIN = "ffmpeg"
command = [FFMPEG_BIN,
'-i', path,
'-f', 'image2pipe',
'-pix_fmt', 'rgb24',
'-vcodec', 'rawvideo', '-']
pipe = sp.Popen(command, stdout = sp.PIPE, bufsize=10**8)
# read first frame and save as image
raw_image = pipe.stdout.read(framesize)
image = np.fromstring(raw_image, dtype='uint8')
image = image.reshape(resolution[0], resolution[1], 3)
pylab.imshow(image)
pylab.savefig('first_frame_ffmpeg_only.png')
pipe.stdout.flush()
# forward 1000 frames
for _ in range(1000):
raw_image = pipe.stdout.read(framesize)
pipe.stdout.flush()
# save frame 1001
image = np.fromstring(raw_image, dtype='uint8')
image = image.reshape(resolution[0], resolution[1], 3)
pylab.imshow(image)
pylab.savefig('frame_1001_ffmpeg_only.png')
pipe.terminate()
这会产生正确的结果! (正确的时间戳 9:26:15)
其他信息
在 cmets 中,我被要求提供我的 cvconfig.h 文件。我似乎只有/opt/opencv/3.1.0/include/opencv2/cvconfig.h 下的 cv2 版本 3.1.0 的这个文件。
HERE 是此文件的粘贴。
如果有帮助,我可以使用VideoCapture.get 提取以下视频信息。
亮度0.0
对比度 0.0
转换_rgb 0.0
曝光 0.0
格式 0.0
四cc 1684633187.0
fps 100.0
帧数 18000.0
框架高度 593.0
frame_width 792.0
增益 0.0
色调 0.0
模式 0.0
openni_baseline 0.0
openni_focal_length 0.0
openni_frame_max_depth 0.0
openni_output_mode 0.0
openni_registration 0.0
pos_avi_ratio 0.01
pos_frames 0.0
pos_msec 0.0
整改0.0
饱和度 0.0
【问题讨论】:
-
什么平台?为什么要使用这么旧版本的 OpenCV?这太奇怪了,我根本不希望文件中的
read()跳过帧...... -
顺便说一句,我已经在 Win10 上使用 Python 2.7.5 和 OpenCV 2.4.11 进行了本地尝试,两个 after_ 图像都显示了
9:26:15的时间戳。有趣的是,即使您的after_setting.png也比所需时间晚了 2 秒。这可能是 OpenCV 中的错误,或者更有可能是您使用的任何库来解码 AVI 文件... -
@timgeb 你用
ffmpeg库编译OpenCV 了吗?您可以尝试直接使用ffmpeg(tutorial here) 读取帧以查看是否有类似的跳过,并且您可以指定使用 OpenCV 中的ffmpeg库为VideoCapture和cv2.VideoCapture('demo.avi', apiPreference=cv2.CAP_FFMPEG)。试一试并报告! -
@timgeb 我可能会尝试重新编译 OpenCV 并确保使用它构建
ffmpeg。除此之外,我在这里没有我的专业知识。希望你能得到一个可行的解决方案,这绝对是一个奇怪的问题。
标签: python opencv ubuntu video video-processing