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

в 19:01, , рубрики: dust, gimp, обработка изображений, пакетная обработка, Программирование, пыль, фотография, метки: , , , ,

Думаю многим фотографам приходилось чистить отснятые фотографии от пылинок на матрице. Не имея полного 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 консоль
Убираем пыль с 1000 фотографий с помощью Gimp и Script Fu

Внешний вид консоли справа.
Убираем пыль с 1000 фотографий с помощью Gimp и Script Fu
Скрипт лучше писать в каком либо текстовом редакторе, отображающем связи скобок. Я для этого использовал Notepad++. После написания будем вставлять скрипт в консоль и проверять как он работает.

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

Для проверки идеи я не стал сразу писать «функцию» по всем правилам, как описано тут.
Для начала я нашёл нужный мне функционал в браузере процедур.
Убираем пыль с 1000 фотографий с помощью Gimp и Script Fu
И начал подбирать параметры.
Тут началось самое интересное и не документированное. Дьявол скрывается в деталях.
Как видим из описания функции на вход её подаются 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. Если всё удалось — видим в консоли следующее
Убираем пыль с 1000 фотографий с помощью Gimp и Script Fu
В случае ошибок — увидим описание ошибки.

А вот картинки, до скрипта и после.
Убираем пыль с 1000 фотографий с помощью Gimp и Script Fu
С параметрами кисти можно поиграть на своё усмотрение.

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

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

Получаем такой скрипт

(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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js