opencv 从视频文件保存帧的算法

ocebsuys  于 2023-03-30  发布在  其他
关注(0)|答案(2)|浏览(164)

是否可以从视频文件中保存所需数量的图像(DESIRED_FRAMES_TO_SAVE),并在整个视频文件素材中均匀分布?
希望这是有意义的,我有一个视频文件,是8秒的长度,我想保存60帧的视频文件的顺序。
尝试扔在一起的东西在打开CV的代码工作,但我知道它应该得到改进。例如,if count % 3 == 0:我从计算的视频文件的总帧是223,然后除以DESIRED_FRAMES_TO_SAVE60我想出约3.72...所以换句话说,我认为每3或4帧,我应该保存一个来提出~ DESIRED_FRAMES_TO_SAVE的视频文件的结尾。对不起,这是一个奇怪的问题,但有人有任何建议,如何重写这个更好地没有到while循环?

import cv2

VIDEO_FILE = "hello.mp4"
DESIRED_FRAMES_TO_SAVE = 60

count = 0
cap = cv2.VideoCapture(VIDEO_FILE)

'''
# calculate total frames
while True:
  success, frame = cap.read()
  if success == True:
    count += 1

  else:
      break

total_frames = count
print("TOTAL_FRAMES",total_frames)
'''

count = 0

while True:
    success, frame = cap.read()
    name = './raw_images/' + str(count) + '.jpg'

    if success == True:

        if count % 3 == 0:
            cv2.imwrite(name, frame)
            print(count)

        elif count > DESIRED_FRAMES_TO_SAVE:
            break

        count += 1

    else:
        break
      
cap.release() 
cv2.destroyAllWindows()
yyhrrdl8

yyhrrdl81#

OpenCV是为计算机视觉设计的。它可以读取视频文件,但它不是为这个而设计的。它有真实的的局限性。它给你的是“帧有索引”这种破碎的抽象。
你***必须***为***可变帧速率***做好准备,这是一个真实的东西。许多视频文件不通过“索引”而是通过***时间戳***来跟踪帧。视频文件也可能不知道它们包含多少帧。任何帧计数只是持续时间(这是真实的)乘以 * 平均 * 帧速率(这是猜测)。
最可靠的解决方案是读取整个视频,查看每个帧的时间戳,然后决定是否保留它。这只需要一点数学和一些计数。伪代码:

# basic definitions

vid = cv.VideoCapture(...)
fps = vid.get(cv.CAP_PROP_FPS)
duration_msec = vid.get(cv.CAP_PROP_FRAME_COUNT) / fps * 1000

frames_to_save = 60

indices = range(frames_to_save)
timestamps_msec = [k / frames_to_save * duration_msec for k in indices]
# any monotonically increasing list, values from 0 .. duration_msec
# either:

frames_saved = 0
while frames_saved < frames_to_save:
    (ok, frame) = vid.read()
    if not ok: break
    timestamp_msec = vid.get(cv.CAP_PROP_POS_MSEC)
    scheduled_msec = timestamps_msec[frames_saved] # which one do we wait for?
    if timestamp_msec >= scheduled_msec:
        # ...save the frame...
        frames_saved += 1

如果你想赌博,你可以试着“寻找”。首先你计算你想保留的理想时间戳,然后你“寻找”到那些位置。

# or:

for (k, ts_msec) in zip(indices, timestamps_msec):
    vid.set(cv.CAP_PROP_POS_MSEC, ts_msec)
    (ok, frame) = vid.read()
    if not ok: break
    # ...save frame... (you can use k for a number)

一般来说,搜索(不仅仅是OpenCV)有两种操作模式。在任何情况下,库都会告诉你“确切”的位置,所以它不是“不精确”(对于一些“不精确”的概念)。

  • 它要么把你放在最接近的 * 可能的 * 帧(OpenCV的选择),即一个帧或它的直接邻居,这是昂贵的,因为它必须解码许多(但不一定是所有)帧才能到达目标。
  • 或者它会让你在你真正想去的地方附近/前面的某个关键帧处下车,剩下的路你必须步行。

CAP_PROP_POS_FRAMES可能是谎言。它只需要时间戳,然后除以平均帧率。时间戳是视频中唯一的真相。
即使CAP_PROP_FRAME_COUNT也可能是一个谎言。它是从真实的持续时间和平均帧速率计算出来的。遗憾的是,OpenCV并没有公开视频持续时间,但它是OpenCV内部的一个基本值。取帧数,然后乘以(平均)帧速率,得到持续时间。
所有这些都假设使用ffmpeg后端,这是OpenCV中视频文件最常见(和默认)的选择。其他一些专业后端,如内置的AVI+MJPEG后端,* 确实 * 有全部信息,由于文件格式。

e0bqpujr

e0bqpujr2#

完整的代码也需要看一下ffmeg:

import numpy as np
import cv2

VIDEO_FILE = "hello.mp4"
DESIRED_FRAMES_TO_SAVE = 60

count = 0
cap = cv2.VideoCapture(VIDEO_FILE)

length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
print("FRAMES TOTAL LEN: ", length)

fps = cap.get(cv2.CAP_PROP_FPS)
print("FPS: ", fps)

duration_msec = cap.get(cv2.CAP_PROP_FRAME_COUNT) / fps * 1000
print("DURATION M SECONDS: ", duration_msec)

indices = range(DESIRED_FRAMES_TO_SAVE)
timestamps_msec = [k / DESIRED_FRAMES_TO_SAVE * duration_msec for k in indices]
print("TIMESTAMPTS M SECONDS: ", timestamps_msec)

frames_saved = 0
while frames_saved < DESIRED_FRAMES_TO_SAVE:
    name = f'./raw_images/{frames_saved}_.jpg'
    (ok, frame) = cap.read()
    if not ok:
        break

    timestamp_msec = cap.get(cv2.CAP_PROP_POS_MSEC)
    scheduled_msec = timestamps_msec[frames_saved]
    if timestamp_msec >= scheduled_msec:
        cv2.imwrite(name, frame)
        print("Processing ", frames_saved)
        frames_saved += 1

cap.release()
cv2.destroyAllWindows()

相关问题