¿Cómo recortar marcos negros con ffmpeg en windows?

Tengo un video corto y estoy tratando de recortar todos los marcos en blanco o casi en blanco. Este no es un caso en el que me importen las pausas en un video que podría estar oscuro. Literalmente quiero eliminar todos los marcos negros cercanos.

Lo que no me di cuenta con ffmpeg al principio fue que los filtros 'blackframe' o 'blackdetect' es que en realidad no filtraban. Simplemente parecen mostrarle lo que posiblemente puede filtrar, con otro(s) comando(s).

¿Cuál es la mejor manera en 'Windows' para filtrar los marcos encontrados resultantes?

Entonces, con ffmpeg, en realidad estoy concat-(ting) el avance y el reverso del mismo archivo que estoy ejecutando algunos filtros en esos archivos. Ver mi comando actual:

ffmpeg.exe -y -i "2013-10-14_14-30-55.mov" -filter_complex "[0:v]transpose=3,split[tp][tp2];[tp]reverse[vr];[tp2][vr]concat=n=2:v=1:a=0[vbf];[vbf]blackframe=98:32[v]" -map "[v]" -vcodec wmv2 -q 10 -trellis 2 -r 16 "g:\gd5.wmv"

Luego obtengo esta salida: (Solo el final de la misma de todos modos...)

[Parsed_blackframe_4 @ 00000000003bf320] frame:650 pblack:99 pts:21821999 t:21.821999 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:651 pblack:99 pts:21855366 t:21.855366 type:P last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:652 pblack:99 pts:21888733 t:21.888733 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:653 pblack:99 pts:21922099 t:21.922099 type:B last_keyframe:639
[Parsed_blackframe_4 @ 00000000003bf320] frame:654 pblack:99 pts:21955466 t:21.955466 type:I last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:655 pblack:99 pts:21988833 t:21.988833 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:656 pblack:99 pts:22022199 t:22.022199 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:657 pblack:99 pts:22055566 t:22.055566 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:658 pblack:99 pts:22088933 t:22.088933 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:659 pblack:99 pts:22122299 t:22.122299 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:660 pblack:99 pts:22155666 t:22.155666 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:661 pblack:99 pts:22189033 t:22.189033 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:662 pblack:99 pts:22222399 t:22.222399 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:663 pblack:99 pts:22255766 t:22.255766 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:664 pblack:99 pts:22289133 t:22.289133 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:665 pblack:99 pts:22322499 t:22.322499 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:666 pblack:99 pts:22355866 t:22.355866 type:P last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:667 pblack:99 pts:22389233 t:22.389233 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:668 pblack:99 pts:22422599 t:22.422599 type:B last_keyframe:654
[Parsed_blackframe_4 @ 00000000003bf320] frame:669 pblack:99 pts:22455966 t:22.455966 type:I last_keyframe:669
frame=  362 fps= 14 q=10.0 Lsize=    9004kB time=00:00:22.62   bitrate=3260.2kbits/s dup=1 drop=309
video:8922kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.918756%

Entonces, ¿qué hago con esta información de marco negro en Windows? ¿O hay un método mejor con el filtro 'blackdetect'?

Respuestas (2)

He aquí una forma de hacerlo con los filtros blackdetecty .trim

Primero, una mejor manera de obtener la blackdetectsalida es a través ffprobede porque es capaz de escribir datos estructurados como XML, JSONo uno key=valuepor línea.

Haces esto usando la inyección de metadatos:

ffprobe -f lavfi -i "movie=/path/to/input.mp4,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet TAG:lavfi.black_start=0 TAG:lavfi.black_end=5.42208 TAG:lavfi.black_start=73.4067

Como puede ver, devuelve la hora de inicio y finalización de cada sección negra. Utiliza la información devuelta a trimlos segmentos entre cada uno endy el siguiente start:

ffmpeg -i /path/to/input.mp4 -filter_complex "[0:v]trim=start=5.42208:end=73.4067,setpts=PTS-STARTPTS[v1];[0:a]atrim=start=5.42208:end=73.4067,asetpts=PTS-STARTPTS[a1]" -map [v1] -map [a1] output.mp4

Puede encadenar varios trimfiltros en el mismo comando. Vea esta respuesta para un ejemplo detallado.

Para automatizar esto en Windows, puede instalar y usar PowerShell u otro lenguaje de secuencias de comandos de su elección (PHP, Python, etc.).

