16 марта 2012 в 19:47

Tilt-Shift фотографии своими руками

Что такое Tilt-Shift объективы и что с их помощью можно сделать знают многие. Недавно на хабре была статья о Tilt-Shift генераторе, который создает этот эффект путем обработки обычной фотографии. Но программка эта написана только для Windows, да еще и платить за нее надо. Все плагины для графических редакторов почему-то тоже требовали денег и лицензий. Поэтому было принято решение с этим вопросом разобраться самостоятельно и сделать инструмент пусть немного проще профессионального софта, и не идеально симулирующий оптику объектива, но бесплатный, открытый и доступный всем желающим! Что из этого получилось, а что нет — можете посмотреть сами.

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



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

Для работы я выбрал эту фотографию:



После добавления насыщенности и применения фильтров в фотошопе получается такой результат:



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

Перейдем к программированию.

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



В месте с черными пикселями наложения боке происходить не будет и сцена останется в фокусе. Маска была сделана методом “так примерно покатит”, это всего лишь демонстрация подхода.
Немного поковырявшись в python’е, получился такой небольшой и аккуратный скрипт:

  1. import Image
  2. import sys
  3. import numpy as np
  4. from scipy import ndimage
  5. import ImageEnhance
  6.  
  7. # INIT
  8. blur_size = 6
  9. image_base = "/Users/Mango/Desktop/tiltshift_alpha.png"
  10. image_mask = "/Users/Mango/Desktop/mask_tiltshift.png"
  11. image_output = "/Users/Mango/Desktop/tiltshift_preview.png"
  12.  
  13. # LOAD
  14. im_base = Image.open(image_base)
  15. im_mask = Image.open(image_mask)
  16. im_mask = im_mask.resize(im_base.size)
  17.  
  18. # PROCESS
  19. enh = ImageEnhance.Color(im_base)
  20. im_base = enh.enhance(1.7)
  21. enh = ImageEnhance.Contrast(im_base)
  22. im_base = enh.enhance(1.2)
  23.  
  24. im_blurred = np.array(im_base, dtype=float)
  25. im_blurred = ndimage.gaussian_filter(im_blurred, sigma=[blur_size,blur_size,0])
  26. im_blurred = Image.fromarray(np.uint8(im_blurred))
  27. im_mask = im_mask.convert("L")
  28. im_base = im_base.convert("RGBA")
  29.  
  30. # MERGE AND SAVE
  31. im_base.paste(im_blurred,mask=im_mask)
  32. im_base.save(image_output)

Вначале получаем исходное изображение и маску, затем размер маски подгоняется под размер изображения и начинается обработка. С помощью модуля ImageEnhance регулируются такие показатели как цвет, яркость и контраст. После чего в im_blurred сохраняется копия изображения в виде массива. Для создания боке я использовал старый добрый фиьтр размытия Гаусса. Его результат отличается от того же Lens Blur в профессиональных редакторах, но для начала вполне неплохой результат.
На финальной стадии размытое изображение накладывается на наш оригинал, используя альфа-маску. Так же стоит учесть, что каждый слой должен иметь правильную палитру. Маска используется в монохромном режиме L, а исходному изображению при помощи convert(«RGBA») добавляется альфа-слой, который и позволяем с помощью маски накладывать второй слой.
Вот что получилось в итоге:



Теперь немного допилим полученный код и избавимся от некрасивого внешнего дополнения в виде вручную нарисованной маски. В этом нет ничего сложного, она представляет собой так называемый reflected gradient и задается двумя коллинеарными векторами.



В еще более упрощенном виде эту модель можно представить следующим образом:



