Python: extracción y almacenamiento de fotogramas de video

133

Así que seguí este tutorial pero no parece hacer nada. Simplemente nada. Espera unos segundos y cierra el programa. ¿Qué está mal con este código?

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

Además, en los comentarios dice que esto limita los fotogramas a 1000? ¿Por qué?

EDITAR: Intenté hacerlo success = Trueprimero pero eso no ayudó. Solo creó una imagen de 0 bytes.

Conmocionado
fuente
1
¿Cuál es el valor de success?
101
2
Cual es el valor ? El tipo puede ser booleano, pero ¿es Trueo False?
That1Guy
1
Sí, pero ¿cuál es tu valor? Podría ser falso, en cuyo caso su programa simplemente "esperaría unos segundos y se cerraría". En otras palabras, agregue un print successlugar.
101
1
No tiene sentido forzar success; si es falso, significa que la lectura del video ha fallado por alguna razón. Primero necesitas hacer funcionar esa parte.
101
1
Tu lectura está fallando. ¿Ha creado opencv con python y ffmpeg como se indica en el tutorial? brew install opencv --python27 --ffmpegsi está utilizando una versión diferente de Python, deberá cambiarla a su versión.
Knells

Respuestas:

226

Desde aquí descargue este video para que tengamos el mismo archivo de video para la prueba. Asegúrese de tener ese archivo mp4 en el mismo directorio de su código Python. Luego, también asegúrese de ejecutar el intérprete de Python desde el mismo directorio.

Luego modifique el código, descarte waitKeyque está perdiendo el tiempo y sin una ventana no puede capturar los eventos del teclado. También imprimimos el successvalor para asegurarnos de que está leyendo los marcos correctamente.

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

¿Cómo va eso?

hormiga de fuego
fuente
4
Esto ahorra un archivo JPEG vacía y devuelveRead a new frame: False
3
Eso significa que opencv no puede leer el video. Lo más probable es que no pueda acceder a ffmpeg. ¿Qué sistema operativo estás usando?
fireant
1
Mire esto: kronoskoders.logdown.com/posts/…
fireant
3
Busque en Google las instrucciones para su versión específica de opencv y siga con precisión cómo hacer que ffmpeg y opencv-python funcionen en Windows.
fireant
3
Entonces usé esta pregunta para resolver mi problema con la compatibilidad. Tuve que cambiar el nombre de la DLL a opencv_ffmpeg300.dll (ya que la instalación de Python de OpenCV2 era 3.0.0). Lo coloqué en mi directorio de Python (C: \ Python27). No necesitaba instalar la versión de Windows de ffmpeg ni opencv, pero necesitaba esa DLL que viene con OpenCV, pero eliminé el resto de OpenCV después de eso. De todos modos, seleccionaré esto como respuesta, pero cualquiera que lea esto debe saber acerca de esta DLL ESENCIAL.
37

Para ampliar esta pregunta (y la respuesta de @ user2700065) para casos ligeramente diferentes, si alguien no quiere extraer cada fotograma, pero quiere extraer el fotograma cada segundo. Entonces, un video de 1 minuto dará 60 cuadros (imágenes).

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)
Bhushan Babar
fuente
Estoy usando opencv-2.4.9, así que en lugar de cv2.CAP_PROP_POS_MSECtener que usarcv2.cv.CAP_PROP_POS_MSEC
Pratik Kumar
1
¿Cómo cambiar el código si quiero marcos cada 5 segundos?
Soumya Boral
1
@SoumyaBoralcount = count + 5
Bhushan Babar
@BhushanBabar ¿no debería haber un cv2.imwrite()al principio del ciclo ya que llamó cv2.imread()antes del ciclo?
mLstudent33
@ mLstudent33 lo siento, no te entiendo, por favor da más detalles.
Bhushan Babar
12

Este es un ajuste de la respuesta anterior para python 3.x de @GShocked, lo publicaría en el comentario, pero no tengo suficiente reputación

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
fuente
11

Esta es la función que convertirá la mayoría de los formatos de video a la cantidad de cuadros que hay en el video. Funciona Python3conOpenCV 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()
        # 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)

Es compatible con .mtsarchivos normales como .mp4y .avi. Probado y probado en .mtsarchivos. Funciona de maravilla.

Patel duro
fuente
8

Después de mucha investigación sobre cómo convertir cuadros en video, he creado esta función, espero que esto ayude. Requerimos opencv para esto:

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)

cambie el valor de fps (fotogramas por segundo), la ruta de la carpeta de entrada y la ruta de la carpeta de salida de acuerdo con sus propias ubicaciones locales

Puja Sharma
fuente
files.sort (key = lambda x: int (x [5: -4])) AÑADIR LA LÍNEA ANTERIOR que ayuda a ordenar los fotogramas de acuerdo con el número y no la cadena, por ejemplo: inicialmente frame1.jpg fue seguido por frame10.jpg no frame2.jpg, la línea de arriba ordena los archivos según los números que contiene.
Puja Sharma
3
La pregunta era de un video a otro
Santhosh Dhaipule Chandrakanth
no guardar el video para mí por alguna razón
Raksha
7

Las respuestas anteriores han perdido el primer cuadro. Y será bueno almacenar las imágenes en una carpeta.

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

Por cierto, puede comprobar la tasa de fotogramas por VLC. Vaya a Windows -> información multimedia -> detalles del códec

Yuchao Jiang
fuente
¿Hay alguna forma de aumentar la velocidad de fotogramas durante la extracción?
Pratik Khadloya
No. Cuando se hace un video, la velocidad de fotogramas es fija. No puedes extraer más que eso.
Yuchao Jiang
Qué respuesta tan asombrosa. Funcionó perfectamente para mí. ¿Habría alguna manera de modificar el bucle en el código para que solo obtenga cuadros de un cierto rango, como el cuadro 120-160? ¡Gracias!
Bowen Liu
Puede utilizar la variable count para especificar los fotogramas que le gustaría extraer.
Yuchao Jiang
¿Qué tengo que hacer si quiero extraer del video, digamos, 15 cuadros que están colocados equidistantemente en el tiempo desde el comienzo del video hasta el final del video? Descubrí que tengo que usar cv.CAP_PROP_POS_AVI_RATIO, pero no sé cómo. tnx
NeStack
7

Este código extrae fotogramas del video y guarda los fotogramas en formato .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
Regocíjate TJ
fuente
4

Estoy usando Python a través del software Spyder de Anaconda. Usando el código original enumerado en la pregunta de este hilo por @Gshocked, el código no funciona (Python no leerá el archivo mp4). Así que descargué OpenCV 3.2 y copié "opencv_ffmpeg320.dll" y "opencv_ffmpeg320_64.dll" de la carpeta "bin". Pegué ambos archivos dll en la carpeta "Dlls" de Anaconda.

Anaconda también tiene una carpeta "pckgs" ... Copié y pegué toda la carpeta "OpenCV 3.2" que descargué en la carpeta "pckgs" de Anaconda.

Finalmente, Anaconda tiene una carpeta "Biblioteca" que tiene una subcarpeta "bin". Pegué los archivos "opencv_ffmpeg320.dll" y "opencv_ffmpeg320_64.dll" en esa carpeta.

Después de cerrar y reiniciar Spyder, el código funcionó. No estoy seguro de cuál de los tres métodos funcionó, y soy demasiado vago para volver atrás y resolverlo. Pero funciona así, ¡salud!

SpaceFarmer2020
fuente
4

Esta función extrae imágenes de video con 1 fps, ADEMÁS identifica el último cuadro y deja de leer también:

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
fuente
0

El siguiente script extraerá fotogramas cada medio segundo de todos los videos en la carpeta. (Funciona en 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)
Nishān Wickramarathna
fuente