Su ayuda es apreciada y parece ser el camino correcto. Sin embargo, probé el comando ffprobe que sugirió: ffprobe -f lavfi -i "movie=2013-10-14_14-30-55.mov,blackdetect[out0]" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quietpero no sale ninguna entrada. ¿Puede ser porque los marcos en cuestión no son lo suficientemente negros o lo estoy haciendo mal? No entiendo algunos de los comandos.
@RobertKoernke Usé la configuración predeterminada blackdetect, verifique sus argumentos y el ejemplo en ffmpeg.org/ffmpeg-filters.html#blackdetect . Asegúrese de tener también una versión reciente ffmpegy no una instalación de distribución predeterminada.
El comando significa: hacer blackdetect, mostrar solo black_starty black_endetiquetas, usar el formato -of defaultde salida predeterminado sin envoltorios nw=1y suprimir todas las demás salidas-v quiet
por cierto... ¿Me estás dando instrucciones de Windows correctas? Ok, quité temporalmente el -v quiety obtengo esto: [Parsed_movie_0 @ 0056f560] Error al inicializar avformat_open_input 'C' [lavfi @ 0056e2a0] Error al inicializar el filtro 'película' con argumentos 'C:UsersDOCUME~12013-10-14_14-30-55. mov' movie=C:\Users\DOCUME~1\2013-10-14_14-30-55.mov,blackdetect=d=0:pix_th=51.80[out0]: No existe tal archivo o directorio`
El -v quietestaba suprimiendo los mensajes de error.
Lo tengo... Puse el video en el mismo directorio y lo ejecuté, y está funcionando. Así que seguiré probando. Gracias.
He probado muchos elementos diferentes en mis pruebas. Muchas veces obtengo 'archivo no encontrado' cuando claramente el archivo está allí. Como he demostrado, he probado tipos de archivos DOS antiguos. Qué está causando el problema aquí: "movie=C:\Users\INSPIR~1\DOCUME~1\a2d2\A2D2DE~1\MAC_SP~1.com\videos\2013-10-14_14-30-55.mov,blackdetect [fuera0]"
Cuando usa una ruta dentro de un argumento de comando, debe escapar de los caracteres especiales o citarla, o usar rutas más cortas y simples. ffmpeg.org/ffmpeg-utils.html#toc-Cotización y escape
Ningún escape \\..\\ parece funcionar. Continúa mostrando el error: Error initializing filter 'movie' with args- La alternativa que voy a usar es copiar en el directorio local ffprobe/ffmpeg como un archivo temporal, procesarlo allí, y luego muevo la versión procesada donde la necesito. Gracias.
Finalmente descubrí el escape aquí. Finalmente lo necesitaba: "movie=\'C:\\Users\\Inspiron 560\\Documents\\temp.wmv\',blackdetect[out0]" Así... La página de ayuda vinculada anteriormente era vaga y no mostrar el camino para ffprobe-movie.

He creado un script de Python que hará todo esto. Genera los comandos ffprobey ffmpegy los ejecuta Y genera un archivo sin clips negros

Repositorio de GitHub: https://github.com/FarisHijazi/deblack

Gist de GitHub (archivado): https://gist.github.com/FarisHijazi/eff7a7979440faa84a63657e085ec504

"""
@author: https://github.com/FarisHijazi
Use ffprobe to extract black frames and ffmpeg to trim them and output a new video
"""

# help from:
# https://video.stackexchange.com/questions/16564/how-to-trim-out-black-frames-with-ffmpeg-on-windows#new-answer?newreg=d534934be5774bd1938b535cd76608cd
# https://github.com/kkroening/ffmpeg-python/issues/184#issuecomment-493847192


import argparse
import os
import shlex
import subprocess

parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('input', type=str, help='input video file')
parser.add_argument('--invert', action='store_true', help='remove nonblack instead of removing black')
args = parser.parse_args()

##FIXME: sadly you must chdir so that the ffprobe command will work
os.chdir(os.path.split(args.input)[0])
args.input = os.path.split(args.input)[1]

spl = args.input.split('.')
outpath = '.'.join(spl[:-1]) + '.' + ('invert' if args.invert else '') + 'out.' + spl[-1]


def delete_back2back(l):
  from itertools import groupby
  return [x[0] for x in groupby(l)]


def construct_ffmpeg_trim_cmd(timepairs, inpath, outpath):
  cmd = f'ffmpeg -i "{inpath}" -y -filter_complex '
  cmd += '"'
  for i, (start, end) in enumerate(timepairs):
    cmd += (f"[0:v]trim=start={start}:end={end},setpts=PTS-STARTPTS,format=yuv420p[{i}v]; " +
            f"[0:a]atrim=start={start}:end={end},asetpts=PTS-STARTPTS[{i}a]; ")
  for i, (start, end) in enumerate(timepairs):
    cmd += f"[{i}v][{i}a]"
  cmd += f'concat=n={len(timepairs)}:v=1:a=1[outv][outa]'
  cmd += '"'
  cmd += f' -map [outv] -map [outa] "{outpath}"'
  return cmd


def get_blackdetect(inpath, invert=False):
  ffprobe_cmd = f"ffprobe -f lavfi -i \"movie={inpath},blackdetect[out0]\" -show_entries tags=lavfi.black_start,lavfi.black_end -of default=nw=1 -v quiet"
  print('ffprobe_cmd:', ffprobe_cmd)
  lines = subprocess.check_output(shlex.split(ffprobe_cmd)).decode('utf-8').split('\n')
  times = [float(x.split('=')[1].strip()) for x in delete_back2back(lines) if x]
  assert len(times), 'no black detected'

  if not invert:
    times = [0] + times[:-1]
  timepairs = [(times[i], times[i + 1]) for i in range(0, len(times) // 2, 2)]
  return timepairs


timepairs = get_blackdetect(args.input, invert=args.invert)
cmd = construct_ffmpeg_trim_cmd(timepairs, args.input, outpath)

print(cmd)
os.system(cmd)

Combinando múltiples respuestas juntas: