Znaczące miniatury dla wideo używającego FFmpeg

80

FFmpeg może przechwytywać obrazy z filmów, które można wykorzystać jako miniatury do reprezentowania filmu. Najczęstsze sposoby wykonania tego są przechwycone na Wiki FFmpeg .

Ale nie chcę wybierać losowych klatek w niektórych odstępach czasu. Znalazłem kilka opcji za pomocą filtrów na FFmpeg do przechwytywania zmian sceny:

Filtr thumbnailpróbuje znaleźć najbardziej reprezentatywne klatki w filmie:

ffmpeg -i input.mp4 -vf  "thumbnail,scale=640:360" -frames:v 1 thumb.png

a następujące polecenie wybiera tylko ramki, które mają więcej niż 40% zmian w porównaniu z poprzednimi (a więc prawdopodobnie są to zmiany scen) i generuje sekwencję 5 PNG.

ffmpeg -i input.mp4 -vf  "select=gt(scene\,0.4),scale=640:360" -frames:v 5 thumb%03d.png

Informacje kredytowe za powyższe polecenia dla Fabio Sonnati . Drugi wydawał się lepszy, ponieważ mogłem uzyskać n zdjęć i wybrać najlepszy. Próbowałem i wygenerowałem ten sam obraz 5 razy.

Dalsze dochodzenie doprowadziło mnie do:

ffmpeg -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr  out%02d.png

-vsync vfrzapewnia, że ​​otrzymujesz różne obrazy. To wciąż zawsze wybiera pierwszą klatkę filmu, w większości przypadków pierwsza klatka jest napisami / logo i nie ma znaczenia, więc dodałem -ss3, aby odrzucić pierwsze 3 sekundy filmu.

Moje ostatnie polecenie wygląda następująco:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.5)" -frames:v 5 -vsync vfr out%02d.jpg

To było najlepsze, co mogłem zrobić. Zauważyłem, że ponieważ wybrałem tylko 5 filmów, wszystkie pochodzą głównie z początku filmu i mogą przegapić ważne sceny, które pojawią się później w filmie

Chciałbym wybrać twoje mózgi, aby znaleźć inne lepsze opcje.

d33pika
źródło
Ładne przykłady poleceń. FWIW, nie napotkałem żadnych problemów ze zdjęciami JPEG generowanymi przez FFmpeg w OS X (10.8, FFmpeg 1.1 i poniżej). Twoje od drugiego do ostatniego polecenia działa dla mnie dobrze - podobnie jak ostatnie - i żaden z nich nie powoduje pustych plików JPG. Kompilowałem z libopenjpeg... nie jestem pewien, czy to robi różnicę.
slhck
Dzięki slhck. Edytowałem pytanie ze szczegółami konfiguracji / wersji ffmpeg. Nie zaktualizowałem do wersji 1.1 na tym komputerze. Zrobię to i zobaczę, czy to zmieni jakiekolwiek wyniki.
d33pika
1
Więc jesteś na Ubuntu? Czy możesz wypróbować najnowszą wersję Git Master ze statycznej wersji lub samodzielnie się skompilować i uruchomić ponownie? Lub najnowsza stajnia. Właśnie sprawdziłem, używa również mjpegkodera dla mnie, a także sprawdziłem jpegoptimi exiv2oba działają dobrze dla mnie ze wszystkimi wynikami JPG z twoich przykładowych poleceń.
slhck,
1
Zaktualizowałem i działa teraz! Myślę, że poprzednia wersja miała kilka błędów.
d33pika
Czy możesz śmiało opublikować nową wersję rozwiązania, najlepiej z linkiem do dziennika zmian pokazującego napotkany błąd, a następnie naprawionego w nowej wersji?
Lizz,

Odpowiedzi:

29

Co powiesz na poszukiwanie pierwszej klatki o wartości> 40% w każdym z 5 przedziałów czasowych, gdzie przedziały czasowe to 1., 2., 3., 4. i 5. 20% filmu.

Możesz także podzielić go na 6 przedziałów czasowych i zignorować pierwszy, aby uniknąć kredytów.

W praktyce oznaczałoby to ustawienie niskiej liczby klatek na sekundę podczas sprawdzania zmiany sceny i argumentu, aby wyrzucić pierwszy fragment filmu.

...coś jak:

ffmpeg -ss 3 -i input.mp4 -vf "select=gt(scene\,0.4)" -frames:v 5 -vsync vfr -vf fps=fps=1/600 out%02d.jpg
JESTEM
źródło
1
Odbierane są całkowicie czarne lub białe obrazy. Jak tego uniknąć?
d33pika
1
Istnieją filtry do wykrywania czarnych ramek i ich sekwencji ( ffmpeg.org/ffmpeg-filters.html#blackframe lub ffmpeg.org/ffmpeg-filters.html#blackdetect ). Nie sprawia, że ​​żadna z nich nie jest w stanie przekształcić się w rosnącą jedną linijkę, ale zdecydowanie powinieneś być w stanie usunąć czarne ramki (oddzielny krok) i wyodrębnić miniatury z powstałego filmu.
AM
5
Jeśli chodzi o białe ramki, to teraz robi się skomplikowane, ale nadal wygląda na to, że istnieje sposób: 1. usuń czarne ramki 2. neguj (biały zmienia się w czarny) 3. usuń białe ramki 4. ponownie neguj 5. wyodrębnij miniatury (Jeśli zdołasz rozebrać czarne lub białe ramki, szczególnie w jednym wierszu, czy możesz to tutaj zamieścić? Mogę dodać to do odpowiedzi dla przyszłych czytelników. ... lub faktycznie możesz utworzyć nowe pytanie i odpowiedź zdecydowanie poparłbym to.)
AM
3
Aby uniknąć nudnych obrazów, takich jak zwykła czerń i biel: wygeneruj więcej miniatur, niż potrzebujesz, skompresuj je przy pomocy jpeg i wybierz obrazy o większym rozmiarze pliku. Działa to zaskakująco dobrze samo w sobie, aby uzyskać przyzwoite miniatury.
Sam Watkins,
2
To polecenie nie działało, dostałem błąd Unable to find a suitable output format for 'fps=fps=1/600'. Rozwiązaniem jest dodanie -vfprzed tym argumentem (patrz stackoverflow.com/questions/28519403/ffmpeg-command-issue )
John Wiseman,
7

Zdefiniowanie znaczącego jest trudne, ale jeśli chcesz sprawnie tworzyć N miniaturek obejmujących cały plik wideo, tego właśnie używam do generowania miniatur na produkcji z treściami przesłanymi przez użytkowników.

Pseudo kod

for X in 1..N
  T = integer( (X - 0.5) * D / N )  
  run `ffmpeg -ss <T> -i <movie>
              -vf select="eq(pict_type\,I)" -vframes 1 image<X>.jpg`

Gdzie:

  • D - czas trwania wideo odczytywany ffmpeg -i <movie>sam lub z ffprobeładnym edytorem wyjściowym JSON btw
  • N - całkowita liczba miniaturek, które chcesz
  • X - liczba miniatur, od 1 do N.
  • T - punkt czasowy dla Tumbnaila

Po prostu powyższe zapisuje środkową klatkę kluczową każdej partycji filmu. Np. Jeśli film ma 300s długości i chcesz 3 miniatury, to zajmuje jedną klatkę kluczową po 50s, 150s i 250s. Dla 5 miniatur będzie to 30, 90, 150, 210, 270. Możesz ustawić N w zależności od czasu trwania filmu D, np. 5-minutowy film będzie miał 3 miniatury, ale ponad 1 godzina będzie miał 20 miniaturek.

Występ

Każde wywołanie powyższego ffmpegpolecenia zajmuje ułamek sekundy (!) Dla ~ 1 GB H.264. Jest tak, ponieważ natychmiast przeskakuje na <time>pozycję ( -sswcześniej -i) i przyjmuje pierwszą klatkę kluczową, która jest praktycznie kompletnym JPEG. Nie trzeba tracić czasu na renderowanie filmu w celu dopasowania do dokładnej pozycji czasowej.

Przetwarzanie końcowe

Możesz mieszać powyżej z scaledowolną inną metodą zmiany rozmiaru. Możesz także usunąć ramki w jednolitym kolorze lub spróbować połączyć je z innymi filtrami, takimi jak thumbnail.

gertas
źródło
2
wow przejście -ss Ndo poprzedniej -ijest niesamowitą wskazówką. Dziękuję Ci!
apinstein,
2

Kiedyś zrobiłem coś podobnego, ale wyeksportowałem wszystkie klatki wideo (w 1 fps) i porównałem je z narzędziem perla, które znalazłem, które oblicza różnicę między obrazami. Porównałem każdą klatkę z poprzednimi miniaturami, a jeśli różniła się od wszystkich miniatur, dodałem ją do kolekcji miniatur. Zaletą jest to, że jeśli wideo przesunie się ze sceny A do B i powróci do A, ffmpeg wyeksportuje 2 klatki A.

Eran Ben-Natan
źródło
Jakich czynników użyłeś do porównania 2 zdjęć?
d33pika,
Niestety nie pamiętam, to było całkiem dawno. Najpierw musisz zdecydować, której metody porównania użyć, a następnie wykonać kilka testów, aby znaleźć właściwy czynnik. To nie powinno zająć długo.
Eran Ben-Natan
Po prostu myślę tutaj głośno, ale czy nie jest to gorsza opcja? Porównując 2 wyeksportowane obrazy z wideo, straciłeś już niektóre informacje. To znaczy, łatwiej jest obliczyć podobieństwo na podstawie informacji o kodeku niż na podstawie 2 zdjęć, prawda? Ostatnio przyglądałem się niektórym algorytmom / bibliotekom do porównywania obrazów i one nie działają tak dobrze, jak może to wymagać.
Samuel
Cóż, dzięki ffmpeg możesz wyodrębniać ramki bez utraty jakości przy użyciu flagi same_quality. Jeśli korzystasz z informacji o kodeku, musisz sprawdzić, czy nie otrzymujesz tylko ramek iframe. W każdym razie to narzędzie perla działało dla mnie dobrze i jest bardzo konfigurowalne. Zajrzyj tutaj: search.cpan.org/~avif/Image-Compare-0.3/Compare.pm
Eran Ben-Natan
2

Spróbuj tego

 ffmpeg -i input.mp4 -vf fps= no_of_thumbs_req/total_video_time out%d.png

Za pomocą tego polecenia jestem w stanie wygenerować wymaganą liczbę miniatur, które są reprezentatywne dla całego wideo.

Zaginiony szczeniak
źródło
Czy nie fpsbyłaby poprawna formuła (no_of_frames_req * fps_of_vid) / total_video_frames?
flolilo
Szukam odwrotnego rozwiązania: wybierz więcej klatek z okresów, w których kamera jest bardziej stabilna i nie porusza się? (gdzie różnica między kolejnymi ramkami jest mniejsza, a nie większa). Czy jest na to sposób?
Tina J
1

Oto, co robię, aby wygenerować okresową miniaturę dla strumieni na żywo m3u8 do wykorzystania jako plakat. Znalazłem ciągłe zadanie ffmpeg tylko w celu wygenerowania miniaturek zjada cały mój procesor, więc zamiast tego uruchamiam cronjob co 60 sekund, który generuje wszystkie miniatury dla moich strumieni.

#!/usr/bin/env bash

## this is slow but gives better thumbnails (takes about 1+ minutes for 20 files)
#-vf thumbnail,scale=640:360 -frames:v 1 poster.png

## this is faster but thumbnails are random (takes about 8 seconds for 20 files)
#-vf scale=640:360 -frames:v 1 poster.png
cd ~/path/to/streams

find . -type f \
  -name "*.m3u8" \
  -execdir sh -c 'ffmpeg -y -i "$0" -vf scale=640:360 -frames:v 1 "poster.png"' \
  {} \;

Jeśli potrzebujesz robić to częściej niż 60 sekund (ograniczenie cronjob), możesz zrobić whilepętlę w skrypcie i będzie on działać wiecznie. Wystarczy dodać a, sleep 30aby zmienić częstotliwość na 30 sekund. Nie polecam jednak robienia tego z dużą liczbą filmów, ponieważ poprzednie uruchomienie może się nie zakończyć przed rozpoczęciem następnego.

Idealnie z cronjobem uruchamiam go co 5 minut.

chovy
źródło