Divide automaticamente i file video di grandi size .mov in file più piccoli in fotogrammi neri (cambiamenti di scena)?

Ho 65 file video di media denominati tape_01.mov, tape_02.mov, …, tape_65.mov. Ognuno è di circa 2,5 ore e occupa molti gigabyte. Sono copie digitali di quello che sono stati i nastri VHS ("home movies" della mia famiglia).

Ogni file video .mov di 2,5 ore contiene molti "capitoli" (nel senso che a volte la scena termina in una dissolvenza a uno schermo nero e poi scompare una nuova scena).

Il mio objective principale è avere i 65 file di grandi size affiancati in file più piccoli per capitolo. E voglio che questo sia fatto automaticamente attraverso la rilevazione delle cornici nere tra "scene".

Potrei usare ffmpeg (o qualsiasi altra cosa) per passare nei 65 nomi di file originali e farlo automaticamente iterare su each video e tagliarla, nominando i pezzi risultanti tape_01-ch_01.mov, tape_01-ch_02.mov, tape_01-ch_03.mov, eccetera? E voglio che farlo senza ricodifica (voglio che sia una semplice fetta senza perdita).

Come posso fare questo?

Ecco i passaggi che voglio:

  1. Iterate su una cartella di file .mov (named tape_01.mov, tape_02.mov, …, tape_65.mov) per esportrli come file mp4 (denominati tape_01.mp4, tape_02.mp4, …, tape_65.mp4) . Voglio che le impostazioni di compressione siano facili da configurare per questo passaggio. I file .mov originali devono ancora esistere dopo che i file .mp4 vengono creati.
  2. Iterate sui file mp4: per each file MP4, generare un file di text che specifica l'ora di inizio e la durata dei segmenti neri tra le "scene". Ogni MP4 può avere 0 o più di queste interruzioni di scena nere. L'ora di inizio e la durata dovrebbero essere precisi per una frazione di secondo. HH: MM: Il formato SS non è abbastanza preciso.
  3. Sarò quindi in grado di controllare manualmente questi file di text per vedere se identificano correttamente le modifiche della scena (segmenti neri). Posso regolare i tempi se voglio.
  4. Un altro script leggerà each file mp4 e il suo file txt e divide (in modo super veloce e senza perdita) il MP4 in tante parti come necessario sulla base delle righe del file di text (i tempi indicati). Le divisioni dovrebbero avvenire al centro di each segmento nero. Ecco un esempio di file mp4 (con audio rimosso per scopi di riservatezza) che ha cambiamenti di scena fade-to-black. I file mp4 originale dovrebbero rimanere incontaminati quando vengono creati i file mp4 più piccoli. I file più piccoli di mp4 dovrebbero avere lo schema di denominazione di tape_01_001.mp4, tape_01_002.mp4, tape_01_003.mp4, ecc.
  5. Bonus! Sarebbe impressionante se un altro script potesse allora iterare sui file di piccole size e generare un'image .png per ognuno di essi che è un mosaico di screenshot come questa persona sta cercando: Tabelle significative per un video utilizzando FFmpeg

Mi piacerebbe che questi script siano script di shell che posso eseguire in Mac, Ubuntu e Windows Git Bash.

Ecco due script PowerShell per dividere video lunghi in capitoli più piccoli da scene nere.

Salvalo come Detect_black.ps1 e Cut_black.ps1. Scaricare ffmpeg per Windows e indicare lo script il path del tuo ffmpeg.exe e della tua cartella video sotto la sezione delle opzioni.

immettere qui la descrizione dell'immagine

Entrambi gli script non toccheranno i file video esistenti, restano intatti.
Tuttavia, otterrai un paio di nuovi file nello stesso luogo in cui sono i tuoi video di input

  • Un file di registro per video con l'output della console per entrambi i comandi utilizzati ffmpeg
  • Un file CSV per video con tutti i timestamp di scene nere per la sintonizzazione manuale
  • Un paio di nuovi video a seconda di quante scene nere sono state rilevate in precedenza

immettere qui la descrizione dell'immagine

Primo script da eseguire: 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 } 

Come funziona

Il primo script si estende attraverso tutti i file video che corrispondono a un'estensione specificata e non corrispondono al pattern *_???.* Poiché i nuovi capitoli video sono stati denominati <filename>_###.<ext> e vogliamo escludere loro.

