Aquí hay dos scripts de PowerShell para dividir videos largos en capítulos más pequeños por escenas negras.
Guárdelos como Detect_black.ps1 y Cut_black.ps1. Descarga ffmpeg para Windows y dile al script la ruta a tu ffmpeg.exe y a tu carpeta de video en la sección de opciones.
Ambas secuencias de comandos no tocarán los archivos de video existentes, permanecen intactos.
Sin embargo, obtendrá un par de archivos nuevos en el mismo lugar donde están sus videos de entrada
- Un archivo de registro por video con la salida de la consola para ambos comandos ffmpeg utilizados
- Un archivo CSV por video con todas las marcas de tiempo de escenas negras para un ajuste manual
- Un par de videos nuevos dependiendo de cuántas escenas negras se detecten previamente
Primer script que se ejecuta: 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
}
Como funciona
El primer script recorre todos los archivos de video que coinciden con una extensión especificada y no coinciden con el patrón *_???.*
, ya que se nombraron nuevos capítulos de video <filename>_###.<ext>
y queremos excluirlos.
Busca en todas las escenas negras y escribe la marca de tiempo de inicio y la duración de la escena negra en un nuevo archivo CSV llamado <video_name>_cutpoints.txt
También calcula puntos de corte como se muestra: cutpoint = black_start + black_duration / 2
. Más tarde, el video se segmenta en estas marcas de tiempo.
El archivo cutpoints.txt para su video de muestra mostrará:
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
Después de una carrera, puede manipular los puntos de corte manualmente si lo desea. Si ejecuta el script nuevamente, todo el contenido antiguo se sobrescribe. Tenga cuidado al editar manualmente y guardar su trabajo en otro lugar.
Para el video de muestra, el comando ffmpeg para detectar escenas negras es
$ffmpeg -i "Tape_10_3b.mp4" -vf blackdetect=d=4:pic_th=0.98:pix_th=0.15 -an -f null
Hay 3 números importantes que se pueden editar en la sección de opciones del script
d=4
significa que solo se detectan escenas negras de más de 4 segundos
pic_th=0.98
es el umbral para considerar una imagen como "negra" (en porcentaje)
pix=0.15
establece el umbral para considerar un píxel como "negro" (en luminancia). Como tiene videos antiguos de VHS, no tiene escenas completamente negras en sus videos. El valor predeterminado 10 no funcionará y tuve que aumentar ligeramente el umbral
Si algo sale mal, verifique el archivo de registro correspondiente llamado <video_name>__ffmpeg.log
. Si faltan las siguientes líneas, aumente los números mencionados anteriormente hasta que detecte todas las escenas negras:
[blackdetect @ 0286ec80]
black_start:236.908 black_end:242.247 black_duration:5.33877
Segundo script que se ejecuta: 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
}
Como funciona
El segundo script itera sobre todos los archivos de video de la misma manera que lo hizo el primer script. Lee solo las marcas de tiempo cortadas del correspondiente cutpoints.txt
de un video.
Luego, reúne un nombre de archivo adecuado para los archivos de capítulos y le dice a ffmpeg que segmente el video. Actualmente, los videos se cortan sin volver a codificar (superrápidos y sin pérdidas). Debido a esto, puede haber una imprecisión de 1-2 segundos con las marcas de tiempo del punto de corte porque ffmpeg solo puede cortar en fotogramas clave. Como solo copiamos y no volvemos a codificar, no podemos insertar fotogramas clave por nuestra cuenta.
El comando para el video de muestra sería
$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"
Si algo sale mal, eche un vistazo al ffmpeg.log correspondiente
Referencias
Que hacer
Pregunte a OP si el formato CSV es mejor que un archivo de texto como archivo de punto de corte, para que pueda editarlos con Excel un poco más fácil
»Implementado
Implemente una forma de formatear marcas de tiempo como [hh]: [mm]: [ss], [milisegundos] en lugar de solo segundos
»Implementado
Implemente un comando ffmpeg para crear archivos png mosaik para cada capítulo
»Implementado
Elabore si -c copy
es suficiente para el escenario de OP o si necesitamos volver a codificar por completo.
Parece que Ryan ya está en eso .
Aquí está la ventaja: este tercer script de PowerShell genera una imagen en miniatura de mosaico
Recibirá una imagen por video de capítulo que se verá como en el siguiente ejemplo.
La idea principal es obtener un flujo continuo de imágenes a través del video completo. Hacemos esto con la opción de selección de ffmpeg .
Primero, recuperamos el recuento total de cuadros con un método ingenioso (por ejemplo, 2000) y lo dividimos a través de nuestro conteo de miniaturas predeterminado (por ejemplo, 5 x 4 = 20). Así que queremos generar una imagen cada 100 cuadros desde
2000 / 20 = 100
El comando ffmpeg resultante para generar la miniatura podría verse así
En el código anterior, ves 9
-vf
combinaciones diferentes que consisten enselect=not(mod(n\,XXX))
donde XXX es una velocidad de fotogramas calculadathumbnail
que selecciona los cuadros más representativos automáticamenteselect='gt(scene\,XXX)
+-vsync vfr
donde XXX es un umbral con el que tienes que jugarmpdecimate
- Eliminar cuadros casi duplicados. Bueno contra escenas negrasyadif
- Desentrelazar la imagen de entrada. No sé por qué, pero funciona.La versión 3 es la mejor opción en mi opinión. Todos los demás están comentados, pero aún puedes probarlos. Pude eliminar la mayoría de las miniaturas borrosas usando
mpdecimate
,yadif
yselect=not(mod(n\,XXX))
. ¡Si!Para su video de muestra , obtengo estas vistas previas
Click para agrandar
Click para agrandar
Subí todas las miniaturas creadas por esas versiones. Eche un vistazo a ellos para una comparación completa.
fuente
select='gt(scene\,0.4)'
al trabajo. Y tampoco pude irfps="fps=1/600"
a trabajar. Por cierto, ¿tienes alguna idea sobre superuser.com/questions/725734 ? Muchas gracias por toda su ayuda. Estoy muy emocionado por ayudar a mi familia a descubrir toneladas de viejos recuerdos.Aprecio ambos guiones, excelente manera de dividir videos.
Aún así, tuve algunos problemas con los videos que no mostraban la hora correcta después y no pude saltar a una hora específica. Una solución fue demux y luego mux las transmisiones usando Mp4Box.
Otra forma más fácil para mí era usar mkvmerge para dividir. Por lo tanto, ambos scripts tuvieron que ser alterados. Para detect_black.ps1, solo se debía agregar "* .mkv" a la opción de filtro, pero eso no es necesariamente si comienza solo con archivos mp4.
La función es la misma, pero hasta ahora no hay problemas con los tiempos de video. Gracias por la inspiracion.
fuente
Espero estar equivocado, pero no creo que lo que pidas sea posible. Puede ser posible mientras se vuelve a codificar el video, pero como un "segmento simple sin pérdidas" no sé de ninguna manera.
Muchos programas de edición de video tendrán una función de análisis automático o algo equivalente que puede dividir sus videos en cuadros negros, pero esto requerirá una nueva codificación del video.
¡Buena suerte!
fuente