Щелевая съёмка: реализация на bash (ffmpeg + imagemagick)


Не помню что и почему я искал в интернете несколько дней назад, но я наткнулся на интересную статью с необычными фотографиями. А позже на еще одну статью, где описывалась реализация алгоритма создания таких фотографий на python. После прочтения меня заинтересовала эта тема и я решил провести вечера майских праздников с пользой для себя, а именно реализовать алгоритм «конвертирования» видео в щелевое фото. Правда, не на питоне, но подручными средствами на bash'е. Но обо всем по порядку.

Что такое щелевое фото

Вид фотографии, на которой запечатлено не одно событие в один конкретный момент времени, а несколько событий. Это достигается за счет того, что щелевая камера снимает кадры шириной в один пиксель (это и есть «щель») и «склеивает» их в одно фото. Немного запутанно звучит и пока сложно представить, что это такое и как выглядит. Самым доходчивым объяснением для меня был комментарий к одной из вышеупомянутых статей от пользователя Stdit:
image
После этого все становится понятным.

Пример для наглядности
image
Смотря на это фото можно сделать вывод, что первый слева легковой автомобиль сначала стоял неподвижно, а потом начал двигаться.

Алгоритм построения щелевой фотографии

  1. Разложить видео на множество изображений.
  2. Обрезать каждое полученное изображение по ширине в один пиксель с заданным смещением (щелью).
  3. Собрать полученное множество изображений в одно.

Звучит нестрашно и просто.

Дано

  • Камера Xiaomi Yi
  • Желание разобраться и сделать несколько необычных фото
  • Пару вечеров свободного времени


Решение

Первое и самое простое, что приходит на ум, это написать bash скрипт, который будет обрабатывать видео и фото в соответствии с описанными шагами алгоритма. Для реализации задуманного мне понадобился ffmpeg и imagemagick. В упрощенном виде на псевдо bash скрипт выглядит так:
ffmpeg -i videoFile frame-%d.png

for ((i = 1; i <= framesCount; i++));
do
  convert -crop 1xframeHeight+slitShift+0 frame-$i.png slit-$i.png
done

montage slit-%d.png[1-framesCount] -tile framesCountx1 -geometry +0+0 outputImage


Разберемся что здесь происходит

  • Во-первых, с помощью утилиты ffmpeg разбиваем видео на множество изображений вида frame-0.png...frame-n.png.
  • Во-вторых, с помощью утилиты convert из пакета imagemagick обрезаем каждое полученное изображение (ключ -crop) следующим образом: ширина == 1px, высота == высоте изображения. Так же указываем смещение щели по горизонтали. Сохраняем в файлы вида slit-0.png...slit-n.png.
  • В-третьих, с помощью утилиты montage из пакета imagemagick собираем полученные изображения в одно фото. Ключ -tile указывает на то, что все фото нужно собрать в одно по шаблону «framesCount по горизонтали и 1 по вертикали», то есть собрать множество изображений в один ряд.


Результат

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

Пользоваться скриптом очень просто:
./slitcamera.sh --input=test.avi --output=test.png --slit-shift=100

где input — видео файл для обработки, output — название результирующего файла, slit-shift — смещение щели по горизонтали.

Первым делом для быстрого тестирования я не стал снимать видео на камеру, а скачал первое попавшееся видео с youtube и «скормил» его скрипту. Вот что из этого вышло:


На следующий день с собой на прогулку я взял свою Xiaomi Yi и снял несколько видео. Вот что из этого вышло.

Родное Азовское море (фото сделано из видео разрешением в 1920x1080 пикселей и продолжительностью в 31 секунду, 60к/с)




А эти фото собраны из видео разрешением в 1280x720 пикселей и продолжительностью в 16 секунд, 120к/с. Обратите внимание на фон второго фото. Он не статичен. На фоне было движущееся колесо обозрения.

Посмотреть и скачать скрипт можно в моем репозитории на GitHub. Предложения, критика и пулреквесты только приветствуются.




К сожалению, не доступен сервер mySQL