Pull to refresh

Убираем пыль с 1000 фотографий с помощью Gimp и Script-Fu

Reading time6 min
Views21K
Думаю многим фотографам приходилось чистить отснятые фотографии от пылинок на матрице. Не имея полного Photoshop-a или LightRoom-a быстро обработать большое количество фотографий крайне трудно.
Но у нас есть Gimp и желание написать к нему скрипт.
На Хабре уже было не мало статей про возможность написания скриптов в Gimp.
Вот самый подробные обзор самого языка Script-fu и возможности написания на нём расширений к Gimp
Вот тут статья про пакетную обработку.
По идее, прочитав 2 эти статьи, можно сделать что угодно. Но вот только при решении обозначенной в заголовке проблемы, я столкнулся с многими нюансам, на преодоление которых ушло не мало времени, и которые мало где описаны. Даже в англоязычных Tutorial-ах и Help-ах. О них и пойдёт речь.


Отступление


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

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

В таком случае большинство специалистов используют Adobe Photoshop Light Room или же последние редакции полного Adobe Photoshop.
В LR используют богатые возможностями пакетной обработки фотографий .
В полном Photoshop — возможности создания Action-ов.

Но у обычных людей как правило нет ни LightRoom ни уж тем более Photoshop.
Зато есть Gimp, возможность написания к нему скриптов, и немного времени и усердствования.

Пишем скрипт


Для этого открываем Gimp а в нём Script-Fu консоль


Внешний вид консоли справа.

Скрипт лучше писать в каком-либо текстовом редакторе, отображающем связи скобок. Я для этого использовал Notepad++. После написания будем вставлять скрипт в консоль и проверять как он работает.

Для реализации идеи будем использовать некий функционал clone который по сути должен делать тоже что и инструмент копирования в GUI самого Gimp-a. Будем вводить координаты нашей пылинки на фотографии и как-то задавать область откуда клонировать участок, чтобы эту пылинку закрыть.

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

И начал подбирать параметры.
Тут началось самое интересное и не документированное. Дьявол скрывается в деталях.
Как видим из описания функции на вход её подаются 7 параметров, два первых из них, это DRAWABLE объекты. Немного погуглив легко можно найти примеры скриптов, получающих эти drawabale из пути к картинке

Вот код
( let* 
    (
      (image (car (gimp-file-load RUN-NONINTERACTIVE "E:/test.png" "E:/test.png")))
      (drawable (car (gimp-image-get-active-layer image)))
    )
)

Первые 2 параметра есть (т.к. и source и target у нас наша же картинка)
Третий параметр выставляем в IMAGE-CLONE, т.к. мы хотим клонировать из этой же картинки а не из какого-то там ранее созданного паттерна. 4й и 5й тоже всё ясно — пишем сюда координаты нашей пылинки.
А вот 6 и 7й параметры не так очевидны.

