В GIMP я не новичок, но раньше руки никогда не доходили до написания к нему скриптов. Точнее доходили, но не далеко. Всякая предыдущая попытка что-нибудь сваять на этом поприще заканчивалась провалом в виду первого же неработающего примера, а досконально разбираться времени не хватало. Но недавно меня припёрло... Просто стало жалко своего времени когда речь зашла о пакетной обработке фотографий для подгодовки к публикации на сайте. ImageMagic я конечно тоже рассматривал, но у него все-таки не столь широкие возможности. Смотрел и в сторону GEGL, но не нашел достаточно документации. Вооружился я в итоге браузером и исходниками GIMP и пошел курить эту тему до умопомрачения.
В итоге я выяснил что с недавних пор поменялся диалект Schema (который в свою очередь является диалектом Lisp), используемый в GIMP. Не так давно им был
SIOD, тепрь (а точнее в сентябре 2006-го в версии GIMP 2.2) нам подсунули
Tiny-Fu, базирущийся на стандарте
R5RS. Выяснил я это после того как очередная попытка использовать более менее серьезный пример на SIOD, изобилующих в сети, окончилась провалом =) И с тех пор замаячил свет в конце тонеля в моих попытках разобраться что к чему. Но это было позже....
А сначала я взял за основу статью
Сергея Головко. Статья хороша тем, что вменяемо описывает синтаксис и основы Lisp. Но сходу написать нужный скрипт все равно не удалось. Во-первых он описывает в статье диалект SIOD (тут я еще не был в курсе о смене диалекта). Во-вторых было совершенно не понятно как работать с файлами (этим я озадачился на первом этапе). Ну и не совсем еще были ясны нюансы в основных конструкциях языка, а просмотр процедур в GIMP дает информацию исключительно о PDB (для тех, кто не в курсе - это набор внутренних функций GIMP для работы с изображением, задумчиво именуемый базой данных), а по языку ну ни слова.
В итоге я полез в исходники, где и наткнулся на "документацию" по TinySCHEME. Для интересующихся скажу что лежит она в исходниках по пути: gimp-2.6.1/plug-ins/script-fu/tinyscheme/Manual.txt. Этот файлик и открыл мне глаза, хотя документацией его можно назвать с большой натяжкой... Чуть позднее я наткнулся на другой файлик, в котором описывались функции не реализованные в Tiny-Fu для совместимости с SIOD: /usr/local/share/gimp/scripts/script-fu-compat.init. Именно из этих файлов я и узнал нужное мне ключевое слово для поиска. А именно TinySCHEME. Справедливости ради следует отметить, что если бы я был более внимательным, то нашел бы это в Консоли Script-Fu =)
И все-таки удобно когда есть доступ к исходникам уже готового функционала. Дальнейшее изучение языка я продолжил в уже готоых script-fu скриптах, лежащих в: /usr/local/share/gimp/scripts/. Описание моих последующих поисков, путешествия по граблям и изучение методом научного тыка я пожалуй опущу. Вот что у меня получилось в итоге:
(define (images-prepare dir_to_prepare i_w i_h t_w t_h)
; Выводим список паттернов для имен загружаемых файлов
(define (prepare-patterns dtp)
(define fts '("jpg" "JPG" "jpeg" "JPEG" "png" "PNG"))
(define pts ())
(for-each (lambda (format)
(set! pts (cons (string-append dtp "/*." format) pts))
) fts
)
pts
)
; Выводим список путей загружаемых файлов
(define (get-file-paths pats)
(define fls ())
(for-each (lambda (pattern)
(set! fls (append (cadr (file-glob pattern 1)) fls))
) pats
)
fls
)
; Делим строку по разделяющему символу
(define (split-sring str sep)
(define ret ())
(define tmp ())
(for-each (lambda (simb)
(if (string=? (atom->string simb) sep)
(begin
(if (null? tmp)
(begin
(set! ret (cons (list->string tmp) ret))
)
(begin
(set! ret (cons (list->string (reverse tmp)) ret))
)
)
(set! tmp ())
)
(begin
(set! tmp (cons simb tmp))
)
)
) (string->list str) )
(if (null? tmp)
(begin
(set! ret (cons (list->string tmp) ret))
)
(begin
(set! ret (cons (list->string (reverse tmp)) ret))
)
)
(reverse ret)
)
; Получаем имя файла с изображением превьюшки
(define (get-tumb-name name)
(define ret "")
(define lst (split-sring name "."))
(define len (length lst))
(define iteract 0)
(for-each (lambda (simb)
(set! iteract (+ iteract 1))
(set! ret (string-append ret simb))
(if (= iteract (- len 1))
(begin
(set! ret (string-append ret "_th"))
)
)
(if (< iteract len)
(begin
(set! ret (string-append ret "."))
)
)
) lst )
ret
)
; Повышаем резкость изображения по хитрому алгоритму
(define (rez img ind)
(define sublayer_one 0)
(define sublayer_two 0)
(set! sublayer_one (car (gimp-layer-copy (car (gimp-image-get-active-layer img)) 1)))
(gimp-image-add-layer img sublayer_one -1)
(gimp-desaturate sublayer_one)
(gimp-layer-set-mode sublayer_one GRAIN-MERGE-MODE)
(set! sublayer_two (car (gimp-layer-copy sublayer_one 1)))
(gimp-image-add-layer img sublayer_two -1)
(gimp-invert sublayer_one)
(plug-in-gauss 1 img sublayer_one ind ind 1)
(gimp-image-flatten img)
)
(define width 0)
(define height 0)
(define width_th 0)
(define height_th 0)
(define img 0)
(define drawable 0)
(define save_image 0)
(define newfilename "")
(define filelist (get-file-paths (prepare-patterns dir_to_prepare)))
(for-each (lambda (file)
(set! img (car (gimp-file-load 1 file file)))
(set! drawable (car (gimp-image-get-active-layer img)))
(gimp-layer-set-name drawable "bg")
(if (> (car (gimp-image-width img)) (car (gimp-image-height img)) )
(begin
(set! width i_w)
(set! height i_h)
(set! width_th t_w)
(set! height_th t_h)
)
(begin
(set! width i_h)
(set! height i_w)
(set! width_th t_h)
(set! height_th t_w)
)
)
; Подгоняем размер изображения
(gimp-image-scale-full img width height INTERPOLATION-LANCZOS)
(rez img 1.0)
(set! newfilename (string-append dir_to_prepare "/out/" (car (gimp-image-get-name img))))
; Удаляем exif данные из изображения. По умолчанию GIMP поворачивает
; изображение, если в exif есть информация об ориентации, а саму exif
; запись не изменяет. Лечится удалением exif
(gimp-image-parasite-detach img "exif-data")
; Сохраняем изображение
(gimp-file-save RUN-NONINTERACTIVE img (car (gimp-image-get-active-layer img)) newfilename newfilename)
; Делаем превьюшку
(gimp-image-scale-full img width_th height_th INTERPOLATION-LANCZOS)
(rez img 0.5)
(set! newfilename (string-append dir_to_prepare "/out/" (get-tumb-name (car (gimp-image-get-name img)))))
(gimp-file-save RUN-NONINTERACTIVE img (car (gimp-image-get-active-layer img)) newfilename newfilename)
; Быстро бежим к помойке и выносим мусор =)
(gimp-image-clean-all img)
(gimp-image-delete img)
) filelist
)
)
(script-fu-register "images-prepare"
"/Script-Fu/Prepare Images"
"Prepare Images"
"SvvoRD"
"SvvoRD"
"2008"
""
;Вид объекта интерфейса Название Значение по умолчанию
SF-DIRNAME "Directory" ""
SF-ADJUSTMENT "Image width" '(800 1 4000 1 10 0 1)
SF-ADJUSTMENT "Image height" '(600 1 4000 1 10 0 1)
SF-ADJUSTMENT "Thumbnail width" '(133 1 500 1 10 0 1)
SF-ADJUSTMENT "Thumbnail height" '(100 1 500 1 10 0 1)
)
Перед употреблением следует скопировать все нужные файлы в отдельную директорию и создать в ней директорию "out". Туда и будет падать результат.
Честно говоря желаемого результата я еще не достиг. По хорошему тут очень желательно было бы добавить интерактивный вызов инструмента Crop и инструменты по подгонке цвета, но как выяснилось, Script-Fu делать этого не позволяет. Либо я опять копал не в том месте...
В следующий раз посмотрю как себя поведут скрипты на Perl и Python. Правда первый с GIMP используется редко, но его я знаю хорошо, а второй используетя часто, но его я практически не знаю. Так что мне еще предстоит множество увлекательных приключений. Чего и вам желаю....
З.Ы. Редактор тут убогий...