247

So I've followed this tutorial but it doesn't seem to do anything. Simply nothing. It waits a few seconds and closes the program. What is wrong with this code?

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

Also, in the comments it says that this limits the frames to 1000? Why?

EDIT: I tried doing success = True first but that didn't help. It only created one image that was 0 bytes.

ChrisGPT was on strike
  • 127,765
  • 105
  • 273
  • 257
Rontron
  • 3,963
  • 7
  • 26
  • 44
  • 2
    What's the value of `success`? – 101 Oct 23 '15 at 20:49
  • Its a boolean I believe, the code is from the tutorial I linked to. –  Oct 23 '15 at 20:50
  • 2
    What is the _value_? The _type_ may be boolean, but is it `True` or `False`? – That1Guy Oct 23 '15 at 20:50
  • 1
    Yes, but what is _your_ value? It might be false in which case your program would simply "wait a few seconds and close". In other words, add a `print success` somewhere. – 101 Oct 23 '15 at 20:51
  • I tried setting it to `True` but that only made one frame that was empty (0 bytes) –  Oct 23 '15 at 20:52
  • 1
    It doesn't make sense to force `success`; if it's false then that means the video read has failed for some reason. You need get that bit working first. – 101 Oct 23 '15 at 20:59
  • I've tried doing a full path to my video, doing `./`, doing just the name (like in the code) and the results don't change. I've tried several videos, with and without audio, and nothing works. –  Oct 23 '15 at 21:06
  • 1
    Your readh is failing. Have you built opencv with python and ffmpeg as instructed in the tutorial? `brew install opencv --python27 --ffmpeg` if you are using a different version of Python you will need to change it to your version. – Knells Oct 28 '15 at 02:01
  • 1
    Are you using OSX? Linux? Windows? – fireant Oct 28 '15 at 19:18

14 Answers14

421

From here download this video so we have the same video file for the test. Make sure to have that mp4 file in the same directory of your python code. Then also make sure to run the python interpreter from the same directory.

Then modify the code, ditch waitKey that's wasting time also without a window it cannot capture the keyboard events. Also we print the success value to make sure it's reading the frames successfully.

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

How does that go?