Методом проб и ошибок выяснил что работает этот инструмент кистью. И как раз 6й и 7й параметры задают путь по которому эта кисть будет двигаться. О том как задать верно кисть опишу ниже.
Больше всего времени ушло на понимание 6го параметра, и не мало сумятицы внёс коментарий к нему
Number of stroke control points (count each coordinate as 2 points) (num-strokes >= 2)
Я упорно вводил сюда количество координат, а не чисел. И получал непредвиденные результаты. Оказывается это просто количество чисел в следующем параметре (массиве). Т.е. допустим если мы хотим начать клонирование в x=50 y=50 и сделать кистью маршрут через 3 точки из точки 0, 0 в точку 10, 10 а затем в точку 20, 30 (точки соединятся прямыми, но не закольцовываются, т.е. последняя не соединяться с первой) то нам надо написать что-то вроде вот этого
  (gimp-clone drawable drawable IMAGE-CLONE 50 50 6 #(0 0 10 10 20 30) ) 

6 чисел в массиве, но они описывают всего 3 координаты. Если надо 4 координаты, то написали бы 8 и потом массив из 8 чисел. и так далее.

Теперь про кисть. Увы в данной версии Gimp (2.8.4) есть некий глюк при работе с кистями. Нельзя выбрать кисть из уже существующих и задать ей размер (вот этот баг). Размер будет браться либо стандартный либо тот что у вас в GUI сейчас для неё выставлен.
По-этому нам придётся делать свою собственную кисть и задавать ей параметры (что на самом деле более удобно)

Вот код открывающий тестовую картинку, создающий кисть и клонирующий левую часть картинки — в правой части по заданному маршруту (нарисуем треугольник).
( let* 
    ; create variables for get DRAWABLE
    (
      (image (car (gimp-file-load RUN-NONINTERACTIVE "E:/test.png" "E:/test.png")))
      (drawable (car (gimp-image-get-active-layer image)))
    )
    ; create and set brush	
    (gimp-brush-new "MyBrush")
    (gimp-brush-set-radius "MyBrush" 4)
    (gimp-brush-set-shape "MyBrush" BRUSH-GENERATED-CIRCLE)
    (gimp-brush-set-hardness "MyBrush" 0.50)
    (gimp-brush-set-spacing "MyBrush" 0)
    (gimp-brush-set-spikes "MyBrush" 0)
    (gimp-brushes-set-brush "MyBrush")
    ; clones
    (gimp-clone drawable drawable IMAGE-CLONE 100 10 8 #(450 10 360 150 540 150 450 10) )
    ; save result
    (gimp-file-save RUN-NONINTERACTIVE image drawable "E:/test_e.png" "E:/test_e.png")
    (gimp-image-delete image)
)

Копируем скрипт в консоль и жмём Enter. Если всё удалось — видим в консоли следующее

В случае ошибок — увидим описание ошибки.

А вот картинки, до скрипта и после.

С параметрами кисти можно поиграть на своё усмотрение.

Применяем результат к тысячам файлов


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

Получаем такой скрипт
(define (script-fu-batch-dust-remove inputFolder outputFolder dustX dustY dustRadius)
  (let* ((filelist (cadr (file-glob (string-append inputFolder DIR-SEPARATOR "*") 1))))
    ; create and set brush	
    (gimp-brush-new "DustBrush")
    (gimp-brush-set-radius "DustBrush" dustRadius)
    (gimp-brush-set-shape "DustBrush" BRUSH-GENERATED-CIRCLE)
    (gimp-brush-set-hardness "DustBrush" 0.70)
    (gimp-brush-set-spacing "DustBrush" 0)
    (gimp-brush-set-spikes "DustBrush" 0)
    (gimp-brushes-set-brush "DustBrush")
    ; go throw all files in inputFolder
    (while (not (null? filelist))
      (let* ((filename (car filelist))
          (image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
          (drawable (car (gimp-image-get-active-layer image)))
		  (dustCoordinates (vector dustX dustY))
        )
        ; clone
        (gimp-clone drawable drawable IMAGE-CLONE (+ dustX (* dustRadius 2)) dustY 2 dustCoordinates)
        ; save result to outputFolder
        (set! filename (string-append outputFolder DIR-SEPARATOR (car (gimp-image-get-name image))))
        (gimp-file-save RUN-NONINTERACTIVE image drawable filename  filename)
        (gimp-image-delete image)
      )
      (set! filelist (cdr filelist))
    )
    ; remove just created Brush for not spam brush list
    (gimp-brush-delete "DustBrush")
  )
)

Вставляем его в консоль. Теперь у нас в консоли есть функция script-fu-batch-dust-remove
Теперь открываем наши фотографии с пылью, находим с помощью подбора кисти размер пылинки (наведя мышью на пылинку и подобрав так размер кисти, чтобы она полностью покрывала пылинку). Слева с низу пишутся координаты нашего курсора на фотографии.
Выписываем полученные координаты и радиус
Копируем все файлы которые мы хотим исправить в папку. Копировать все подряд не стоит, я копировал только те где пылинка явно бросается в глаза, т.е. к примеру на фоне чистого неба или моря или другой однородной текстуры. Если скопировать все — даже те где пылинки не видно из-за неоднородности изображения в этом месте — то попортим эти фотографии не нужным клонированием.

Запускаем скрипт в консоли
(script-fu-batch-dust-remove "E:/toEdit/in" "E:/toEdit/out" 3186 682 15)

И в папке E:/toEdit/out получаем все файлы с теми же именами — но уже без пыли!
Результат достигнут.

P.S.
После ряда экспериментов пришёл в к выводу что лучше использовать функцию не gimp-clone а gimp-heal. С ней результаты лучше, вне зависимоти от окружающей пыль картинки. Параметры точно такие-же, только нету IMAGE-CLONE
Так что вызов в моём скрипте будет примерно такой
(gimp-heal drawable drawable (+ dustX2 (* dustRadius2 2)) dustY2 2 dustCoordinates2)


UPD1
Примеры работы скрипта с gimp-heal
Удачная обработка (диафрагма 22)
До

После


Не совсем удачная обработка (диафрагма 11)
До

После (половина окна теперь вместо пылинки)


Результат при диафрагме 7,0
До

После (видно что небольшая аура всё же осталась, надо было бы наверно увеличить радиус для этой фотографии)


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

UPD2
В свете того что в скрипте я использовал для сохранения результатов метод gimp-file-save выходные файлы получаются по размеру меньше исходных. Т.е. получается gimp-file-save жмёт JPEG в другом качестве, нежели исходные файлы. Если кому это критично — могут почитать в браузере процедур про file-jpeg-save и использовать её вместо предложенного мной, более простого решения с gimp-file-save.
Tags:
Hubs:
+37
Comments24

Articles

Change theme settings