Думаю многим фотографам приходилось чистить отснятые фотографии от пылинок на матрице. Не имея полного 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) есть некий глюк при работе с кистями. Нельзя выбрать кисть из уже существуюoих и задать ей размер (вот этот баг). Размер будет браться либо стандартный либо тот что у вас в 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)
Автор: reJit