Как обрезать черные рамки с помощью ffmpeg на окнах?

У меня есть короткое видео, и я пытаюсь вырезать все пустые или почти пустые кадры. Это не тот случай, когда меня волнуют перерывы в видео, которые могут быть темными. Я буквально хочу убрать все почти черные рамки.

Что я сначала не понял с ffmpeg, так это то, что фильтры «черный кадр» или «черный детектор» на самом деле не фильтруют. Кажется, они просто показывают вам, что вы можете отфильтровать с помощью других команд.

Каков наилучший способ в «Windows» отфильтровать найденные кадры?

Таким образом, с помощью ffmpeg я на самом деле объединяю прямое и обратное направление одного и того же файла, чем запускаю некоторые фильтры для этих файлов. Смотрите мою текущую команду:

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"

Затем я получаю этот вывод: (В любом случае, это только конец...)

[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%

Итак, что мне делать с этой информацией о черном фрейме в Windows? Или есть лучший метод с фильтром 'blackdetect'?

Ответы (2)

Вот как это сделать с помощью фильтров blackdetectи .trim

Во-первых, лучший способ получить blackdetectвывод — через, ffprobeпотому что он способен записывать структурированные данные, такие как XML, JSONили по одному key=valueв строке.

Вы делаете это с помощью внедрения метаданных:

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

Как видите, он возвращает время начала и окончания каждой черной секции. Вы используете возвращенную информацию для trimсегментов между каждым endи следующим 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

Вы можете объединить несколько trimфильтров в одну команду. См. этот ответ для подробного примера.

Чтобы автоматизировать это в Windows, вы можете установить и использовать PowerShell или другой язык сценариев по вашему выбору (PHP, Python и т. д.).

Ваша помощь приветствуется, и кажется, что это правильный путь. Однако я попробовал предложенную вами команду ffprobe: 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 quietно записи вообще не появляются. Может ли это быть из-за того, что рассматриваемые кадры недостаточно черные, или я что-то не так делаю? Я не понимаю некоторые команды.
@RobertKoernke Я использовал настройки по умолчанию blackdetect, проверьте его аргументы и пример на ffmpeg.org/ffmpeg-filters.html#blackdetect . Убедитесь, что у вас также установлена ​​последняя версия, ffmpegа не установка дистрибутива по умолчанию.
Команда означает: делать blackdetect, показывать только black_startи black_endтеги, использовать формат вывода по умолчанию -of defaultбез оберток nw=1и подавлять весь остальной вывод .-v quiet
Кстати... Вы правильно даете мне инструкции для Windows? Хорошо, я временно снял -v quietи получаю следующее: [Parsed_movie_0 @ 0056f560] Failed to avformat_open_input 'C' [lavfi @ 0056e2a0] Ошибка инициализации фильтра 'movie' с аргументами '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]: Нет такого файла или каталога`
Подавление -v quietсообщений об ошибках.
Я понял... Я поместил видео в тот же каталог и запустил его, и оно работает. Так что я буду продолжать тестировать .. Спасибо
Я пробовал много разных предметов в своих тестах. Много раз я получаю сообщение «файл не найден», когда явно файл есть. Как я уже показал, я пробовал старые типы файлов DOS. Что вызывает проблему здесь: "movie=C:\Users\INSPIR~1\DOCUME~1\a2d2\A2D2DE~1\MAC_SP~1.com\videos\2013-10-14_14-30-55.mov,blackdetect [выход0]"
Когда вы используете путь внутри аргумента команды, вы должны экранировать специальные символы или заключать его в кавычки, или использовать более короткие и простые пути. ffmpeg.org/ffmpeg-utils.html#toc-Цитирование и экранирование
Похоже, что экранирование \\..\\ не работает. Он продолжает показывать ошибку: Error initializing filter 'movie' with args- Альтернативой является копирование в локальный каталог ffprobe/ffmpeg в качестве временного файла, обработка его там, а затем переместите обработанную версию туда, где она мне нужна. Спасибо.
Наконец-то разобрался, как сбежать отсюда. В конце концов мне это понадобилось: "movie=\'C:\\Users\\Inspiron 560\\Documents\\temp.wmv\',blackdetect[out0]" Вот так... Страница справки, указанная выше, была расплывчатой ​​и не укажи путь для ffprobe-movie.

Я собрал скрипт Python, который сделает все это. Генерирует команды ffprobeи ffmpegи запускает их И выводит файл без черных клипов

Репозиторий GitHub: https://github.com/FarisHijazi/deblack

GitHub суть (из архива): 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)

Объединение нескольких ответов вместе: