Oto dwa skrypty PowerShell do dzielenia długich filmów na mniejsze rozdziały według czarnych scen.
Zapisz je jako Detect_black.ps1 i Cut_black.ps1. Pobierz ffmpeg dla Windows i podaj skryptowi ścieżkę do pliku ffmpeg.exe i folderu wideo w sekcji opcji.
Oba skrypty nie dotykają istniejących plików wideo, pozostają nietknięte.
Otrzymasz jednak kilka nowych plików w tym samym miejscu, w którym znajdują się Twoje filmy wejściowe
- Plik dziennika na wideo z wyjściem konsoli dla obu używanych komend ffmpeg
- Plik CSV na wideo ze wszystkimi znacznikami czasu czarnych scen do ręcznego dostrajania
- Kilka nowych filmów w zależności od liczby wcześniej wykrytych czarnych scen
Pierwszy skrypt do uruchomienia: Detect_black.ps1
### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe" # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*" # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4") # Set which file extensions should be processed
$dur = 4 # Set the minimum detected black duration (in seconds)
$pic = 0.98 # Set the threshold for considering a picture as "black" (in percent)
$pix = 0.15 # Set the threshold for considering a pixel "black" (in luminance)
### Main Program ______________________________________________________________________________________________________
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){
### Set path to logfile
$logfile = "$($video.FullName)_ffmpeg.log"
### analyse each video with ffmpeg and search for black scenes
& $ffmpeg -i $video -vf blackdetect=d=`"$dur`":pic_th=`"$pic`":pix_th=`"$pix`" -an -f null - 2> $logfile
### Use regex to extract timings from logfile
$report = @()
Select-String 'black_start:.*black_end:' $logfile | % {
$black = "" | Select start, end, cut
# extract start time of black scene
$start_s = $_.line -match '(?<=black_start:)\S*(?= black_end:)' | % {$matches[0]}
$start_ts = [timespan]::fromseconds($start_s)
$black.start = "{0:HH:mm:ss.fff}" -f ([datetime]$start_ts.Ticks)
# extract duration of black scene
$end_s = $_.line -match '(?<=black_end:)\S*(?= black_duration:)' | % {$matches[0]}
$end_ts = [timespan]::fromseconds($end_s)
$black.end = "{0:HH:mm:ss.fff}" -f ([datetime]$end_ts.Ticks)
# calculate cut point: black start time + black duration / 2
$cut_s = ([double]$start_s + [double]$end_s) / 2
$cut_ts = [timespan]::fromseconds($cut_s)
$black.cut = "{0:HH:mm:ss.fff}" -f ([datetime]$cut_ts.Ticks)
$report += $black
}
### Write start time, duration and the cut point for each black scene to a seperate CSV
$report | Export-Csv -path "$($video.FullName)_cutpoints.csv" –NoTypeInformation
}
Jak to działa
Pierwszy skrypt iteruje wszystkie pliki wideo, które pasują do określonego rozszerzenia i nie pasują do wzorca *_???.*
, ponieważ nazwano nowe rozdziały wideo <filename>_###.<ext>
i chcemy je wykluczyć.
Przeszukuje wszystkie czarne sceny i zapisuje znacznik czasu rozpoczęcia i czas trwania czarnej sceny do nowego pliku CSV o nazwie <video_name>_cutpoints.txt
To również oblicza punkty cięcia, jak pokazano: cutpoint = black_start + black_duration / 2
. Później wideo jest dzielone na segmenty według tych znaczników czasu.
Plik cutpoints.txt dla Twojego przykładowego wideo pokaże:
start end cut
00:03:56.908 00:04:02.247 00:03:59.578
00:08:02.525 00:08:10.233 00:08:06.379
Po uruchomieniu można ręcznie manipulować punktami cięcia. Po ponownym uruchomieniu skryptu wszystkie stare treści zostaną zastąpione. Zachowaj ostrożność podczas ręcznej edycji i zapisz swoją pracę w innym miejscu.
Dla przykładowego wideo komenda ffmpeg do wykrywania czarnych scen to
$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null
Istnieją 3 ważne liczby, które można edytować w sekcji opcji skryptu
d=4
oznacza, że wykrywane są tylko czarne sceny dłuższe niż 4 sekundy
pic_th=0.98
to próg uznawania obrazu za „czarny” (w procentach)
pix=0.15
ustawia próg uznawania piksela za „czarny” (w luminancji). Ponieważ masz stare filmy VHS, nie masz w nich całkowicie czarnych scen. Domyślna wartość 10 nie zadziała i musiałem nieznacznie zwiększyć próg
Jeśli coś pójdzie nie tak, sprawdź odpowiedni plik dziennika o nazwie <video_name>__ffmpeg.log
. Jeśli brakuje następujących linii, zwiększ liczby wymienione powyżej, aż wykryjesz wszystkie czarne sceny:
[blackdetect @ 0286ec80]
black_start:236.908 black_end:242.247 black_duration:5.33877
Drugi skrypt do uruchomienia: cut_black.ps1
### Options __________________________________________________________________________________________________________
$ffmpeg = ".\ffmpeg.exe" # Set path to your ffmpeg.exe; Build Version: git-45581ed (2014-02-16)
$folder = ".\Videos\*" # Set path to your video folder; '\*' must be appended
$filter = @("*.mov","*.mp4") # Set which file extensions should be processed
### Main Program ______________________________________________________________________________________________________
foreach ($video in dir $folder -include $filter -exclude "*_???.*" -r){
### Set path to logfile
$logfile = "$($video.FullName)_ffmpeg.log"
### Read in all cutpoints from *_cutpoints.csv; concat to string e.g "00:03:23.014,00:06:32.289,..."
$cuts = ( Import-Csv "$($video.FullName)_cutpoints.csv" | % {$_.cut} ) -join ","
### put together the correct new name, "%03d" is a generic number placeholder for ffmpeg
$output = $video.directory.Fullname + "\" + $video.basename + "_%03d" + $video.extension
### use ffmpeg to split current video in parts according to their cut points
& $ffmpeg -i $video -f segment -segment_times $cuts -c copy -map 0 $output 2> $logfile
}
Jak to działa
Drugi skrypt iteruje wszystkie pliki wideo w taki sam sposób, jak pierwszy skrypt. Odczytuje tylko wycięte znaczniki czasu z odpowiedniego cutpoints.txt
filmu.
Następnie zestawia odpowiednią nazwę pliku dla plików rozdziałów i nakazuje ffmpeg segmentację wideo. Obecnie filmy są dzielone na części bez ponownego kodowania (superszybkie i bezstratne). Z tego powodu mogą występować niedokładności 1-2s ze znacznikami czasu punktu przecięcia, ponieważ ffmpeg może wycinać tylko w klatkach kluczowych. Ponieważ po prostu kopiujemy i nie kodujemy ponownie, nie możemy samodzielnie wstawiać klatek kluczowych.
Poleceniem dla przykładowego wideo byłoby
$ffmpeg -i "Tape_10_3b.mp4" -f segment -segment_times "00:03:59.578,00:08:06.379" -c copy -map 0 "Tape_10_3b_(%03d).mp4"
Jeśli coś pójdzie nie tak, spójrz na odpowiedni plik ffmpeg.log
Bibliografia
Do zrobienia
Zapytaj OP, czy format CSV jest lepszy niż plik tekstowy jako plik punktu cięcia, abyś mógł edytować je w programie Excel nieco łatwiej
»Wdrożony
Zaimplementuj sposób formatowania znaczników czasu jako [hh]: [mm]: [ss], [milisekund] zamiast tylko sekund
»Zaimplementowano
Zaimplementuj polecenie ffmpeg, aby utworzyć pliki mozaikowe png dla każdego rozdziału
»Zaimplementowane
Sprawdź, czy -c copy
wystarczy scenariusz OP, czy też musimy w pełni ponownie zakodować.
Wygląda na to, że Ryan już to robi .
Oto bonus: Ten trzeci skrypt PowerShell generuje obraz mozaiki miniatury
Otrzymasz jeden obraz na rozdział wideo, z których wszystkie wyglądają jak na poniższym przykładzie.
Główną ideą jest uzyskanie ciągłego strumienia obrazów w całym wideo. Robimy to z opcją wyboru ffmpeg .
Najpierw pobieramy całkowitą liczbę klatek za pomocą genialnej metody (np. 2000) i dzielimy ją przez naszą domyślną liczbę miniatur (np. 5 x 4 = 20). Od tego czasu chcemy generować jeden obraz na 100 klatek
2000 / 20 = 100
Wynikowa komenda ffmpeg do wygenerowania miniatury mogłaby wyglądać
W powyższym kodzie widać 9 różnych
-vf
kombinacji składających się zselect=not(mod(n\,XXX))
gdzie XXX to obliczona liczba klatek na sekundęthumbnail
który automatycznie wybiera najbardziej reprezentatywne klatkiselect='gt(scene\,XXX)
+-vsync vfr
gdzie XXX to próg, z którym musisz się bawićmpdecimate
- Usuń prawie zduplikowane ramki. Dobry na czarne scenyyadif
- Usuń przeplot obrazu wejściowego. Nie wiem dlaczego, ale działaWersja 3 jest moim zdaniem najlepszym wyborem. Wszystkie pozostałe są komentowane, ale nadal możesz je wypróbować. Byłem w stanie usunąć większość rozmytych miniatur za pomocą
mpdecimate
,yadif
iselect=not(mod(n\,XXX))
. Tak!Dla twojego przykładowego wideo otrzymuję te zapowiedzi
Kliknij, aby powiększyć
Kliknij, aby powiększyć
Przesłałem wszystkie miniatury utworzone przez te wersje. Obejrzyj je, aby uzyskać pełne porównanie.
źródło
select='gt(scene\,0.4)'
do pracy. Nie mogłem też zabrać sięfps="fps=1/600"
do pracy. Nawiasem mówiąc, czy masz pojęcie o superuser.com/questions/725734 ? Dziękuję bardzo za całą waszą pomoc. Bardzo się cieszę, że pomagam mojej rodzinie odkryć mnóstwo starych wspomnień.Doceń oba skrypty, świetny sposób na dzielenie filmów.
Miałem jednak pewne problemy z tym, że filmy nie wyświetlały później właściwego czasu i nie byłem w stanie przejść do określonego czasu. Jednym z rozwiązań było demultipleksowanie, a następnie zmiksowanie strumieni przy użyciu Mp4Box.
Innym, łatwiejszym dla mnie sposobem było użycie mkvmerge do dzielenia. Dlatego oba skrypty musiały zostać zmienione. W przypadku wykrycia_black.ps1 tylko opcja „* .mkv” musiała zostać dodana do opcji filtru, ale niekoniecznie, jeśli zaczynasz tylko z plikami mp4.
Funkcja jest taka sama, ale jak dotąd nie ma problemów z czasem wideo. Dzięki za inspirację.
źródło
Mam nadzieję, że się mylę, ale nie sądzę, że to, o co pytasz, jest możliwe. Może być to możliwe podczas ponownego kodowania wideo, ale jako „prosty bezstratny wycinek” nie znam żadnego sposobu.
Wiele programów do edycji wideo będzie miało funkcję automatycznej analizy lub coś równoważnego, co może być w stanie podzielić twoje filmy na czarne ramki, ale będzie to wymagać ponownego kodowania wideo.
Powodzenia!
źródło