cv2.VideoCapture cap.set 和循环读取之间的不一致行为
cv2.VideoCapture inconsistent behavior between cap.set and loop read
我正在尝试使用 cv2.VideoCapture
读取视频的第 600 帧。但是,我发现下面两种方法都成功读取了一张图片,但是图片不一样。我想知道读取第 600 帧的正确方法是什么,为什么生成的图像不同?与mp4编码有关吗?谢谢!
方法一
cap = cv2.VideoCapture("test.mp4")
print(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 1187
cap.set(1, 600)
ret, frame1 = cap.read() # Read the frame
方法二
cap = cv2.VideoCapture("test.mp4")
print(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 1187
for i in range(601):
ret, frame2 = cap.read() # Read the frame
到read/obtain一个视频的第X
帧或者类似的判断一个视频文件的帧数,有两种方法:
- 方法 #1:利用 built-in OpenCV 属性访问视频文件元信息
快速高效但不准确
- 方法 #2:使用计数器手动循环视频文件中的每一帧,该计数器缓慢且低效但准确
方法#1 速度很快,并且依赖于 OpenCV 的 video property functionality,它几乎可以瞬间确定视频文件中的帧信息。但是,有一个准确度 trade-off,因为它取决于您的 OpenCV 和视频编解码器版本。来自文档:
Reading / writing properties involves many layers. Some unexpected result might happen along this chain. Effective behavior depends from device hardware, driver and API Backend.
另一方面,手动计算每一帧直到我们达到所需的帧数将是 100% 准确的,尽管它会慢得多。下面是演示两种方法之间不一致行为的示例。它默认尝试执行方法#1,如果失败,它将自动使用方法#2
def frame_count(video_path, manual=False):
def manual_count(handler):
frames = 0
while True:
status, frame = handler.read()
if not status:
break
frames += 1
return frames
cap = cv2.VideoCapture(video_path)
# Slow, inefficient but 100% accurate method
if manual:
frames = manual_count(cap)
# Fast, efficient but inaccurate method
else:
try:
frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frames = manual_count(cap)
cap.release()
return frames
基准
if __name__ == '__main__':
import timeit
import cv2
start = timeit.default_timer()
print('frames:', frame_count('testtest.mp4', manual=False))
print(timeit.default_timer() - start, '(s)')
start = timeit.default_timer()
print('frames:', frame_count('testtest.mp4', manual=True))
print(timeit.default_timer() - start, '(s)')
方法#1 结果
frames: 3671
0.018054921 (s)
方法 #2 结果
frames: 3521
9.447095287 (s)
请注意这两种方法有 150 帧的差异,并且 方法 #2 比方法 #1 慢得多。一般来说,如果您需要速度但又愿意牺牲准确性,请使用方法#1。在您可以延迟但需要精确帧的情况下,请使用方法 #2。
所以结论是:当您使用 cap.get
或任何内置 VideoCaptureProperties(例如 cv2.CAP_PROP_FRAME_COUNT
时,您实质上是在使用方法 #1,该方法快速高效但不准确.在您的第一个示例中,当您尝试使用 cap.set
读取精确帧时,您实际上得到的是接近所需 X
帧的“估计”帧,而不是实际的 X
框架。
相比之下,从您的第二个代码片段中,您手动逐帧检查每一帧,因此当它落在第 X
帧时,保证是准确的。这就是为什么当您尝试使用每种方法读取相同的帧编号时,您可能会得到不同的图像。
我正在尝试使用 cv2.VideoCapture
读取视频的第 600 帧。但是,我发现下面两种方法都成功读取了一张图片,但是图片不一样。我想知道读取第 600 帧的正确方法是什么,为什么生成的图像不同?与mp4编码有关吗?谢谢!
方法一
cap = cv2.VideoCapture("test.mp4")
print(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 1187
cap.set(1, 600)
ret, frame1 = cap.read() # Read the frame
方法二
cap = cv2.VideoCapture("test.mp4")
print(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 1187
for i in range(601):
ret, frame2 = cap.read() # Read the frame
到read/obtain一个视频的第X
帧或者类似的判断一个视频文件的帧数,有两种方法:
- 方法 #1:利用 built-in OpenCV 属性访问视频文件元信息 快速高效但不准确
- 方法 #2:使用计数器手动循环视频文件中的每一帧,该计数器缓慢且低效但准确
方法#1 速度很快,并且依赖于 OpenCV 的 video property functionality,它几乎可以瞬间确定视频文件中的帧信息。但是,有一个准确度 trade-off,因为它取决于您的 OpenCV 和视频编解码器版本。来自文档:
Reading / writing properties involves many layers. Some unexpected result might happen along this chain. Effective behavior depends from device hardware, driver and API Backend.
另一方面,手动计算每一帧直到我们达到所需的帧数将是 100% 准确的,尽管它会慢得多。下面是演示两种方法之间不一致行为的示例。它默认尝试执行方法#1,如果失败,它将自动使用方法#2
def frame_count(video_path, manual=False):
def manual_count(handler):
frames = 0
while True:
status, frame = handler.read()
if not status:
break
frames += 1
return frames
cap = cv2.VideoCapture(video_path)
# Slow, inefficient but 100% accurate method
if manual:
frames = manual_count(cap)
# Fast, efficient but inaccurate method
else:
try:
frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
except:
frames = manual_count(cap)
cap.release()
return frames
基准
if __name__ == '__main__':
import timeit
import cv2
start = timeit.default_timer()
print('frames:', frame_count('testtest.mp4', manual=False))
print(timeit.default_timer() - start, '(s)')
start = timeit.default_timer()
print('frames:', frame_count('testtest.mp4', manual=True))
print(timeit.default_timer() - start, '(s)')
方法#1 结果
frames: 3671
0.018054921 (s)
方法 #2 结果
frames: 3521
9.447095287 (s)
请注意这两种方法有 150 帧的差异,并且 方法 #2 比方法 #1 慢得多。一般来说,如果您需要速度但又愿意牺牲准确性,请使用方法#1。在您可以延迟但需要精确帧的情况下,请使用方法 #2。
所以结论是:当您使用 cap.get
或任何内置 VideoCaptureProperties(例如 cv2.CAP_PROP_FRAME_COUNT
时,您实质上是在使用方法 #1,该方法快速高效但不准确.在您的第一个示例中,当您尝试使用 cap.set
读取精确帧时,您实际上得到的是接近所需 X
帧的“估计”帧,而不是实际的 X
框架。
相比之下,从您的第二个代码片段中,您手动逐帧检查每一帧,因此当它落在第 X
帧时,保证是准确的。这就是为什么当您尝试使用每种方法读取相同的帧编号时,您可能会得到不同的图像。