Щелевая съёмка: Ловля птичек с помощью питона

от автора

Прочитав несколько месяцев назад статью про щелевую съемку, я твердо решил написать небольшую утилитку, позволяющую создавать щелевые фотографии. Перетряхнув закрома, я обнаружил следующие ресурсы: отложенный в долгий ящик учебный курс по Python, старенький ноутбук, камеру GoPro, несколько часов свободного времени, март месяц и кормушку для птиц.

Наблюдая за интенсивным трафиком синиц у кормушки, я не сомневался, что заснять синичку в полете будет «как два байта переслать». Водрузив камеру на штативчик, я пододвинул ее к кромушке и, включив режим покадровой съемки с интервалом 0.5 секунд, спрятался в доме. Синички вернулись буквально через пару минут. Выждав еще минут пять, я радостно выбежал на террасу, схватил камеру и устремился к ноутбуку. Каково же было мое разочарование, когда через полчаса я увидел обработанные результаты съемки — чередующиеся светлые и темные горизонтальные полосы с мелкими враплениями вертикальных «срезов» синичек, шириной в несколько пикселей. Похоже синички летали слишком быстро.

Не беда, переставим камеру в режим спортивного видео (60 кадров/секунду) и попробуем еще разок. Синички, очевидно, уже привыкли к штативу, поэтому воздушный трафик восстановился буквально через несколько секунд. Еще через час я был счастливым обладателем серии фотографий, сделанных с интервалом 1/60 сек. (в разложении видео на фото сильно помог ffmpeg). Я был уверен в успехе. Но результат все равно оказался плачевным — полоски из синичек стали чуть шире, но связной картины не образовывали. После анализа (методом пристального взгляда) нескольких фотосерий стало понятно, что синички летают не просто быстро, а очень быстро. От момента взлета синички до ее растворения среди деревьев проходит менее 0,3 секунды!

Т. е. чтобы сделать щелевое фото полета синицы шириной хотя бы 120px нужно снимать с частотой 400 кадров/секунду! Это было фиаско.

В тяжелой задумчивости, оторвав ребенка от игры в MineCraft и нацепив на него лыжи, я отправился на лыжную прогулку. Камеру я захватил с собой. Ребенок, проникшись моей проблемой, предложил — «Пап, а может тебе огонь в лыжном домике заснять?».

Эврика! Пройдя несколько километров по лыжне, мы добрели до хижины. Разведя огонь и поставив жариться колбаски, я укрепил камеру напротив…

Проведя час-другой в хижине, мы сытые и довольные вернулись домой.

Результат оказался любопытным, но скучноватым:

Почесав в затылке, я решил добавить ключ -hz в код для того, чтобы можно было сделать щелевое фото не только вертикальной щелью, но и горизонтальной. В этот раз все оказалось куда интереснее:

Соответствующий щелевому фото видео фрагмент:

На щелевом фото виден вход, выход и снова вход моего сына через пространственно-временное окно. Обратите внимание на синусоидальное искажение створки двери! Интуитивно угаданное смещение щели (600px, ~55% высоты фото) оказалось самым интересным, прогон по остальным смещениям не дал столь эффектного результата.

Краткие выводы: Искать подходящие объекты для щелевого фото — занятие азартное и захватывающее. Щелевое фото не просто очередной эффект, а очень интересный и забавный взгляд на мир. Надеюсь, что несложный код, приведенный ниже, снизит для вас порог вхождения в него. Удачи!

P.S. <задумчиво>Где бы мне достать камеру с частотой 400 кадров/секунду?</задумчиво> У вас нет, случайно?

#Slit-scan photography #CopyLeft 2013 OlloSnow  import os, Image, argparse  def add_slice(img_slt, img, offst, wdth, hz, num):   if hz:     img_tmp = img.crop((0, offst, img.size[0], offst+wdth))     img_slt.paste(img_tmp, (0, num*wdth, img.size[0], (num+1)*wdth))   else:     img_tmp = img.crop((offst, 0, offst+wdth, img.size[1]))     img_slt.paste(img_tmp, (num*wdth, 0, (num+1)*wdth, img.size[1]))   return  def main():   parser = argparse.ArgumentParser(usage=\     '%(prog)s dir_images res_image [-hz] [-o slit_offset] [-w slit_width] [--help]')   parser.add_argument('dir_img', type = str,\     help = 'images directory')   parser.add_argument('res_img', type = str,\     help = 'result slit-scan image')   parser.add_argument('-hz', '--horizontal', action='store_true',\     help = 'horizontal orientation of the slit (vertical by default)')   parser.add_argument('-o', '--offset', type = int, default = 0,\     help = 'slit offset (pixels; 0 by default)')   parser.add_argument('-w', '--width',type = int, default = 1,\     help = 'slit width (pixels; 1 by default)')   args = parser.parse_args()        filenames = sorted(os.listdir(args.dir_img))   img = Image.open(os.path.join(args.dir_img, filenames[0]))   if args.horizontal:     width = img.size[0]     height = len(filenames)*args.width   else:     width = len(filenames)*args.width     height = img.size[1]   img_slt = Image.new('RGB',(width,height))      i = 0   max_i = len(filenames)   for file in filenames:     img = Image.open(os.path.join(args.dir_img, file))     add_slice(img_slt, img, args.offset, args.width, args.horizontal, i)     i += 1     print max_i - i, '\r',        img_slt.save(args.res_img, 'JPEG')    if __name__ == "__main__":   main() 

ссылка на оригинал статьи http://habrahabr.ru/post/173115/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *