Часто ли вам приходится работать с графикой? Как часто вы сталкиваетесь с недостатком стандартных эффектов/плагинов для обработки изображений? А можете ли вы, комбинируя доступные эффекты, получать требуемый результат? Если Вы утвердительно отвечаете на эти вопросы — эта статья для Вас!
К счастью, сегодня существует достаточное количество программных пакетов для обработки графики. Это Corel Draw, Photoshop, The GIMP, Paint Shop Pro и т. д. Этот список можно продолжать до бесконечности. Лично мне больше всего нравится The GIMP (GNU Image Manipulation Program). Он достаточно функционален, удобен (во всяком случае для меня) в использовании и, самое главное, легко автоматизируется и бесплатен!
В The GIMP (далее просто GIMP) реализован язык сценариев Script-FU, который, собственно, и позволяет автоматизировать обработку/создание изображений. С его помощью реализована почти сотня (!) эффектов, включенных в стандартную поставку GIMP. Этот язык прост и эффективен одновременно. О нем и пойдет речь в данной статье.
Хочу заметить, что самому языку я вас учить не буду, а лишь покажу один из возможных путей его САМОСТОЯТЕЛЬНОГО освоения. Я убежден, что лучшим методом изучения чего-либо является самостоятельная ПРАКТИЧЕСКАЯ работа над примерами, примерами и еще раз примерами.
Недавно мне понадобился эффект для имитации состарившейся бумаги. Его, как такового, я не нашел, но смог воплотить его (приближенно) в жизнь, комбинируя доступные. Чтобы его повторить, мне пришлось потратить больше минуты. Тогда я и решил, что будет легче создать соответствующий сценарий, чем выполнять рутинную работу несколько раз! В
данной статье я покажу, как я осваивал язык сценариев.
Итак, поставим задачу так: необходимо составить сценарий для имитации старения бумаги. В GIMP оказалось возможным достичь подобного эффекта (Рис. 1) с помощью трех эффектов: применяем холст, наносим кофейное пятно, применяем эффект старого фото. Первое, что нас не устраивает — пятна кофе круглые (нужна прямоугольная их форма); второе — они появляются в случайных местах; третье — нам вовсе не нужно несколько пятен — достаточно и одного.
Я подразумеваю, что мы работаем в Линуксе. Также подразумевается, что у вас достаточные познания GIMP'a и английского, т. е. увидь что-нибудь вроде gimp-gradients-set-active, вы поймете, что эта операция устанавливает рабочий (активный) градиент и знаете (хоть и примерно) где находится (если конечно находится) эта операция в меню. Желательно знание какого-нибудь языка программирования (Pascal, C).
Итак, начнем вносить соответствующие коррективы. В каталоге /usr/share/gimp/номер_версии/scripts/ находятся стандартные сценарии GIMP'а. Ищем coffee.scm и копируем его в каталог .gimp/scripts (в своем домашнем каталоге). Далее создаем его копию с именем old_paper.scm.
Настало время заглянуть в сценарий и проанализировать его. По пути я буду его комментировать. Замечу лишь, что строки, начинающиеся с ";" считаются комментариями. Также комментарии — все то, что находится за ";" в строках (но не в строковых константах).
; Chris Gutteridge (cjg@ecs.soton.ac.uk) ; At ECS Dept, University of Southampton, England. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
В этой «шапке» нет полезной информации — только лишь указывается авторство и условия распространения (GNU License).
(define (script-fu-coffee-stain inImage inLayer inNumber inDark)
Здесь объявляется функция script-fu-coffee-stain с аргументами inImage (исходное изображение), inLayer (рабочий слой), inNumber (количество пятен) и inDark (эффект «замена темным»).
(set! old-gradient (car (gimp-gradients-get-active)))
оператор set! объявляет переменную old-gradient и присваивает ей значение, возвращаемое функцией gimp-gradients-get-active. Вызов функции выполняется с помощью оператора car. Следует заметить, что все выражения (и вызовы функций) должны быть заключены в скобки. Все выражения оформляются следующим образом:
(оператор переменная1 переменная2/функция)
Это эквивалентно следующей записи, к примеру, на Паскале:
переменная1 оператор переменная2/функция
Например, (set! old-gradient (car (gimp-gradients-get-active))) на Паскале может быть записано так: old_gradient:=gimp_gradients_get_active; (а может быть и так: old_gradient:=gimp.gradients.get_active;)
(set! theImage inImage) ; дальше будем работать с переменной theImage — исходным изображением (set! theHeight (car (gimp-image-height theImage))) ; высота изображения (set! theWidth (car (gimp-image-width theImage))) ; и ширина (set! theNumber inNumber) ; количество пятен кофе (set! theSize (min theWidth theHeight) ) ; найменьшее — высота или ширина (while (> theNumber 0) ; пока theNumber>0, делать следующее: (set! theNumber (- theNumber 1)) ; theNumber:=theNumber-1; (set! theStain (car (gimp-layer-new theImage theSize theSize RGBA_IMAGE "Stain" 100 (if (= inDark TRUE) DARKEN-ONLY NORMAL) ))) ; создается новый слой theStain... (gimp-image-add-layer theImage theStain 0) ; ...и добавляется к нашему изображению (gimp-selection-all theImage) ; выделяем все изображение (gimp-edit-clear theStain) ; очищаем слой theStain (let ((blobSize (/ (rand (- theSize 40)) (+ (rand 3) 1) ) ) ); в переменную blobSize помещаем расстояние, на котором будет находится граница пятна от границы картинки (gimp-ellipse-select theImage (/ (- theSize blobSize) 2) (/ (- theSize blobSize) 2) blobSize blobSize REPLACE TRUE 0 FALSE) ; производим "эллипсоидальное" выделение ) (script-fu-distress-selection theImage theStain (* (+ (rand 15) 1) (+ (rand 15) 1)) (/ theSize 25) 4 2 TRUE TRUE ) ; искажаем границы выделения (gimp-gradients-set-active "Coffee") ; устанавливаем активным градиент "Coffee" (gimp-blend theStain CUSTOM NORMAL SHAPEBURST-DIMPLED 100 0 REPEAT-NONE FALSE 0 0 0 0 0 0) ; производим заливку выделения градиентом (gimp-layer-set-offsets theStain (- (rand theWidth) (/ theSize 2)) (- (rand theHeight) (/ theSize 2)) theSize) ) ; сдвигаем слой (gimp-selection-none theImage) ; отменяем выделение (gimp-gradients-set-active old-gradient) ; возвращаем старый градиент (gimp-displays-flush) ; выводим полученный слой из буфера ) ; конец цикла ; Register the function with the GIMP: ; самое главное — регистрируем функцию в GIMP'e : (script-fu-register "script-fu-coffee-stain" ; название функции _"<img>/Script-Fu/Decor/Coffee Stain..." ; положение в меню "Draws realistic looking coffee stains" ; описание "Chris Gutteridge" ; описание — автор "1998, Chris Gutteridge / ECS dept, University of Southampton, England." "25th April 1998" "RGB*" SF-IMAGE "The Image" 0 ; передаваемые параметры SF-DRAWABLE "The Layer" 0 SF-ADJUSTMENT _"Stains" '(3 1 10 1 1 0 0) SF-TOGGLE _"Darken Only\n(Better, but only for Images with a lot of White)" TRUE )
Итак, мы немного узнали о внутреннем строении сценариев в GIMP'e. Следующий шаг — модифицируем этот сценарий для наших нужд.
Первое, что изменяем — это заголовок объявления нашей функции:
(define (script-fu-old-paper inImage inLayer inTime inBorder inCanvas inDirection inDeep)
здесь новые параметры:
•InBorder — ширина "порванной" рамки в пикселях;
•inCanvas — надо ли делать бумагу рельефной (TRUE или FALSE)
•InDirection — направление падения света на рельеф
•inDeep — глубина рельефа
(set! old-gradient (car (gimp-gradients-get-active)))
Далее добавляем переменную theTime и присваиваем ей значение inTime:
(set! theTime inTime)
Добавляем размеры «по иксу» и «по игреку» (лишь для удобства):
(set! theSizeX theWidth ) (set! theSizeY theHeight )
Убираем цикл (while) — ведь нам надо одно «пятно».
Изменяем конструктор слоя theStain так, чтобы он создавался не пустым, а с копией исходного изображения:
(set! theStain (car (gimp-layer-copy (car(gimp-image-get-active-layer theImage)) TRUE )))
Теперь самое время поговорить о том, где можно брать названия и параметры вызова функций. Как было сказано выше, изучать мы будем САМОСТОЯТЕЛЬНО. С самим GIMP'ом не поставляется справка о языке сценариев. Поэтому будем выкручиваться сами . Брать информацию о функциях лучше всего в самих же сценариях! Открываете любой сценарий и смотрите, какие функции вызываются. Следует обращать внимание на структуру их названий. Это очень важно, потому что по аналогии можно составить имя предполагаемой функции (и выполнить в сценариях ее поиск — большая вероятность, что такая найдется, еще и с примером использования :-)). Это что касается сценариев Scriop-FU.
Плагины же поставляются без исходных текстов и для того, чтобы ознакомится с информацией по интересующему плагину, надо найти его в файле pluginrc в каталоге .gimp-номер_версии/ в вашем домашнем каталоге. Например, нам необходим плагин Apply Canvas (Применить Холст). Ищем. Смотрим:
(plug-in-def "/usr/lib/gimp/1.2/plug-ins/struc" 1003474617 (proc-def "plug_in_apply_canvas" 1 "Adds a canvas texture map to the picture" "This function applies a canvas texture map to the drawable." "Karl-Johan Andersson" "Karl-Johan Andersson" "1997" "<img>/Filters/Artistic/Apply Canvas..." "" "" "" "RGB*, GRAY*" 5 0 (proc-arg 0 "run_mode""Interactive, non-interactive") (proc-arg 13 "image""Input image (unused)") (proc-arg 16 "drawable""Input drawable") (proc-arg 0 "direction""Light direction (0 — 3)") (proc-arg 0 "depth""Texture depth (1 — 50)")))
Здесь нас интересует точное название (plug_in_apply_canvas), а также количество, назначение и тип аргументов (по умолчанию используются целые числа). В данном примере:
•(proc-arg 0 "run_mode""Interactive, non-interactive") — режим запуска: (интерактивный, с диалоговым окном) = 0, неинтерактивный (как раз для нас) = 1);
•(proc-arg 13 "image""Input image (unused)") — исходное изображение (только для чтения);
•(proc-arg 16 "drawable""Input drawable") — изображение, которое будет модифицироваться;
•(proc-arg 0 "direction""Light direction (0 — 3)") — направление падения света и его диапазон (0 — 3);
•(proc-arg 0 "depth""Texture depth (1 — 50)"))) — глубина текстуры (1 — 50)
Т. е. вызов этого плагина из сценария может иметь такой вид:
(plug-in-apply-canvas 1 theStain theStain inDirection inDeep)
Замечание: символ подчеркивания в объявлении плагина в файле pluginrc следует заменять на символ "-" при вызове плагина.
Итак, продолжим изменение нашего сценария...
После строчки (gimp-selection-all theImage) добавляем
(if (= inCanvas TRUE) (plug-in-apply-canvas 1 theStain theStain inDirection inDeep))
(вызов плагина происходит, если inCanvas=TRUE).
Изменяем и тип и размер выделяемой области:
(let ( ( blobSizeX (- theSizeX inBorder) ) ( blobSizeY (- theSizeY inBorder)) ) (gimp-rect-select theImage (/ (- theSizeX blobSizeX) 2) (/ (- theSizeY blobSizeY) 2) blobSizeX blobSizeY REPLACE TRUE 0 FALSE)
Теперь убираем смещение нашего «пятна» (gimp-layer-set-offsets) и инвертируем выделение:
(gimp-selection-invert theImage)
а затем и очистим его:
(gimp-edit-clear theStain ) (gimp-selection-invert theImage) ; снова инвертируем
Добавим цикл для того, чтобы «состарить» изображение theTime раз (с помощью плагина «Старое фото»):
( while (> theTime 1) (set! theTime (- theTime 1)) (script-fu-old-photo theImage theStain FALSE FALSE FALSE TRUE FALSE) )
Теперь применим этот плагин для искажения цветов («пожелтение»):
(script-fu-old-photo theImage theStain FALSE FALSE TRUE TRUE FALSE)
На этом принципиальные изменения нашего сценария закончились. Теперь надо изменить объявление функции. А его приведу полностью:
(script-fu-register "script-fu-old-paper" _"<img>/Script-Fu/Decor/Old Paper..." "Makes the white paper more old" "Alexandr Kuzmuk" "2002, Alexandr Kuzmuk / Dniepropetrovsk National University, Ukraine." "30th May 2002" "RGB*" SF-IMAGE "The Image" 0 ; здесь начинаются аргументы сценария SF-DRAWABLE "The Layer" 0 SF-ADJUSTMENT _"Time" '(1 1 10 1 1 0 0) ; SF-AJUSTMENT — это ползунок в диалоге (управляет устареванием бумаги) SF-ADJUSTMENT _"Border" '(40 15 70 5 1 0 0) ; размер границы SF-TOGGLE _"Apply Canvas" FALSE ; выключатель" — принимает значения "ДА" или "НЕТ". Сейчас по умолчанию будет FALSE SF-OPTION _"Light direction" '(_"Top Right" _"Top Left" _"Bottom Left" _"Bottom Right" ) ; выбор одного элемента из списка SF-ADJUSTMENT _"Deep of the canvas" '(4 1 50 1 1 0 0) )
На этом наш сценарий объявляю законченным! Диалоговое окно его параметров и результаты работы показаны на Рис. 2 и 3 соответственно.


