【问题标题】:Python - Extracting and Saving Video FramesPython - 提取和保存视频帧
【发布时间】:2016-01-23 12:14:48
【问题描述】:

所以我关注了this tutorial,但它似乎没有做任何事情。简直什么都没有。它等待几秒钟并关闭程序。这段代码有什么问题?

import cv2
vidcap = cv2.VideoCapture('Compton.mp4')
success,image = vidcap.read()
count = 0
success = True
while success:
  success,image = vidcap.read()
  cv2.imwrite("frame%d.jpg" % count, image)     # save frame as JPEG file
  if cv2.waitKey(10) == 27:                     # exit if Escape is hit
      break
  count += 1

另外,在 cmets 中它说这将帧数限制为 1000?为什么?

编辑: 我尝试先做success = True,但这并没有帮助。它只创建了一个 0 字节的图像。

【问题讨论】:

  • success 的值是多少?
  • 什么是type 可能是布尔值,但它是True 还是False
  • 是的,但是你的值是多少?在这种情况下,您的程序可能只是“等待几秒钟并关闭”,这可能是错误的。换句话说,在某处添加print success
  • 强制success没有意义;如果它是假的,那么这意味着视频读取由于某种原因而失败。你需要先让那一点工作。
  • 您的 readh 失败了。您是否按照教程中的说明使用 python 和 ffmpeg 构建了 opencv? brew install opencv --python27 --ffmpeg 如果您使用的是不同版本的 Python,则需要将其更改为您的版本。

标签: python python-2.7 opencv


【解决方案1】:

here 下载这个video,这样我们就有了相同的视频文件进行测试。确保该 mp4 文件位于 python 代码的同一目录中。然后还要确保从同一目录运行 python 解释器。

然后修改代码,抛弃waitKey,这很浪费时间,而且没有窗口它无法捕获键盘事件。我们还打印success 值以确保它成功读取帧。

import cv2
vidcap = cv2.VideoCapture('big_buck_bunny_720p_5mb.mp4')
success,image = vidcap.read()
count = 0
while success:
  cv2.imwrite("frame%d.jpg" % count, image)     # save frame as JPEG file      
  success,image = vidcap.read()
  print('Read a new frame: ', success)
  count += 1

怎么样?

【讨论】:

  • 这会保存一个空的jpeg文件,并返回Read a new frame: False
  • 这意味着opencv无法读取视频。它很可能无法访问 ffmpeg。你用的是什么操作系统?
  • 谷歌你的特定版本的opencv的说明,并准确地遵循如何让ffmpeg和opencv-python在Windows上工作。
  • 所以我使用this question 来解决我的兼容性问题。我不得不将 DLL 重命名为 opencv_ffmpeg300.dll(因为 OpenCV2 的 Python 安装是 3.0.0)。我将它放入我的 Python 目录 (C:\Python27)。我不需要安装 ffmpeg 和 opencv 的 windows 版本,但我确实需要 OpenCV 附带的 DLL,但之后我删除了 OpenCV 的其余部分。无论如何,我会选择这个作为答案,但是任何阅读这个的人都必须知道这个 ESSENTIAL DLL。
【解决方案2】:

如果有人不想提取每一帧但想每隔一秒提取一帧,就稍微不同的情况扩展这个问题(& 由@user2700065 回答)。因此,1 分钟的视频将提供 60 帧(图像)。

import sys
import argparse

import cv2
print(cv2.__version__)

def extractImages(pathIn, pathOut):
    count = 0
    vidcap = cv2.VideoCapture(pathIn)
    success,image = vidcap.read()
    success = True
    while success:
        vidcap.set(cv2.CAP_PROP_POS_MSEC,(count*1000))    # added this line 
        success,image = vidcap.read()
        print ('Read a new frame: ', success)
        cv2.imwrite( pathOut + "\\frame%d.jpg" % count, image)     # save frame as JPEG file
        count = count + 1

if __name__=="__main__":
    a = argparse.ArgumentParser()
    a.add_argument("--pathIn", help="path to video")
    a.add_argument("--pathOut", help="path to images")
    args = a.parse_args()
    print(args)
    extractImages(args.pathIn, args.pathOut)

【讨论】:

  • 我使用的是opencv-2.4.9,所以我必须使用cv2.cv.CAP_PROP_POS_MSEC而不是cv2.CAP_PROP_POS_MSEC
  • 如果我想每隔 5 秒显示帧,如何更改代码?
  • @SoumyaBoral count = count + 5
  • @BhushanBabar 不应该在循环开头有一个cv2.imwrite(),因为您在循环之前调用了cv2.imread()
  • @mLstudent33 抱歉,没听懂,请详细说明。
【解决方案3】:

这是一个函数,它将大多数视频格式转换为视频中的帧数。它适用于 Python3OpenCV 3+

import cv2
import time
import os

def video_to_frames(input_loc, output_loc):
    """Function to extract frames from input video file
    and save them as separate frames in an output directory.
    Args:
        input_loc: Input video file.
        output_loc: Output directory to save the frames.
    Returns:
        None
    """
    try:
        os.mkdir(output_loc)
    except OSError:
        pass
    # Log the time
    time_start = time.time()
    # Start capturing the feed
    cap = cv2.VideoCapture(input_loc)
    # Find the number of frames
    video_length = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) - 1
    print ("Number of frames: ", video_length)
    count = 0
    print ("Converting video..\n")
    # Start converting the video
    while cap.isOpened():
        # Extract the frame
        ret, frame = cap.read()
        if not ret:
            continue
        # Write the results back to output location.
        cv2.imwrite(output_loc + "/%#05d.jpg" % (count+1), frame)
        count = count + 1
        # If there are no more frames left
        if (count > (video_length-1)):
            # Log the time again
            time_end = time.time()
            # Release the feed
            cap.release()
            # Print stats
            print ("Done extracting frames.\n%d frames extracted" % count)
            print ("It took %d seconds forconversion." % (time_end-time_start))
            break

if __name__=="__main__":

    input_loc = '/path/to/video/00009.MTS'
    output_loc = '/path/to/output/frames/'
    video_to_frames(input_loc, output_loc)

它支持.mts.mp4.avi 等普通文件。对.mts 文件进行了尝试和测试。像魅力一样工作。

【讨论】:

  • 只需添加if not ret: continue 即可跳过格式错误的帧(如果存在)!
【解决方案4】:

这是@GShocked 对python 3.x 的先前答案的调整,我会将其发布到评论中,但没有足够的声誉

import sys
import argparse

import cv2
print(cv2.__version__)

def extractImages(pathIn, pathOut):
    vidcap = cv2.VideoCapture(pathIn)
    success,image = vidcap.read()
    count = 0
    success = True
    while success:
      success,image = vidcap.read()
      print ('Read a new frame: ', success)
      cv2.imwrite( pathOut + "\\frame%d.jpg" % count, image)     # save frame as JPEG file
      count += 1

if __name__=="__main__":
    print("aba")
    a = argparse.ArgumentParser()
    a.add_argument("--pathIn", help="path to video")
    a.add_argument("--pathOut", help="path to images")
    args = a.parse_args()
    print(args)
    extractImages(args.pathIn, args.pathOut)