<video_name>_cutpoints.txt ricerca di tutte le scene nere e scrive il timestamp di inizio e la durata della scena nera in un nuovo file CSV denominato <video_name>_cutpoints.txt

Inoltre calcola i punti di taglio come mostrato: cutpoint = black_start + black_duration / 2 . Più tardi, il video viene segmentato in questi timestamps.

Il file cutpoints.txt per il video di esempio mostrerebbe:

 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 

Dopo una corsa, è ansible manipolare manualmente i punti di taglio se desiderato. Se si esegue nuovamente lo script, tutti i vecchi contenuti vengono sovrascritti. Fare attenzione quando si modifica manualmente e salva il lavoro altrove.

Per il video di esempio il command ffmpeg per rilevare scene nere è

 $ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null 

Ci sono 3 numbers importnti che sono modificabili nella sezione di opzioni del script

  • d=4 significa solo scene nere più lunghe di 4 secondi
  • pic_th=0.98 è la soglia per considerare un'image come "nera" (in percentuale)
  • pix=0.15 imposta la soglia per considerare un pixel come "nero" (in luminanza). Poiché hai vecchi video VHS, non hai scene completamente nere nei tuoi video. Il valore predefinito 10 non funziona e ho dovuto aumentare leggermente la soglia

Se qualcosa va storto, controllare il logfile corrispondente chiamato <video_name>__ffmpeg.log . Se mancano le seguenti righe, aumenta i numbers sopra indicati fino a individuare tutte le scene nere:

 [blackdetect @ 0286ec80] black_start:236.908 black_end:242.247 black_duration:5.33877 

Secondo script da eseguire: 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 eg "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 } 

Come funziona

Il secondo script si estende su tutti i file video nello stesso modo in cui il primo script ha fatto. Legge solo i timestamps tagliati dai corrispondenti cutpoints.txt di un video.

Successivamente, mette insieme un nome di file appropriato per i file capitolo e dice a ffmpeg di segmentare il video. Attualmente i video vengono tagliati senza ricodifica (superfast e lossless). A causa di questo, potrebbe esserci un impreciso di 1-2s con timestamps del punto di taglio perché ffmpeg può solo tagliare a key_frames. Poiché abbiamo solo copiato e non ricodifica, non possiamo inserire i key_frames da soli.

Il command per il video di esempio sarebbe

 $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" 

Se qualcosa va storto, osserva il corrispondente ffmpeg.log

Riferimenti

  • Scaricamento FFmpeg per Windows
  • Documentazione di segmento : i segmenti_sisthemes e gli ambiti segmentari sono opzioni

Fare

  • Chiedi OP se il formato CSV è migliore di un file di text come file di taglio, in modo da poterli modificare con Excel un po 'più facile
    »Attuato
  • Implementare un modo per formattare i timestamps come [hh]: [mm]: [ss], [millisecondi] piuttosto che solo secondi
    »Attuato
  • Implementare un command ffmpeg per creare file mpg png per each capitolo
    »Attuato
  • Elaborare se -c copy è sufficiente per lo scenario di OP o di che abbiamo bisogno di ri-codificare completamente.
    Sembra che Ryan sia già su di esso .

Ecco il bonus: questo script 3rd PowerShell genera un'image in miniatura di mosaico

Riceverai un'image per each capitolo video che sembra l'esempio riportto di seguito.

 ### 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 $x = 5 # Set how many images per x-axis $y = 4 # Set how many images per y-axis $w = 384 # Set thumbnail width in px (full image width: 384px x 5 = 1920px,) ### Main Program ______________________________________________________________________________________________________ foreach ($video in dir $folder -include "*_???.mp4" -r){ ### get video length in frames, an ingenious method $log = & $ffmpeg -i $video -vcodec copy -an -f null $video 2>&1 $frames = $log | Select-String '(?<=frame=.*)\S+(?=.*fps=)' | % { $_.Matches } | % { $_.Value } $frame = [Math]::floor($frames / ($x * $y)) ### put together the correct new picture name $output = $video.directory.Fullname + "\" + $video.basename + ".jpg" ### use ffmpeg to create one mosaic png per video file ### Basic explanation for -vf options: http://trac.ffmpeg.org/wiki/FilteringGuide #1 & $ffmpeg -y -i $video -vf "select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output #2 & $ffmpeg -y -i $video -vf "yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select=not(mod(n\,`"$frame`")),scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output #4 & $ffmpeg -y -i $video -vf "select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output #5 & $ffmpeg -y -i $video -vf "yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output #6 & $ffmpeg -y -i $video -vf "mpdecimate,yadif,select='gt(scene\,0.06)',scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 -vsync vfr $output #7 & $ffmpeg -y -i $video -vf "thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output #8 & $ffmpeg -y -i $video -vf "yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output #9 & $ffmpeg -y -i $video -vf "mpdecimate,yadif,thumbnail,scale=`"$w`":-1,tile=`"$x`"x`"$y`"" -frames:v 1 $output } 