fireant
  • 14,080
  • 4
  • 39
  • 48
  • 4
    This saves an empty jpeg file, and it returns `Read a new frame: False` –  Oct 28 '15 at 23:06
  • 4
    That means opencv can not read the video. Most likely it cannot access ffmpeg. what OS are you using? – fireant Oct 28 '15 at 23:10
  • I've also tried this instead of the `while` loop, but it just made 50 empty jpegs. `for i in range(50): success,image = vidcap.read() cv2.imwrite('Frame ' + str(i) + '.jpg',image) ` –  Oct 28 '15 at 23:10
  • I did not realize I needed ffmpeg. I've tried using ffmpeg alone to separate the frames, but I couldn't figure out how to install it. I did not know if I needed the regular ffmpeg or pyffmpeg, which was troublesome. –  Oct 28 '15 at 23:13
  • 1
    Look at this: http://kronoskoders.logdown.com/posts/256664-installing-opencv-and-ffmpeg-on-windows – fireant Oct 28 '15 at 23:14
  • Still, even after installing ffmpeg and its env variables, I get the same output. I've tested and ffmpeg works in windows cmd, but not in Python. –  Oct 28 '15 at 23:28
  • 3
    Google the instructions for your specific version of opencv and follow precisely on how to get ffmpeg and opencv-python working on Windows. – fireant Oct 28 '15 at 23:40
  • 3
    So I used [this](http://stackoverflow.com/questions/11699298/opencv-2-4-videocapture-not-working-on-windows) question to solve my problem with the compatibility. I had to rename the DLL to opencv_ffmpeg300.dll (as the Python-installation of OpenCV2 was 3.0.0). I placed it into my Python directory (C:\Python27). I didn't need to install the windows version of ffmpeg nor opencv, but I did need that DLL that comes with OpenCV, but I deleted the rest of OpenCV after that. Anyhow, I will select this as an answer, but anyone reading this must know about this ESSENTIAL DLL. –  Oct 29 '15 at 00:33
  • 1
    @GShocked that looks like you should write up your own answer with that information in it. – Bill Woodger Oct 30 '15 at 10:38
  • @BillWoodger I did now –  Oct 30 '15 at 15:45
  • Does this require a vidcap.release() to close things out properly? – auro Jul 05 '16 at 20:30
  • @auro I don't think so. I don't have the time to go check the code now, but the destructor is [supposed](http://docs.opencv.org/2.4/modules/highgui/doc/reading_and_writing_images_and_video.html#videocapture-release) to call release() – fireant Jul 14 '16 at 18:45
  • Its not working for python 3. How can I use this with python 3? – Anmol Monga Feb 13 '17 at 09:33
  • @AnmolMonga simply replace `print 'Read a new frame: ', success` with `print('Read a new frame: ', success)` – Danny Wang Jul 21 '17 at 01:29
  • Anyone have advice for installing opencv-python with support for ffmpeg on os x? I found [this post](http://blog.jiashen.me/2014/12/23/build-opencv-3-on-mac-os-x-with-python-3-and-ffmpeg-support/) but it references another post by @Luigolas that has since been removed. – daveaseeman Dec 11 '17 at 22:59
  • @daveaseeman there are some tutorials on how to install opencv using homebrew, I think that has a flag for ffmpeg. If those blog posts / stackoverflow posts are not much of help then should ask a new question here. – fireant Dec 12 '17 at 05:11
  • 1
    an easy fix is to run frame extractor code in the folder where videos are located.! Somehow, cv2.VideoCapture() doesn't recognise the given video_path – Anu Sep 28 '18 at 03:17
  • 1
    Good stuff this worked for me and replaced FFMpeg which was giving me issues – MNM Dec 04 '19 at 02:47
  • 1
    It's worked thank you, Done in Windows 10 python v3, opencv v2 – Dang_Ho Dec 07 '19 at 08:13
  • this worked for me, how can i tell to capture a couple of frames every second ? and not all of them – PayamB. Apr 05 '20 at 08:51
  • Does anyone know why this does not work if the .mp4 is not being read from the same directory as the `.py` code? Wasted way too much time combing through my syntax trying to get this to work but I just cannot figure out why – fuGGet321 Aug 18 '23 at 19:07
72

To extend on this question (& answer by @user2700065) for a slightly different cases, if anyone does not want to extract every frame but wants to extract frame every one second. So a 1-minute video will give 60 frames(images).

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)
Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
BeeBee8
  • 2,944
  • 1
  • 27
  • 39
25

This is Function which will convert most of the video formats to number of frames there are in the video. It works on Python3 with OpenCV 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)

It supports .mts and normal files like .mp4 and .avi. Tried and Tested on .mts files. Works like a Charm.

Harsh Patel
  • 409
  • 6
  • 16
14

This is a tweak from previous answer for python 3.x from @GShocked, I would post it to the comment, but dont have enough reputation

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)
XRarach
  • 196
  • 1
  • 8
12

After a lot of research on how to convert frames to video I have created this function hope this helps. We require opencv for this:

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)

change the value of fps(frames per second),input folder path and output folder path according to your own local locations

Puja Sharma
  • 139
  • 1
  • 4
  • 1
    files.sort(key = lambda x: int(x[5:-4])) ADDING THE ABOVE LINE which helps to sort the frames according to the number and not the string eg: initially frame1.jpg was followed by frame10.jpg not frame2.jpg ,the line above sorts the files according to the numbers in it . – Puja Sharma May 05 '17 at 05:12
  • 5
    Question was from video to frame – Santhosh Dhaipule Chandrakanth Nov 30 '18 at 05:35
12

The previous answers have lost the first frame. And it will be nice to store the images in a folder.

# 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))

By the way, you can check the frame rate by VLC. Go to windows -> media information -> codec details

