Как сопоставить кадр, извлеченный с помощью ffmpeg, и субтитры видео? (проблема точности кадра)

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

Я использую сценарий python с pysrt, чтобы открыть файл subrip и создать текстовые файлы. Что я делаю, так это то, что каждому кадру присваивается имя с номером кадра с помощью ffmpeg, а затем, поскольку они извлекаются с постоянной скоростью, я могу легко получить временную позицию кадра, используя формулу t1 = fnum/fps, где fnum - номер кадра кадр, полученный с именем файла, а fps - частота, передаваемая в ffmpeg для извлечения кадра.

Несмотря на то, что я использую тот же файл субтитров для извлечения позиций текста на временной шкале, который использовался в видео, я все равно получаю ошибки точности. В большинстве случаев у меня отсутствуют некоторые текстовые файлы или их не должно быть.

Поскольку время на самом деле не непрерывно, когда речь идет о кадрах, я попытался перекалибровать t, используя fps видео с жестко закодированными субтитрами, назовем это fps vfps для видео fps (я убедился, что частота кадров видео до и после субтитров одинакова. горение). Получаю формулу: t2 = int(t1*vfps)/vfps. Это все еще не на 100% точно.

Например, у меня видео с частотой 30 кадров в секунду (vfps=30), и я извлекал кадры со скоростью 4 кадра в секунду (fps=4). Извлеченный кадр 166 (fnum=166) не показывает субтитров. В файле subrip предыдущий субтитр заканчивается на t_prev=41.330, а следующий субтитр начинается с t_next=41.400, что означает, что t_sub должно удовлетворять: t_prev < t_sub and t_sub < t_next, но я не могу этого добиться.

Формулы, которые я пробовал:

t1 = fnum/fps  # 41.5 > t_next
t2 = int(fnum*vfps/fps)/vfps  # 41.5 > t_next
# is it because of a indexing problem? No:
t3 = (fnum-1)/fps  # 41.25 < t_prev
t4 = int((fnum-1)*vfps/fps)/vfps  # 41.23333333 < t_prev
t5 = int(fnum*vfps/fps - 1)/vfps  # 41.466666 > t_next
t6 = int((fnum-1)*vfps/fps + 1)/vfps  # 41.26666 < t_prev

Используемая команда:

# burning subtitles
# (previously)
# ffmpeg -r 25 -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# now:
ffmpeg -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# frames extraction
ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner

Почему это происходит и как я могу это решить?

Одна вещь, которую я заметил, заключается в том, что если я извлекаю кадры из исходного видео и субтитров, изменяю кадры, результатом становятся не только субтитры, но и вариации фона (этого не должно происходить). Если я проделаю один и тот же опыт, используя одно и то же видео два раза, разница будет нулевая, что означает, что извлечение кадра будет постоянным.

Код отличия:

ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner
ffmpeg -i no_sub.mp4 -vf fps=4 extracted_no_sub/%05.bmp -hide_banner
for img in no_sub/*.bmp; do
    convert extracted/${img##*/} $img -compose minus -composite diff/${img##*/}
done

Спасибо.


person Nick Skywalker    schedule 12.11.2019    source источник
comment
ffmpeg -r 25 -i nosub.mp4 - ›это изменит временные рамки кадров и уничтожит исходные временные метки. Вы не хотите этого делать, если не знаете, что делаете.   -  person Gyan    schedule 12.11.2019
comment
Не совсем ... Я хотел записать субтитры, не меняя частоту кадров. Итак, я сначала попробовал это: ffmpeg -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4, но он изменил частоту с 25 на 30, поэтому я вручную установил частоту кадров, и мне кажется, что это неправильно. Я не большой пользователь ffmpeg. Я искал в Google, как сохранить частоту кадров с помощью ffmpeg, и нашел этот заголовок superuser.com/questions/460332/, но нет принятого ответа.   -  person Nick Skywalker    schedule 13.11.2019
comment
Поделитесь журналом своей первоначальной попытки.   -  person Gyan    schedule 13.11.2019
comment
Мой плохой, исходный fps был 30, поэтому моя первая попытка записать субтитры не изменила частоту кадров. Однако проблемы остаются. И выполнение вычитания кадра до и после записи субтитров показывает больше различий, чем субтитр. Напомню, что это не та проблема, которую я пытаюсь решить, но она может быть причиной моей проблемы. Какой журнал вы хотите посмотреть?   -  person Nick Skywalker    schedule 13.11.2019
comment
Также стоит отметить, что в этом примере границы интервала, умноженного на извлечение кадра, дают: t_prev*fps=165.32 и t_next*fps=165.6, что подразумевает, что если кадр, извлеченный с помощью ffmpeg, является коэффициентом 1/fps, тогда я не должен получать кадр 166 для находиться между двумя субтитрами, но вместо этого отображать второй (или, возможно, предыдущий). То же самое при корректировке по fps видео: int(t_next*vfps)*fps/vfps=165.2, int(t_next*vfps)*fps/vfps=165.6   -  person Nick Skywalker    schedule 13.11.2019
comment
Я открыл вопрос по проблеме ненулевой разницы кадров до и после записи субтитров unix.stackexchange.com/questions/551928/   -  person Nick Skywalker    schedule 13.11.2019
comment
Если вам нужны точные временные метки извлеченных кадров, это можно сделать.   -  person Gyan    schedule 14.11.2019
comment
Да, это могло быть решением.   -  person Nick Skywalker    schedule 14.11.2019


Ответы (1)


Вы можете извлекать кадры с точными отметками времени, таким образом

ffmpeg -i nosub.mp4 -vf subtitles=sub.srt,settb=AVTB,select='if(eq(n\,0)\,1\,floor(4*t)-floor(4*prev_t))' -vsync 0 -r 1000 -frame_pts true extracted/%08d.bmp

Это будет извлекать первый кадр из каждой четверти секунды. Имя выходного файла состоит из 8 символов, где первые 5 цифр - секунды, а последние три - миллисекунды. Вы можете изменить размер поля в зависимости от максимальной продолжительности файла.

person Gyan    schedule 14.11.2019
comment
Не могли бы вы подробнее рассказать о параметрах? Также я получаю сообщение об ошибке: Missing ')' or too many args in 'if(eq(n,0)' - person Nick Skywalker; 14.11.2019
comment
Фильтр настройки используется для установки временной развертки на 1 микросекунду. Выражение фильтра выбора выберет первый кадр, а затем выберет каждый кадр, ближайший к предыдущей четверти секунды. -r 1000 преобразует временные метки с точностью до миллисекунды (иначе имя файла стало бы еще длиннее). vsync гарантирует, что ffmpeg не смещает временные метки. frame_pts печатает метку времени в имени файла. - person Gyan; 14.11.2019
comment
Отредактировал cmd, чтобы избежать всех запятых - person Gyan; 14.11.2019