L'idea principale è quella di get un stream continuo di immagini sul video completo. Lo facciamo con l'opzione di selezione di ffmpeg.

In primo luogo, recupebranch il count totale del fotogramma con un metodo ingegnoso (es. 2000) e dividerlo attraverso il nostro numero di anteprima predefinito (es. 5 x 4 = 20). Quindi, vogliamo generare un'image each 100 fotogrammi dal 2000 / 20 = 100

Il command ffmpeg generato per generare la miniatura potrebbe essere simile

 ffmpeg -y -i input.mp4 -vf "mpdecimate,yadif,select=not(mod(n\,100)),scale=384:-1,tile=5x4" -frames:v 1 output.png 

Nel codice di cui sopra, si vedono 9 diverse combinazioni -vf consistenti di

  • select=not(mod(n\,XXX)) where XXX è un framerato calcolato
  • thumbnail che seleziona automaticamente i fotogrammi più rappresentativi
  • select='gt(scene\,XXX) + -vsync vfr where XXX è una soglia con cui devi giocare
  • mpdecimate – rimuove i fotogrammi quasi duplicati. Buono contro scene nere
  • yadif – Deinterlace l'image di ingresso. Non so perché, ma funziona

La versione 3 è la scelta migliore a mio parere. Tutti gli altri sono commentati, ma puoi ancora provarli. Ho potuto rimuovere la maggior parte delle miniature sfocate usando mpdecimate , yadif e select=not(mod(n\,XXX)) . Si!

Per il tuo video di esempio ottengo queste anteprime

immettere qui la descrizione dell'immagine
clicca per ingrandire

immettere qui la descrizione dell'immagine
clicca per ingrandire

Ho caricato tutte le miniature create da quelle versioni. Date un'occhiata a loro per un confronto completo.

Apprezzate entrambi gli script, ottimo modo per dividere i video.

Tuttavia, ho avuto alcuni problemi con i video che non hanno mostrato tempo corretto in seguito e non sono stato in grado di saltare a un tempo specifico. Una soluzione è stata quella di demux e poi mux i flussi utilizzando Mp4Box.

Un altro modo più semplice per me è stato quello di utilizzare mkvmerge per la divisione. Pertanto, entrambi gli scripts wherevano essere alterati. Per detect_black.ps1, solo l'opzione "* .mkv" whereva essere aggiunta all'opzione del filter, ma non è necessariamente se si inizia solo con i file mp4.

 ### Options $mkvmerge = ".\mkvmerge.exe" # Set path to mkvmerge.exe $folder = ".\*" # Set path to your video folder; '\*' must be appended $filter = @("*.mov", "*.mp4", "*.mkv") # 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 eg "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 mkvmerge $output = $video.directory.Fullname + "\" + $video.basename + "-%03d" + ".mkv" ### use mkvmerge to split current video in parts according to their cut points and convert to mkv & $mkvmerge -o $output --split timecodes:$cuts $video } 

La function è la stessa, ma nessun problema con i tempi video finora. Grazie per l'ispirazione.

Spero che mi sbaglio, ma non credo che la tua richiesta sia ansible. Potrebbe essere ansible durante la nuova codifica del video, ma come una "fetta semplice senza perdita" non so di alcun modo.

Molti programmi di editing video avranno una function di analisi automatica o qualcosa di equivalente che potrebbe essere in grado di dividere i tuoi video nei frame neri, ma questo richiederà la nuova codifica del video.

In bocca al lupo!