Python - wyodrębnianie i zapisywanie ramek wideo


135

Więc skorzystałem z tego samouczka, ale wydaje się, że nic nie robi. Po prostu nic. Czeka kilka sekund i zamyka program. Co jest nie tak z tym kodem?

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

Również w komentarzach jest napisane, że ogranicza to ramki do 1000? Czemu?

EDYCJA: próbowałem zrobić success = Truenajpierw, ale to nie pomogło. Utworzył tylko jeden obraz, który miał 0 bajtów.


1
Jaka jest wartość success?
101

2
Jaka jest wartość ? Typu mogą być logiczna, ale jest to Truealbo False?
That1Guy

1
Tak, ale jaka jest Twoja wartość? Może to być fałsz, w którym to przypadku Twój program po prostu „odczeka kilka sekund i zamknie się”. Innymi słowy, dodaj print successgdzieś.
101

1
Nie ma sensu na siłę success; jeśli jest fałszywa, oznacza to, że odczyt wideo z jakiegoś powodu nie powiódł się. Najpierw musisz uruchomić ten kawałek.
101

1
Twój readh zawodzi. Czy zbudowałeś opencv z pythonem i ffmpeg zgodnie z instrukcją w samouczku? brew install opencv --python27 --ffmpegjeśli używasz innej wersji Pythona, będziesz musiał zmienić ją na swoją wersję.
Knells

Odpowiedzi:


228

Od tutaj pobrać ten film, więc mamy ten sam plik wideo do testu. Upewnij się, że ten plik mp4 znajduje się w tym samym katalogu, w którym znajduje się kod Pythona. Następnie upewnij się, że interpreter języka Python jest uruchamiany z tego samego katalogu.

Następnie zmodyfikuj kod, porzuć waitKeymarnowanie czasu również bez okna nie może przechwytywać zdarzeń klawiatury. Drukujemy również successwartość, aby upewnić się, że pomyślnie odczytuje ramki.

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

Jak to idzie?


4
Oszczędza to pusty plik JPEG, i zwracaRead a new frame: False

3
Oznacza to, że opencv nie może odczytać wideo. Najprawdopodobniej nie może uzyskać dostępu do ffmpeg. jakiego systemu operacyjnego używasz?
fireant


3
Wygoogluj instrukcje dla twojej konkretnej wersji opencv i postępuj dokładnie, jak sprawić, by ffmpeg i opencv-python działały w systemie Windows.
fireant

3
Więc użyłem tego pytania, aby rozwiązać mój problem z kompatybilnością. Musiałem zmienić nazwę biblioteki DLL na opencv_ffmpeg300.dll (ponieważ instalacja OpenCV2 w Pythonie to 3.0.0). Umieściłem go w moim katalogu Python (C: \ Python27). Nie musiałem instalować wersji ffmpeg ani opencv dla systemu Windows, ale potrzebowałem tej biblioteki DLL, która jest dostarczana z OpenCV, ale później usunąłem resztę OpenCV. W każdym razie wybiorę to jako odpowiedź, ale każdy, kto to czyta, musi wiedzieć o tej ESSENTIAL DLL.

37

Aby rozszerzyć to pytanie (i odpowiedź autorstwa @ user2700065) dla nieco innych przypadków, jeśli ktoś nie chce wyodrębniać każdej klatki, ale chce wyodrębniać klatkę co jedną sekundę. Tak więc 1-minutowy film da 60 klatek (obrazów).

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)

Używam opencv-2.4.9, więc zamiast cv2.CAP_PROP_POS_MSECmusiałem użyćcv2.cv.CAP_PROP_POS_MSEC
Pratik Kumar

1
Jak zmienić kod, jeśli chcę, aby ramki były co powiedzmy 5 sekund?
Soumya Boral

1
@SoumyaBoralcount = count + 5
Bhushan Babar

@BhushanBabar nie powinno być znaku cv2.imwrite()na początku pętli, skoro dzwoniłeś cv2.imread()przed pętlą?
mLstudent33

@ mLstudent33 przepraszam, nie rozumiem, proszę rozwinąć.
Bhushan Babar

12

