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



    Прочитав несколько месяцев назад статью про щелевую съемку, я твердо решил написать небольшую утилитку, позволяющую создавать щелевые фотографии. Перетряхнув закрома, я обнаружил следующие ресурсы: отложенный в долгий ящик учебный курс по 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))
      
      max_i = len(filenames)
      for i, file in enumerate(filenames):
        img = Image.open(os.path.join(args.dir_img, file))
        add_slice(img_slt, img, args.offset, args.width, args.horizontal, i)
        print max_i - i, '\r',
        
      img_slt.save(args.res_img, 'JPEG')
      
    if __name__ == "__main__":
      main()
    

    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 21
    • +5
      Домик класс! Эт где такое?
      • +4
        Финляндия, однако.
        • 0
          Камера Canon S100, 320x240 снимает 240 кадров в секнду, 640x480 -120 кадров в секунду…

          длительность 30секунд по моему…
          • +1
            Эх, разрешение маловато :(
            • +1
              я цапель в полете снимаю или какую то активность народного плана…
              Например сахар на пеньке молотом выбивают, китайская уличная магия…
              Огонь, воду, или вращение чайника «чайное шоу» и так далее…
      • 0
        Классный набор в закромах )
      • +2
        > Где бы мне достать камеру с частотой 400 кадров/секунду?
        Предлагаю найти медленных птиц ))

        p.s. Как вариант, снимать улиток!
        • +4
          Точно! Пингвины наш выбор! :))
        • +2
          image
          • +1
            О, да! Я тоже вспомнил этот фильм, когда увидел получившуюся фотку.
        • +1
            max_i = len(filenames)
            for i, file in enumerate(filenames):
              img = Image.open(os.path.join(args.dir_img, file))
              add_slice(img_slt, img, args.offset, args.width, args.horizontal, i)
              print max_i - i, '\r',
          

          • 0
            Да, так гораздо лучше, поправил.
          • +4
            Ловля птиц с помощью питона… почему то представил эту картинку сразу.

            habrastorage.org/storage2/f16/384/08d/f1638408d1ad9b07878340cc2b379c33.jpg
            (К сожалению не могу прикрепить картинкой или ссылкой)

            Сори за офтоп )
            • 0
              Ого! 8)
              • +1
                А мне картинку из «маленького принца», ну… слегка модифицированную, т.к. там слон был )
              • +1
                Ключ -hz

                Для тех, кто не в теме, звучит загадочно))
                • 0
                  Ну там из кода видно о чем оно
                  и звучит красиво!
                  :)

                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                Самое читаемое
                Интересные публикации