Python - Extraindo e salvando quadros de vídeo


133

Então, eu segui este tutorial, mas não parece fazer nada. Simplesmente nada. Ele espera alguns segundos e fecha o programa. O que está errado neste 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

Além disso, nos comentários diz que isso limita os frames a 1000? Por quê?

EDIT: Eu tentei fazer success = Trueprimeiro, mas não ajudou. Ele criou apenas uma imagem com 0 bytes.


1
Qual é o valor de success?
101,

2
Qual é o valor ? O tipo pode ser booleano, mas é Trueou False?
That1Guy

1
Sim, mas qual é o seu valor? Pode ser falso, caso em que seu programa simplesmente "espere alguns segundos e feche". Em outras palavras, adicione um print successalgum lugar.
101,

1
Não faz sentido forçar success; se for falso, isso significa que a leitura do vídeo falhou por algum motivo. Você precisa fazer isso funcionar primeiro.
101,

1
Seu readh está falhando. Você construiu o opencv com python e ffmpeg conforme as instruções do tutorial? brew install opencv --python27 --ffmpegse estiver usando uma versão diferente do Python, você precisará alterá-la para a sua versão.
Knells

Respostas:


226

A partir daqui descarregue esta vídeo por isso temos o mesmo arquivo de vídeo para o teste. Certifique-se de ter esse arquivo mp4 no mesmo diretório do seu código Python. Em seguida, certifique-se de executar o interpretador python no mesmo diretório.

Então modifique o código, valha waitKeyque está perdendo tempo também sem janela não consegue capturar os eventos do teclado. Também imprimimos o successvalor para ter certeza de que ele está lendo os quadros com sucesso.

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

Como vai isso?


4
Isso salva um arquivo jpeg vazio e ele retornaRead a new frame: False

3
Isso significa que o opencv não pode ler o vídeo. Provavelmente, ele não pode acessar o ffmpeg. qual sistema operacional você está usando?
dia


3
Pesquise no Google as instruções para sua versão específica do opencv e siga precisamente como fazer o ffmpeg e o opencv-python funcionarem no Windows.
dia

3
Usei essa questão para resolver meu problema de compatibilidade. Tive que renomear a DLL para opencv_ffmpeg300.dll (já que a instalação do OpenCV2 em Python era 3.0.0). Coloquei-o em meu diretório Python (C: \ Python27). Não precisei instalar a versão do Windows do ffmpeg nem do opencv, mas precisava daquela DLL que vem com o OpenCV, mas apaguei o resto do OpenCV depois disso. De qualquer forma, selecionarei isso como uma resposta, mas quem estiver lendo isso deve conhecer esta DLL ESSENCIAL.

37

Para estender esta questão (& resposta por @ user2700065) para casos ligeiramente diferentes, se alguém não quiser extrair todos os quadros, mas quiser extrair os quadros a cada um segundo. Portanto, um vídeo de 1 minuto fornecerá 60 quadros (imagens).

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)

Estou usando opencv-2.4.9, então em vez de cv2.CAP_PROP_POS_MSECusarcv2.cv.CAP_PROP_POS_MSEC
Pratik Kumar,

1
Como alterar o código se eu quiser quadros a cada 5 segundos, digamos?
Soumya Boral

1
@SoumyaBoralcount = count + 5
Bhushan Babar

@BhushanBabar não deveria haver um cv2.imwrite()no início do loop, já que você chamou cv2.imread()antes do loop?
mLstudent33

@ mLstudent33 desculpe, não entendo, explique.
Bhushan Babar

12

Este é um ajuste da resposta anterior para python 3.x de @GShocked, eu postaria no comentário, mas não tenho reputação suficiente

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)

11

Esta é uma função que irá converter a maioria dos formatos de vídeo para o número de quadros existentes no vídeo. Funciona Python3comOpenCV 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)

Suporta .mtsarquivos normais como .mp4e .avi. Experimentado e testado em .mtsarquivos. Funciona como um encanto.


8

Depois de muita pesquisa sobre como converter quadros em vídeo, criei esta função e espero que isso ajude. Exigimos o opencv para isso:

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)

mude o valor de fps (frames por segundo), caminho da pasta de entrada e caminho da pasta de saída de acordo com suas próprias localizações locais


files.sort (key = lambda x: int (x [5: -4])) ADICIONANDO A LINHA ACIMA que ajuda a ordenar os quadros de acordo com o número e não a string, por exemplo: inicialmente frame1.jpg foi seguido por frame10.jpg não frame2.jpg, a linha acima classifica os arquivos de acordo com seus números.
Puja Sharma

3
A pergunta foi do vídeo para o quadro
Santhosh Dhaipule Chandrakanth

não salvando o vídeo para mim por algum motivo
Raksha

7

As respostas anteriores perderam o primeiro quadro. E será bom armazenar as imagens em uma pasta.

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

A propósito, você pode verificar o frame rat e por VLC. Vá para janelas -> informações de mídia -> detalhes do codec


Existe uma maneira de aumentar a taxa de quadros durante a extração?
Pratik Khadloya

Não. Quando um vídeo é feito, a taxa de quadros é fixa. Você não pode extrair mais do que isso.
Yuchao Jiang de

Que resposta incrível. Funcionou perfeitamente para mim. Haveria uma maneira de ajustar o loop no código de forma que eu só obtivesse quadros de um determinado intervalo, como os quadros 120 - 160? Obrigado!
Bowen Liu

Você pode usar a contagem de variável para especificar os quadros que deseja extrair.
Yuchao Jiang

o que tenho que fazer se quiser extrair do vídeo, digamos, 15 quadros que estão posicionados equidistantemente no tempo, do início ao fim do vídeo? Descobri que preciso usar cv.CAP_PROP_POS_AVI_RATIO, mas não sei como. tnx
NeStack

7

Este código extrai frames do vídeo e salva os frames no 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

4

Estou usando Python por meio do software Spyder do Anaconda. Usando o código original listado na questão deste tópico por @Gshocked, o código não funciona (o python não lerá o arquivo mp4). Portanto, baixei o OpenCV 3.2 e copiei "opencv_ffmpeg320.dll" e "opencv_ffmpeg320_64.dll" da pasta "bin". Colei esses dois arquivos dll na pasta "Dlls" do Anaconda.

O Anaconda também tem uma pasta "pckgs" ... Copiei e colei toda a pasta "OpenCV 3.2" que baixei para a pasta "pckgs" do Anaconda.

Finalmente, o Anaconda possui uma pasta "Biblioteca" que possui uma subpasta "bin". Colei os arquivos "opencv_ffmpeg320.dll" e "opencv_ffmpeg320_64.dll" nessa pasta.

Depois de fechar e reiniciar o Spyder, o código funcionou. Não tenho certeza de qual dos três métodos funcionou, e estou com preguiça de voltar e descobrir. Mas funciona assim, saúde!


4

Esta função extrai imagens de vídeo com 1 fps, ALÉM DISSO, identifica o último quadro e também pára de ler:

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

0

O script a seguir irá extrair quadros a cada meio segundo de todos os vídeos na pasta. (Funciona em 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)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.