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.