Вся система задается несколькими параметрами: направление, длина вектора А, зоны фокусировки и вектора Б. Все расстояния для простоты задаем по оси y.
Построение градиента происходит следующим образом. Сначала по заданным расстояниям строится изображение шириной в один пиксель, оно будет выступать в роли паттерна. После чего разными манипуляциями можно придать ему любую форму и повернуть на нужный угол.
Немного поэкспериментировав у меня получилась такая функция:

  1. import Image
  2. import ImageDraw
  3. import ImageOps
  4. import math
  5.  
  6. def draw_mask(angle,width,height,offset_init,offset_A,offset_focus,offset_B):
  7. offset = height*offset_init/100
  8. vectorA = offset+offset_A*height/100
  9. focus = vectorA+offset_focus*height/100
  10. vectorB = focus+offset_B*height/100
  11.  
  12. mask = Image.new('L'(width,height))
  13. mask_1px = Image.new('L'(1,height))
  14. draw_1px = ImageDraw.Draw(mask_1px)
  15. for y in range (0,offset)# draw white zone
  16. draw_1px.point((0,y),255)
  17. for y in range (offset,vectorA)# draw vectorA
  18. draw_1px.point((0,y),(vectorA-y)*(255/(vectorA-offset)))
  19. for y in range (vectorA,focus): # draw white zone
  20. draw_1px.point((0,y),0)
  21. for y in range (focus,vectorB): # draw vectorB
  22. draw_1px.point((0,y),255-(vectorB-y)*(255/(vectorB-focus)))
  23. for y in range (vectorB,height)# draw white zone
  24. draw_1px.point((0,y),255)
  25.  
  26. m_width,m_height = mask.size
  27. mask_1px = mask_1px.resize((int(m_width*3),m_height), Image.ANTIALIAS)
  28. mask_1px = ImageOps.invert(mask_1px)
  29. mask_top = mask_1px.rotate(angle,Image.NEAREST,1)
  30. mask_top = ImageOps.invert(mask_top)
  31.  
  32. mask.convert("RGBA")
  33. n_width,n_height = mask_top.size
  34. mask.paste(mask_top,(-n_width/2,-(n_height/2-height/2)))
  35. mask.convert("L")
  36. return mask



Этот код учитывает угол поворота на случай, если захочется сделать инструмент более универсальным или прикрутить к веб-интерфейсу, чем я и собираюсь заняться в ближайшем будущем.
Если кому понадобятся исходники, финальная версия есть на github.
Матвей Дядьков @matveyco
карма
56,0
рейтинг 0,0
Самое читаемое Разработка