To jest poprawka z poprzedniej odpowiedzi dla Pythona 3.x z @GShocked, opublikowałbym to w komentarzu, ale nie mam wystarczającej reputacji

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

Jest to funkcja, która konwertuje większość formatów wideo na liczbę klatek w filmie. Działa Python3zOpenCV 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)

Obsługuje .mtsi normalne pliki, takie jak .mp4i .avi. Wypróbowane i przetestowane na .mtsplikach. Działa jak marzenie.


8

Po wielu badaniach nad konwersją klatek na wideo stworzyłem tę funkcję, mam nadzieję, że to pomoże. Do tego potrzebujemy 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)

zmień wartość fps (klatki na sekundę), ścieżkę folderu wejściowego i ścieżkę folderu wyjściowego zgodnie z lokalnymi lokalizacjami


files.sort (key = lambda x: int (x [5: -4])) DODAWANIE POWYŻSZEJ LINII, która pomaga sortować klatki według numeru a nie ciągu znaków np .: początkowo po ramce1.jpg następował frame10.jpg nie frame2.jpg, powyższa linia sortuje pliki zgodnie z zawartymi w nich numerami.
Puja Sharma

3
Pytanie było od wideo do klatki
Santhosh Dhaipule Chandrakanth

z jakiegoś powodu nie
zapisuję

7

Poprzednie odpowiedzi straciły pierwszą klatkę. I fajnie będzie przechowywać obrazy w folderze.

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

Nawiasem mówiąc, możesz sprawdzić współczynnik klatek e przez VLC. Przejdź do windows -> informacje o multimediach -> szczegóły kodeka


Czy istnieje sposób na zwiększenie liczby klatek na sekundę podczas wyodrębniania?
Pratik Khadloya,

Nie. Podczas tworzenia wideo liczba klatek na sekundę jest stała. Nie możesz wydobyć więcej niż to.
Yuchao Jiang

Cóż za niesamowita odpowiedź. U mnie zadziałało idealnie. Czy byłby sposób na zmodyfikowanie pętli w kodzie, tak żebym otrzymywał tylko klatki z określonego zakresu, na przykład klatki 120-160? Dzięki!
Bowen Liu

Możesz użyć liczby zmiennych, aby określić klatki, które chcesz wyodrębnić.
Yuchao Jiang

co mam zrobić, jeśli chcę wyodrębnić z wideo, powiedzmy, 15 klatek, które są rozmieszczone w równych odstępach czasu od początku wideo do końca wideo? Dowiedziałem się, że muszę użyć cv.CAP_PROP_POS_AVI_RATIO, ale nie wiem jak. tnx
NeStack

7

Ten kod wyodrębnia klatki z wideo i zapisuje klatki w formacie .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

Używam Pythona za pośrednictwem oprogramowania Spyder firmy Anaconda. Używając oryginalnego kodu podanego w pytaniu tego wątku przez @Gshocked, kod nie działa (python nie odczyta pliku mp4). Pobrałem więc OpenCV 3.2 i skopiowałem „opencv_ffmpeg320.dll” i „opencv_ffmpeg320_64.dll” z folderu „bin”. Wklejałem oba te pliki dll do folderu „Dlls” Anacondy.

Anaconda ma również folder „pckgs”… Skopiowałem i wkleiłem cały pobrany folder „OpenCV 3.2” do folderu „pckgs” programu Anaconda.

Wreszcie Anaconda ma folder „Library”, który ma podfolder „bin”. Wklejałem pliki „opencv_ffmpeg320.dll” i „opencv_ffmpeg320_64.dll” do tego folderu.

Po zamknięciu i ponownym uruchomieniu Spydera kod działał. Nie jestem pewien, która z trzech metod zadziałała, i jestem zbyt leniwy, żeby wrócić i to rozgryźć. Ale to działa, na zdrowie!


4

Ta funkcja wyodrębnia obrazy z wideo z 1 fps, DODATKOWO identyfikuje ostatnią klatkę i przestaje czytać:

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

Poniższy skrypt wyodrębni klatki co pół sekundy ze wszystkich filmów w folderze. (Działa na Pythonie 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)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.