Yuchao Jiang
  • 3,522
  • 30
  • 23
  • Is there a way to increase frame rate while extraction? – Pratik Khadloya Aug 19 '18 at 00:41
  • No. When a video is made, the frame rate is fixed. You can't extract more than that. – Yuchao Jiang Aug 20 '18 at 21:43
  • What an amazing answer. Worked perfectly for me. Would there be a way to tweak the loop in the code so that I only get frames from a certain range, like frame 120 - 160? Thanks! – Bowen Liu Feb 06 '20 at 01:03
  • You can use the variable count to specify the frames you would like to extract. – Yuchao Jiang Feb 07 '20 at 04:14
  • what do I have to do if I want to extract from the video let's say 15 frames that are positioned equidistantly in time from the video beginning to the video end? I found out I have to use `cv.CAP_PROP_POS_AVI_RATIO`, but I don't know how. tnx – NeStack Jun 26 '20 at 19:05
10

This code extract frames from the video and save the frames in .jpg formate

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
Rejoice T J
  • 109
  • 1
  • 4
10

In 2022 you also have the option to use ImageIO to do this, which IMHO is much more hasslefree and readable.

import imageio.v3 as iio

for idx, frame in enumerate(iio.imiter("imageio:cockatoo.mp4")):
    iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)

Sidenote 1: "imageio:cockatoo.mp4" is a standard image provided by ImageIO for testing and demonstration purposes. You can simply replace it with "path/to/your/video.mp4".

Sidenote 2: You will have to install one of ImageIO's optional dependencies to read video data, which can be done via pip install imageio-ffmpeg or pip install av.


You can time this against OpenCV and you will find that, there isn't that much to gain from OpenCV on this front either:

Read-Only Timings
=================
OpenCV:         0.453
imageio_ffmpeg: 0.765
imageio_pyav:   0.272
Read + Write Timings
====================
OpenCV:         3.237
imageio_ffmpeg: 1.597
imageio_pyav:   1.506

By default, OpenCV and ImageIO+av are about equally fast when reading. Both direct bind into the FFmpeg libraries under the hood so this is rather unsurprising. However, ImageIO allows you to tweak FFmpeg's default threadding model (thread_type="FRAME") which is much faster when bulk reading.

More importantly, ImageIO is much faster at writing JPEG compared to OpenCV. This is because pillow is faster than OpenCV here which ImageIO capitalizes on. Writing images dominates runtime for this scenario, so you end up with an overall 2x improvement when using ImageIO instead of OpenCV.

Here is the code for reference:

import imageio.v3 as iio
import cv2
import timeit
from pathlib import Path

# create a common local file for benchmarking
video_file = "shared_video.mp4"

if not Path(video_file).exists():
    frames = iio.imread("imageio:cockatoo.mp4")
    meta = iio.immeta("imageio:cockatoo.mp4", exclude_applied=False)
    iio.imwrite(video_file, frames, fps=meta["fps"])

repeats = 10


def read_cv2():
    vidcap = cv2.VideoCapture(video_file)
    success, image = vidcap.read()
    idx = 0
    while success:
        cv2.imwrite(f"extracted_images/frame{idx:03d}.jpg", image)
        success, image = vidcap.read()
        idx += 1


def read_imageio_ffmpeg():
    for idx, frame in enumerate(iio.imiter(video_file, plugin="FFMPEG")):
        iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)


def read_imageio_pyav():
    for idx, frame in enumerate(
        iio.imiter(video_file, plugin="pyav", format="rgb24", thread_type="FRAME")
    ):
        iio.imwrite(f"extracted_images/frame{idx:03d}.jpg", frame)


time_cv2 = (
    timeit.timeit("read_cv2()", setup="from __main__ import read_cv2", number=repeats)
    / repeats
)
time_imageio_ffmpeg = (
    timeit.timeit(
        "read_imageio_ffmpeg()",
        setup="from __main__ import read_imageio_ffmpeg",
        number=repeats,
    )
    / repeats
)
time_imageio_pyav = (
    timeit.timeit(
        "read_imageio_pyav()",
        setup="from __main__ import read_imageio_pyav",
        number=repeats,
    )
    / repeats
)

print(
    f"""
Timings
=======
OpenCV:         {time_cv2:<3.3f}
imageio_ffmpeg: {time_imageio_ffmpeg:<3.3f}
imageio_pyav:   {time_imageio_pyav:<3.3f}
"""
)