Комментарии (60)

  • +5
    Супер! Надеюсь, скоро появится веб-версия с возможностью загружать фотки и выгружать уже откорректированные.
  • +6
    В фотошопе это делается 2-5 простыми движениями (без использования дополнительных плагинов), если кому интересно, могу написать. Вот, что получается:

    Смотреть результат >>>

    Правда, конечно, Photoshop — не бесплатное решение, и свободный открытый скрипт — это круче! Так что, автору — спасибо и респект!
    • +1
      PS. Хотя, принцип, почти не отличается. Кто владеет на начальном уровне фотошопом, сделает тоже самое и сам, достаточно описаний метода в топике. В качестве инструмента (фильтра) размытия надо использовать Lens Blur.
    • +2
      Опишите по шагам, пожалуйста.
      • +2
        Берем исходную фотку, нажимаем Q (QuickMask), выбираем Gradient Tool (G) от черного к белому, опция Reflected Gradient и ведем от центра изображения к краю. Снимаем маску (Q) и применяем фильтр Lens Blur с подходящим радиусом.
        Итог sanneo.ru/images/tilt.jpg
        Времени заняло
        • +1
          Добавлю еще, что в опциях Lens Blur есть интересные пресеты, имитирующее различные объективы (т.е. размытие или боке, которое они дают). Конечно, имитация есть имитация, но все-же лучше, чем ничего…
        • +1
          Также добавлю, что необязательно именно от центра к краю, но можно таким образом «в резкости» оставить любой участок фотографии, который хотите «выделить».
        • +1
          … и последним шагом, естественно, играем с Тоном/Насыщенностью (CTRL+U), Уровнями (CTRL+L) и если нужно, другими настройками. Если не получается, говорите, где заминка, поможем.
    • 0
      А в CS6 даже встроенный tilt-shift уже есть :)
    • +1
      Сделал подобную вещь на бесплатном фотошопе за 1минуту.

      image
      Но скрипт тоже интересное решение.
      • +2
        Нелохой редактор, кстати, не видел его раньше. Если бы можно было бы слои группировать, было б вообще супер. Мне этого так не хватает в GIMP.
        • +4
          Если бы можно было бы слои группировать, было б вообще супер. Мне этого так не хватает в GIMP.

          Выпущен GIMP 2.7.5 с опциональным однооконным интерфейсом и возможностью группировать слои.
          • 0
            Спасибо, надо потестить. Я ждал ради этого 2.8, думал, до выхода 2.8, думал, не видать мне групп слоев.
    • +5
      Вы не понимаете. Этот эффект, судя по всему, должны будут реализовать на всех языках программирования, включая брейнфак )
  • +12
    Обязательно посмотрите видео «Игрушечный Киев»: vimeo.com/38388574. Использовался обычный объектив, а эффект Tilt-Shift накладывался программно. И кстати, у ребят как-то натуральнее получилось это сделать.
    • +7
      Holy Shift! Впечатляющее видео!
    • +3
      Мне понравилось, но «программность» видна невооружённым взглядом. Шарма это обстоятельство не добавляет.
      Чтобы сделать эффект качественно, нужна как минимум карта расстояний. Объекты в плоскости фокусировки не размываются, чем дальше от этой плоскости, тем сильнее размытие. Это либо достаточно кропотливая ручная работа в фотошопе, либо весьма и весьма умный алгоритм, умеющий считать расстояния по одной фоторгафии.

      Что касается алгоритма, то как минимум нужно использовать что-то похожее habrahabr.ru/post/95541/.
    • 0
      Ещё один впечатляющий ролик: The City Of Samba
    • 0
      Великолепное видео и отличный саундрек. Очень круто.
    • +1
      Специально залогинился, чтобы поделиться ссылкой
      Ляпис Трубецкой — Я верю
  • +1
    Я бы маску сделал более плавной по краям, слишком резко она «обравается», и это видно на конечном результате. Можно сделать ее пошире и переходы по краям более плавными. Будет лучше. ИМХО.
  • +3
    Нормальные Tilt-Shift все-равно отличаются от размытия в Фотошопе и иже с ним (к тому же, на примере в посте размытие слишком сильное). На первый взгляд похоже и прикольно, но не то, имхо.

    У меня, кстати, есть мыльница от Canon, у которой есть встроенный эфект Tilt-Shift (пример: img-fotki.yandex.ru/get/6103/15097144.4a/0_5d54d_5affdb05_XL.jpg), но все-равно это не то.

    А вообще самый смак — это не фотки, а видео, если еще и в режиме Slow Motion…

    • 0
      Конечно не то! Но хороший tilt-shift объектив стоит зачастую как две хорошие зеркальные тушки, или даже как один вполне себе живой подержанный относительно свежий бюджетный автомобиль. Пока он еще не появился в собственном распоряжении, поиграться иной раз хочется. Просто ради собственного удовольствия. А если это доставляет удовольствие кому-то еще, то вообще супер. Но с настоящей оптикой — да еще и хорошей — не сравнится никакое программное решение, даже самое-самое.
  • +3
    В принципе, если интересуют инструменты с открытым исходным кодом, такое можно запросто сделать в узловом редакторе Blender.

    Боке на этом изображении не видно, но среди есть куча узлов на все случаи жизни. Блюр тут даже не пригодился из-за наличия defocus, а вот тона подкрутить или перспективу — с этим любую сцену можно превратить в игрушку :)

    Маску тоже можно генерировать процедурно, просто мне лень было в соседнюю панель лезть. Но любой человек, работавший с композерами подтвердит, что Tilt-shift по предложенной модели делается в полпинка (тут не могу не вспомнить эту статью)
    • 0
      неожиденное применение блендеру!
    • +1
      А ещё можно потратить на 15 минут больше и нарисовать все вертикальные плоскости в виде 3d-фигур — и уже от них брать z-координату.
      А то у вас вот, например, центральное здание — полная лажа, как и у топикстартера.
    • 0
      Когда статью увидел, сам захотел тоже самое сделать :)
  • +3
    Но программка эта написана только для Windows, да еще и платить за нее надо. Все плагины для графических редакторов почему-то тоже требовали денег и лицензий.

    Ну. Не все: «GIMP plug-in Toy для имитации тильт-шифта» (страница модуля в реестре расширений GIMP).
  • 0
    Все равно видно, что тилта как такового нет.
  • +8
    А не лучше посидеть пару минут и нарисовать нормальную маску глубины? Будет «на глаз», конечно, но и обычные градиентные маски рисуются «на глаз». image
    • +2
      А можно ссылку на оригинал?
      Если эта маска — ручная работа — снимаю шляпу!
      • 0
        Нет, это отдельный канал с глубиной резкости отрендеренной трёхмерной сцены. Но, чисто теоретически, такое можно и руками нарисовать, пусть и не так четко. Всё-равно, при размытии маски, детали, скорее всего, пропадут. Нужно попробывать.
    • 0
      Да, это самый правильный подход. К тому же, прекрасно вписывающийся в использование гимпового плагина focus blur, у которого есть карта расстояний.
  • 0
    • +2
      На самом деле это выглядит как плохая фотография.
      • 0
        Может и так, я просто такую же методику применил как описано выше. Я же не специалист.
  • +1
    Ну и конечно же ImageMagick: www.imagemagick.org/Usage/photos/#tilt_shift
  • –2
    Спасибо за статью, вырасту кармой, проголосую )
    вот 5 минут поигрался в Snapseed на ipad. Прикольно, но фотки действительно надо подбирать. Не все подходят.





    • +5
      Классический пример ДБЛ
      • –1
        Классический пример БЛД
        • +6
          Как ни назови, а До Было Лучше
          • –1
            Ну извините, что загадил топик. Учту.
  • +2
    Большинство услуг человечество предоставляет за деньги из-за нежелания постигать нюансы услуги другой частью, и это правильно." Робин Гуды" выступают «Робин Гудами» только для малой части. Ура опен сорцу — ленивый платит, любознательный получает бесплатно новые знания и и услугу.
  • +1
    Баловство это всё, основное применение TS — предметка, когда надо повернутый объект уместить в ГРИП. TS как раз поворачивает плоскость резкости относительно матрицы фотоаппарата.
  • +2
    лучше бы обьективы TS делали доступные
  • 0
    я купил себе бюджетный вариант — lensbaby edge 80

    результат:
    image

    image

    image
    • 0
      во сколько обошлось?
      • 0
        я покупал 2 оптики сразу — Composer Pro with Sweet 35 + Edge 80 — вместе вышло $660
        есть ощущение немного игрушечности от этих объективов, но приятно с ними «играться» :)
        • 0
          а картинка как по ощущениям, фоточки маленькие не очень понятно…

          хотя картинка у них интересная судя по фоточкам выше
          • 0
            я не очень доволен качеством — шумы лезут, если темное фото. но для интернета — подойдет.
            нравится, как размывает — там 2.8 диафрагма
            снимал ночью — вообще интересные кадры получаются
            вот пример большой фотографии — farm8.staticflickr.com/7201/6942922685_de8f63383c_b.jpg
    • 0
      Фото с велосипедом странное.
      Линия неразмытого изображения проходит от левого угла терассы, через голову спыщего мужика в правый нижний угол.
      Какие-то странные ощущения при просмотре.
      • 0
        угол меняется, но не всегда, когда хочется сделать быстрый кадр, есть возможность оперативно подобрать «правильный» угол
        • 0
          Ну я так и понял. Просто из-за неестественности размытости фотография немного странно выглядит
  • +1
    А вот тут замечательнейшее tilt-shift video, смонтированное из 4500 фото.
  • 0
    Забавно, но если набрать в гугл tild, то страница чуть повернется :) Извините за оффтоп…
    • +2
      только tilt а не tild [smile]
  • 0
    В продолжение темы



    Подобный скрипт на языке Processing. Радиус фильтра увеличивается по вертикали.

    int N=8;
    PImage[] imgs=new PImage[N];
    for (int k = 0; k < imgs.length; k++) {
    imgs[k]=loadImage(«test.png»);
    }
    for (int k = 1; k < imgs.length; k++) {
    imgs[k].filter(BLUR,k-1);
    }
    PImage out = createImage(imgs[0].width,imgs[0].height,RGB);

    for (int i = 0; i < imgs[0].width*imgs[0].height; i++) {
    int h=floor(i/imgs[0].width);
    int a=floor(N*h/imgs[0].height);
    out.pixels[i]=imgs[N-a-1].pixels[i];
    }
    size(imgs[0].width,imgs[0].height);
    image(out,0,0);

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