GIMP Script-fu: quick learning and writing a simple Scheme scripts (+ batch processing for free)

Membership


In this article you will learn how in the shortest possible time to learn the basics of scripting in GIMP in the language of the Scheme and proceed directly to the solution of simple practical problems. This material is intended only for those who are going to automate the routine processing of the here and now, not strongly going into details and without sacrificing precious time. Also, the article is not recommended to use as benefits Scheme separately from Script-fu. This is due to the simplistic style of programming in this material and no light other important facts which we are now concerned is much less than the speed of development.

Contents:

    What do we need? the

  1. is Short syntax
  2. the
  3. Variables
  4. the
  5. Functions
  6. the
  7. Lists
  8. the
  9. check the script in GIMP
  10. the
  11. coding
  12. the
  13. Conclusion

What do we need?


English interface: it is enough to create the environment variable "LANG" with value "en". Why is it necessary? First, it will be easier to look for the conformity of the procedures interface objects. Secondly, I don't have to lead the team to two languages. Thirdly, in English on the Internet the most.
the Script-fu Console: Filters → Script-fu → Console. Here we will be able to test small pieces of code that need with the development of the language.
Explorer procedures: Help → Procedure Browser. Here you can easily find the function that performs the desired action and to read the full description (all well documented).
code Editor with highlighting and/or counting matching brackets. Will leave on your taste. I had Notepad++. But remember, the brackets will be a lot!

In the next few sections contain extracts from the first four documentation Script-fu and a bit of ad-libbing. It is strongly recommended to try running the examples below in the console.

brief syntax

    All expressions in Scheme should be surrounded by parentheses. the

  • the function Name always comes first in parentheses, followed by its parameters.
  • the
  • Mathematical operators are also functions.
it's time to give you an example:
the
(* (+ 1 2) (sqrt (- 13 4)) 10)

The latter will be deemed the result of the multiplication. As can be seen, the multiplication function is passed three arguments: the result of adding the result of the root extraction of the difference and the number. Note the number of brackets: they are everywhere compulsory. It can interfere, but it is always clear what is being computed.
    the
  • Function and each argument must be separated from each other by spaces.
Example: "(+ 1 2)" — correct code, "(+1 2)" — is not.
    the
  • Everything that comes after the character ";" is a comment and is ignored.

Variables


Variables in Scheme are defined using the let*. General appearance:
the
(let*
(
(variable value)
...
(variable value)
)
(expression)
...
(expression)
)

When compared with imperative languages, it is something like a Declaration of local variables. In other words, after the brackets, closes the let*, the variables cease to exist.
Example:
the
(let*
(
(a 1)
(b (+ a 2))
)
(+ a b)
)

Another example:
(let*
( (x 9) )
(sqrt x)
)

Please note that even in the case when we define only one variable, external brackets, for a list of variables do not fall!

New value of the variable can be assigned using the set!:
the
(set! the value variable)

Example:
the
(let*
( (a 42) (b 21) (x 0) )
(set! x (/ a b))
)

Options


Their functions can be defined by adding define:
Value of the function is the result of the last command executed in the function code.
Also, the device can calculate the difference modules (). This can be done using the abs, but we will do a little more complicated:
the
(define (difference x y)
(if (x y) (- x y) (- y x))
)

Here we used the if, which tests whether the first its argument, and based on this executes either the second or the third argument (the latter, as you can see, optional).
Please note that the function can treat their arguments as variables, but it changes only their copy. You can verify this in the following way:
the
(let* ((a 3) (b -4)) (list (difference a b) a b))

(The list is used here to output multiple results — values of the function, the variable a variable and b, — and learn more about lists later). Run console and check that the variable values have not changed.

Lists


To define the list, simply write (no commas):
the
'(0 1 1 2 3 5 8 13)

An empty list may be specified via the "'()", and "()". The lists can contain atomic values, as well as other lists:
the
(let*
(
(x
'("GIMP" (1 2 3) ("is" ("the great" () ) ) )
)
)
x
)