FirefoxMetzger
  • 2,880
  • 1
  • 18
  • 32
  • thanks for this, seems that the imageio code will generate 1 image for every frame so if the video is 23fps and the video is 2 seconds long it will generate 46 images, any way like to only generate for example 1 image per second or something like that? – AMM Jul 12 '22 at 02:35
  • @AMM Yes, that's possible. The exact way depends on the backend you use. One way would be to use `if idx % desired_rate != 0: continue`. This works, but may struggle with variable framerate streams. Another could be to let FFmpeg resample to the desired framerate via `pyav` by using the optional `filter_sequence=[("fps", "1")]` kwarg. Filters in pyav are documented [here](https://imageio.readthedocs.io/en/stable/_autosummary/imageio.plugins.pyav.html#filters). – FirefoxMetzger Jul 12 '22 at 04:41
9

Following script will extract frames every half a second of all videos in folder. (Works on 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)
Nishan
  • 3,644
  • 1
  • 32
  • 41
  • @KSp Because of `frameRate = 0.5` it extracts a frame every half a second, If your video length is 3 seconds, number of frames will be 6. – Nishan Feb 06 '21 at 09:10
  • I don't think you want the definition of `getFrame()` inside the loop... – beroe Jan 29 '23 at 23:21
6

This function extracts images from video with 1 fps, IN ADDITION it identifies the last frame and stops reading also:

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
Bence Kővári
  • 153
  • 1
  • 8
4

I am using Python via Anaconda's Spyder software. Using the original code listed in the question of this thread by @Gshocked, the code does not work (the python won't read the mp4 file). So I downloaded OpenCV 3.2 and copied "opencv_ffmpeg320.dll" and "opencv_ffmpeg320_64.dll" from the "bin" folder. I pasted both of these dll files to Anaconda's "Dlls" folder.

Anaconda also has a "pckgs" folder...I copied and pasted the entire "OpenCV 3.2" folder that I downloaded to the Anaconda "pckgs" folder.

Finally, Anaconda has a "Library" folder which has a "bin" subfolder. I pasted the "opencv_ffmpeg320.dll" and "opencv_ffmpeg320_64.dll" files to that folder.

After closing and restarting Spyder, the code worked. I'm not sure which of the three methods worked, and I'm too lazy to go back and figure it out. But it works so, cheers!

2

i might be late here but you can use this pip package to quickly generate images from videos. You can also get images using specific fps.

pip install videoToImages

then type the following command in terminal

videoToimages --videoFolder [pathToVideosFolder]

Example: videoToimages --videoFolder "c:/videos"

for specific output fps , set --fps 10 to any required value. --fps 1 means one image per one second of the video.

Full commands:

  • videoToimages --videoFolder "c:/videos"

  • videoToimages --videoFolder "c:/videos" --fps 10 --img_size (512, 512)

Ali Waqas
  • 306
  • 2
  • 12
1

This code is simple and guarantees reliable execution.


# path of video file
video_path = "path/to/video.mp4"

# Open video file
video = cv2.VideoCapture(video_path)

# number of frames in video
frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))

# Convert frame to image and save to file
for i in range(frame_count):
    ret, frame = video.read()
    if ret:
        image_path = f"path/to/image_{i}.jpg"
        cv2.imwrite(image_path, frame)

# Close video file
video.release()
VDoring
  • 21
  • 2
-2

There are several reasons to extract slides/frames from a video presentation, especially in the case of education or conference related videos. It allows you to access the study notes without watching the whole video. I have faced this issue several times, so I decided to create a solution for it myself using python. I have made the code open-source, you can easily set up this tool and run it in few simple steps. Refer to this for youtube video tutorial. Steps on how to use this tool.

  • Clone this project video2pdfslides
  • Set up your environment by running "pip install -r requirements.txt"
  • Copy your video path
  • Run "python video2pdfslides.py <video_path>" Boom! the pdf slides will be available in in output folder Make notes and enjoy!
Kaushik J
  • 962
  • 7
  • 17