【问题标题】:Extract specific frames of youtube video without downloading video在不下载视频的情况下提取 youtube 视频的特定帧
【发布时间】:2021-05-22 03:49:42
【问题描述】:

我需要提取在线视频的特定帧来处理算法,但我不想下载整个视频,因为这样会降低效率。

对于初学者,我尝试使用 youtube 视频。我可以通过这种方式使用youtube-dl 下载整个视频:

ydl_opts = {'outtmpl': r'OUTPUT_DIRECTORY_HERE',}
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
    ydl.download([url])

然后我可以捕获单个帧。

我需要避免下载整个视频。经过一番研究,我发现ffmpeg 可能会帮助我做到这一点。我发现没有办法只下载帧,所以如果这不可能,第二个选择是我可以下载视频的特定部分。 linux 中的一个这样的例子是here,但我找不到任何适用于 python 的解决方案。

什么是只下载帧或部分视频(在 python 中)而不下载整个内容的好方法?

【问题讨论】:

  • @AyeshaKhan 这个答案是下载所有帧。所以我所做的是我跳过了下载其中的一些(比如我下载了视频中的每 300 帧,即我每 10 秒下载一帧以获取 30 fps 的视频)。这似乎有效。虽然,我不确定我是只是跳过在本地保存它们还是实际上跳过捕获它们。不过谢谢!
  • 我已经构建了一个 API 来获取 youtube 视频的帧:rapidapi.com/abyesilyurt/api/youtube-screenshot1/details。它需要一个 http 请求并返回图像。

标签: python opencv ffmpeg video-capture youtube-dl


【解决方案1】:

在当前答案的基础上,可以使用多处理进一步提高性能。例如,如果您想将视频分割成帧并在 num_cpu 个进程中独立处理:

import os
from functools import partial
from multiprocessing.pool import Pool

import cv2
import youtube_dl

def process_video_parallel(url, skip_frames, process_number):
    cap = cv2.VideoCapture(url)
    num_processes = os.cpu_count()
    frames_per_process = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) // num_processes
    cap.set(cv2.CAP_PROP_POS_FRAMES, frames_per_process * process_number)
    x = 0
    count = 0
    while x < 10 and count < frames_per_process:
        ret, frame = cap.read()
        if not ret:
            break
        filename =r"PATH\shot"+str(x)+".png"
        x += 1
        cv2.imwrite(filename.format(count), frame)
        count += skip_frames  # Skip 300 frames i.e. 10 seconds for 30 fps
        cap.set(1, count)
    cap.release()



video_url = "..."  # The Youtube URL
ydl_opts = {}
ydl = youtube_dl.YoutubeDL(ydl_opts)
info_dict = ydl.extract_info(video_url, download=False)

formats = info_dict.get('formats', None)

print("Obtaining frames")
for f in formats:
    if f.get('format_note', None) == '144p':
        url = f.get('url', None)
        cpu_count = os.cpu_count()
        with Pool(cpu_count) as pool:
            pool.map(partial(process_video_parallel, url, 300), range(cpu_count))

就本应用而言,由于只是从视频中保存图像,因此这可能不会带来巨大的改进(可能需要几秒钟),但如果需要在帧上应用其他算法,则可能是有益的。

【讨论】:

    【解决方案2】:

    我尝试了 @AyeshaKhan 在 cmets 中分享的内容。

    导入cv2,numpy,youtube-dl后:

    
        url=saved_url #The Youtube URL
        ydl_opts={}
        ydl=youtube_dl.YoutubeDL(ydl_opts)
        info_dict=ydl.extract_info(video_url, download=False)
    
        formats = info_dict.get('formats',None)
        print("Obtaining frames")
        for f in formats:
            if f.get('format_note',None) == '144p':
                url = f.get('url',None)
                cap = cv2.VideoCapture(url)
                x=0
                count=0
                while x<10:
                    ret, frame = cap.read()
                    if not ret:
                        break
                    filename =r"PATH\shot"+str(x)+".png"
                    x+=1
                    cv2.imwrite(filename.format(count), frame)
                    count+=300 #Skip 300 frames i.e. 10 seconds for 30 fps
                    cap.set(1,count)
                    if cv2.waitKey(30)&0xFF == ord('q'):
                        break
                cap.release()
    

    cmets 中的答案是下载所有帧,因此我在.format() 中添加的count 确保我按照我的要求跳过了帧。

    另外,x 此处将数量限制为 10。

    尽管如此,我仍然不确定此方法是否实际捕获了指定的帧,或者它是否捕获了所有帧并将指定的帧保存到我的本地存储中。我需要前者。

    但这仍然足够快并且对我有用!

    【讨论】:

      猜你喜欢
      • 2013-06-18
      • 2015-01-14
      • 1970-01-01
      • 2011-07-10
      • 2010-11-08
      • 2011-04-30
      • 1970-01-01
      • 1970-01-01
      • 2020-11-02
      相关资源
      最近更新 更多