Because a single apostrophe we have already written, the inner lists to precede it optionally.
To add to the list another item you need to use the concatenation cons:
the
(cons 1 '(2 3 4) )

It works equally well with an empty list ("(cons 1 () )" will give a list of one element).
To create a list that contains the values of previously declared variables, you will need the list:
the
(let* ( (a 1) (b 2) (c 3) )
(list a b c 4 5)
)

To understand the difference with the definition of the list using an apostrophe, replace "(list a b c 4 5)" to "'(a b c 4 5)" and compare the output.
It's all good, but how do you get the contents of the list? For this there are two functions. First, car, returns the head of list, that is the first element. The second, cdr, returns the tail of the list, i.e. a list containing all elements except the first. Both functions assume the list is nonempty. Examples:
the
(car '(1 2 3 4) )
(cdr '(1 2 3 4) )
(car '(1) )
(cdr '(1) )

Instead of repeatedly calling car and cdr is useful to use functions of type caadr, cddr, etc. for Example, to get the second list item, write the following:
the
(cadr '("first" "second") )
equivalent to
the
(car (cdr '("first" "second") ) )

In the following example, try to get to the element 3, using only two calls to such functions:
the
(let* ( (
x '( (1 2 (3 4 5) 6) 7 8 (9 10) )
) )
; your code here
)

If you have, then you are almost ready to write your first script.

Registration script in GIMP


Before you sit down to write code, to provide convenient conditions.

For scripts user GIMP creates in your home directory to the folder .gimp-2.6/scripts. So the script picked up, enough to put the scm file in the GIMP menu select Filters → Script-fu → Refresh Scripts (that is, if GIMP is already running, otherwise he will load at startup).

File obviously, it is necessary to put written functions. It can contain as many functions, but it would be nice logically different functions spread across different files, and files named after the content. Another recommendation, even the agreement: our functions should be named according to the type of script-fu-functionname.

By and large, this is enough, that we can call our functions from the console.
But if we want to have the script had its own menus, and in his call to open the window with the settings, it is necessary to add two functions responsible for registration. And there is nothing complicated, just look at an example.
Suppose we want to write a function that improves the quality of the text on the image with nonuniform illumination (in fact, I have already written, but this does not prevent us to do it again). Here is its definition:
the
(define (script-fu-readability inImage inLayer inRadius inHigh-input))

I know, I know, there is only function Declaration and it does nothing. The code will be useful later. Now we are and that is enough. Registration happens like so:
the
(script-fu-register
"script-fu-readability"
"Readability"
"Improves text readability on the photos. It's needed only when there is a non-uniform illumination",
"Dragonizer"
"Copyleft, use it at your own sweet will"
"January 7, 2011"
"RGB* GRAY* INDEXED*"
SF-IMAGE "The image" 0
SF-DRAWABLE "The layer" 0
SF-ADJUSTMENT "Median blur: radius" '(15 1 20 1 5 0 SF-SLIDER)
SF-ADJUSTMENT "Levels: intensity of highest input" '(235 0 255 1 10 0 SF-SPINNER)
)
(script-fu-menu-register "script-fu-readability" "< Image > /Filters/User's scripts")

The first function is passed to the next. The first argument is the name of our function, the second is the display name, the description, fourth — the author, the fifth — information about copyright, the sixth — creation date. Seventh — types of supported images (RGB, RGBA, GRAY, GRAYA, INDEXED, INDEXEDA).

Subsequent arguments are optional. They (except for SF-IMAGE and SF-DRAWABLE) allow you to create the script window widgets such as lines, jackdaws, sliders, spinners, color picker, font and more, to convey the user selection to the function. Referred to as SF-IMAGE will give us a link to the current open image, and SF-DRAWABLE — for the selected layer. I won't describe all the SF-*, the settings you can see the tables here (else read is not necessary, because summarized in this article). And I advise you to look this picture to understand what you will need (took here).

The window is ready, it remains to add the call in the menu of GIMP, which makes the second function of the code above. Two arguments: again, the function name and the path to the menu. The path starts with <Image>, if some of the branches previously existed, GIMP will add them.

Another example: if we wanted to write a script that creating image with desired properties, we would have removed the SF-IMAGE and SF-DRAWABLE from the first function instead of "RGB* GRAY* INDEXED*" would use an empty string "" (we don't have the open image, we create it), and the second function would change the path to something like "<Image>/File/Create/Something".

To see the result, save our work in "thescript-fu-readability.scm", and update the scripts. Now open/create any image and choose from the menu to our script.

coding


Here it is, the coveted moment! But hasten to disappoint: there's nothing complicated here. Exactly. Functions you already know how to write. And all you may need from the editor, it is easy to find in the browser procedures. Need some kind of operation with layers? Search by query "layer". Invert the image? You need something that contains "invert". And so on.

I will make only two observations:
    the
  • it would be nice to conclude all the actions performed by the script, between the functions gimp-image-undo-group-start and gimp-image-undo-group-end, as is done below, so that the user does not have to undo each action separately.
  • the
  • All GIMP functions return the result lists, regardless of the amount of data in the result. Easily punctured, waiting for example, layer, and receiving (layer). So don't forget to do car in such cases.
And now an example working code. The algorithm I borrowed here (thanks Killy).
the
(define (script-fu-readability inImage inLayer inRadius inHigh-input)
(let* (
(layer2 0)
)
(gimp-image-undo-group-start inImage)
(if (not (= (car (gimp-image-base-type inImage)) GRAY)) (gimp-image-convert-grayscale inImage))
(set! layer2 (car (gimp-layer-copy inLayer FALSE)))

(plug-in-despeckle RUN-NONINTERACTIVE inImage layer2 inRadius 0 -1 256)
(gimp-layer-set-mode layer2 DIFFERENCE-MODE)
(set! inLayer (car (gimp-image-flatten inImage)))
(gimp-invert inLayer)
(gimp-levels inLayer HISTOGRAM-VALUE 0 inHigh-input 0.1 0 255)
(gimp-image-undo-group-end inImage)
)
)

Having at hand the procedure browser, it is easy to understand here, if you're interested.

Batch processing


Where-where? That's not all. I think we've come this far to write a lousy script that processes one image? Yes hands would be faster! So let's get GIMP to open all files from a given folder to process and save to another folder.

Best of all — to adapt the code below to do something else, just change it calls the function on the right, everything else will not change (well, unless you want to save the file to a different extension).

Code partially borrowed from this topic (thanks Apostol), but it saves the file, overwriting the original. The morph-filename taken otsuda.
the
(define (morph-filename orig-name new-extension)
(let* ((buffer (vector "" "" "")))
(if (re-match "^(.*)[.]([^.]+)$" orig-name buffer)
(string-append (substring orig-name 0 (car (vector-ref buffer 2))) new-extension)
)
)
)

(define (script-fu-batch-readability inInFolder inOutFolder inRadius inHigh-input)
(let* ((filelist (cadr (file-glob (string-append inInFolder DIR-SEPARATOR "*") 1))))
(while (not (null? filelist))
(let* ((filename (car filelist))
(image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
(layer (car (gimp-image-get-active-layer image)))
)
(script-fu-readability image layer inRadius inHigh-input)
(set! layer (car (gimp-image-get-active-layer image)))
(set! filename (string-append inOutFolder DIR-SEPARATOR
(morph-filename (car (gimp-image-get-name image)) "png")))
(file-png-save2 RUN-NONINTERACTIVE image layer filename filename 0 9 0 0 0 1 0 0 0)
(gimp-image-delete image)
)
(set! filelist (cdr filelist))
)
)
)


Opinion


Script Readability with the batch version can be downloaded here (the mirror). Code is commented, even too much.

Once again, that the article is not exhaustive in the broadest sense and is designed only to allow the reader to sit down, carefully read, in parallel with practice, and start creating scripts that solve his problem. So that it takes as much time as required for more or less quality.

If you read the article till the end, now you know how to Script-fu as well as me.
Article based on information from habrahabr.ru

Комментарии

Популярные сообщения из этого блога

Monitoring PostgreSQL + php-fpm + nginx + disk using Zabbix

Templates ESKD and GOST 7.32 for Lyx 1.6.x

Customize your Google