【讨论】:

    【解决方案5】:

    在对如何将帧转换为视频进行了大量研究后,我创建了此功能,希望对您有所帮助。为此,我们需要 opencv:

    import cv2
    import numpy as np
    import os
    
    def frames_to_video(inputpath,outputpath,fps):
       image_array = []
       files = [f for f in os.listdir(inputpath) if isfile(join(inputpath, f))]
       files.sort(key = lambda x: int(x[5:-4]))
       for i in range(len(files)):
           img = cv2.imread(inputpath + files[i])
           size =  (img.shape[1],img.shape[0])
           img = cv2.resize(img,size)
           image_array.append(img)
       fourcc = cv2.VideoWriter_fourcc('D', 'I', 'V', 'X')
       out = cv2.VideoWriter(outputpath,fourcc, fps, size)
       for i in range(len(image_array)):
           out.write(image_array[i])
       out.release()
    
    
    inputpath = 'folder path'
    outpath =  'video file path/video.mp4'
    fps = 29
    frames_to_video(inputpath,outpath,fps)
    

    根据自己的本地位置改变fps(frames per second)、输入文件夹路径和输出文件夹路径的值

    【讨论】:

    • files.sort(key = lambda x: int(x[5:-4])) 添加上面的行这有助于根据数字而不是字符串对帧进行排序,例如:最初是 frame1 .jpg 后面是 frame10.jpg 而不是 frame2.jpg ,上面的行根据其中的数字对文件进行排序。
    • 问题是从视频到帧
    【解决方案6】:

    之前的答案丢失了第一帧。将图像存储在文件夹中会很好。

    # create a folder to store extracted images
    import os
    folder = 'test'  
    os.mkdir(folder)
    # use opencv to do the job
    import cv2
    print(cv2.__version__)  # my version is 3.1.0
    vidcap = cv2.VideoCapture('test_video.mp4')
    count = 0
    while True:
        success,image = vidcap.read()
        if not success:
            break
        cv2.imwrite(os.path.join(folder,"frame{:d}.jpg".format(count)), image)     # save frame as JPEG file
        count += 1
    print("{} images are extacted in {}.".format(count,folder))
    

    顺便说一句,您可以通过 VLC 检查帧率e。转到 windows -> 媒体信息 -> 编解码器详细信息

    【讨论】:

    • 提取时有没有办法提高帧率?
    • 没有。制作视频时,帧速率是固定的。你不能提取更多。
    • 多么了不起的答案。非常适合我。有没有办法调整代码中的循环,以便我只能从某个范围内获取帧,比如第 120 到 160 帧?谢谢!
    • 您可以使用变量count来指定要提取的帧数。
    • 如果我想从视频中提取 15 帧从视频开始到视频结束的时间等距,我该怎么办?我发现我必须使用cv.CAP_PROP_POS_AVI_RATIO,但我不知道如何使用。 tnx
    【解决方案7】:

    此代码从视频中提取帧并将帧保存为 .jpg 格式

    import cv2
    import numpy as np
    import os
    
    # set video file path of input video with name and extension
    vid = cv2.VideoCapture('VideoPath')
    
    
    if not os.path.exists('images'):
        os.makedirs('images')
    
    #for frame identity
    index = 0
    while(True):
        # Extract images
        ret, frame = vid.read()
        # end of frames
        if not ret: 
            break
        # Saves images
        name = './images/frame' + str(index) + '.jpg'
        print ('Creating...' + name)
        cv2.imwrite(name, frame)
    
        # next frame
        index += 1
    

    【讨论】:

      【解决方案8】:

      以下脚本将每半秒从文件夹中的所有视频中提取帧。 (适用于 python 3.7)

      import cv2
      import os
      listing = os.listdir(r'D:/Images/AllVideos')
      count=1
      for vid in listing:
          vid = r"D:/Images/AllVideos/"+vid
          vidcap = cv2.VideoCapture(vid)
          def getFrame(sec):
              vidcap.set(cv2.CAP_PROP_POS_MSEC,sec*1000)
              hasFrames,image = vidcap.read()
              if hasFrames:
                  cv2.imwrite("D:/Images/Frames/image"+str(count)+".jpg", image) # Save frame as JPG file
              return hasFrames
          sec = 0
          frameRate = 0.5 # Change this number to 1 for each 1 second
          
          success = getFrame(sec)
          while success:
              count = count + 1
              sec = sec + frameRate
              sec = round(sec, 2)
              success = getFrame(sec)
      

      【讨论】:

      • @KSp 因为frameRate = 0.5 每半秒提取一帧,如果您的视频长度为3秒,则帧数将为6。
      【解决方案9】:

      此函数从 1 fps 的视频中提取图像,此外它还识别最后一帧并停止读取:

      import cv2
      import numpy as np
      
      def extract_image_one_fps(video_source_path):
      
          vidcap = cv2.VideoCapture(video_source_path)
          count = 0
          success = True
          while success:
            vidcap.set(cv2.CAP_PROP_POS_MSEC,(count*1000))      
            success,image = vidcap.read()
      
            ## Stop when last frame is identified
            image_last = cv2.imread("frame{}.png".format(count-1))
            if np.array_equal(image,image_last):
                break
      
            cv2.imwrite("frame%d.png" % count, image)     # save frame as PNG file
            print '{}.sec reading a new frame: {} '.format(count,success)
            count += 1
      

      【讨论】:

        【解决方案10】:

        我通过 Anaconda 的 Spyder 软件使用 Python。使用@Gshocked 在这个线程的问题中列出的原始代码,代码不起作用(python 不会读取 mp4 文件)。所以我下载了 OpenCV 3.2 并从“bin”文件夹中复制了“opencv_ffmpeg320.dll”和“opencv_ffmpeg320_64.dll”。我将这两个 dll 文件都粘贴到 Anaconda 的“Dlls”文件夹中。

        Anaconda 还有一个“pckgs”文件夹...我将下载的整个“OpenCV 3.2”文件夹复制并粘贴到 Anaconda“pckgs”文件夹中。

        最后,Anaconda 有一个“Library”文件夹,其中有一个“bin”子文件夹。我将“opencv_ffmpeg320.dll”和“opencv_ffmpeg320_64.dll”文件粘贴到该文件夹​​中。

        关闭并重新启动 Spyder 后,代码运行正常。我不确定这三种方法中的哪一种有效,我也懒得回去弄清楚了。但它确实有效,干杯!

        【讨论】:

          【解决方案11】:

          从视频演示中提取幻灯片/帧有多种原因,尤其是在教育或会议相关视频的情况下。它使您无需观看整个视频即可访问学习笔记。 我已经多次遇到这个问题,所以我决定自己使用 python 为它创建一个解决方案。我已将代码开源,您可以轻松设置此工具并通过几个简单的步骤运行它。 请参阅此youtube video tutorial. 如何使用此工具的步骤。

          • 克隆this project video2pdfslides
          • 通过运行“pip install -r requirements.txt”设置您的环境
          • 复制视频路径
          • 运行“python video2pdfslides.py ” 繁荣! pdf 幻灯片将在输出文件夹中可用 做笔记,尽情享受吧!

          【讨论】:

            猜你喜欢
            • 2016-11-14
            • 2012-05-27
            • 1970-01-01
            • 2021-08-24
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多