Using commands arguments and variables Log in to Edit

David Tschumperlé Jerome Ferrari jayprich garagecoder

G'MIC defines a full-featured script language for image processing, and as in many computer languages, you can define and set variables and use them in your own commands.
Here we describe how all this works.

1. Commands arguments.

G'MIC commands can take one or several arguments, separated by commas.
In a custom command, you can use expressions $1,$2,.. to access the value of these arguments. Arguments are always considered as strings.

test :
  -echo "Arguments : $1,$2,$3"

2. Variables

2.1. Single variables

2.1.1 Set/get a variable value

In a G'MIC script, the way to set a variable is :

foo=0

(Don't insert any spaces around the = sign !)

The variable foo does not need to be declared previously. If it already exists, its value is replaced with the new one (here 0), otherwise the memory slot for the variable is created and assigned.
A G'MIC variable does not have a type, it is always a string. If you assign the number 0.5 to a variable, you actually assign the string "0.5" to it.

To access the content of a variable foo, use the expression $foo or ${foo}, as in the example below

test :
  foo="Mary"               # Assign variable 'foo'.
  -echo "Hello "$foo" !"   # Display it on the output stream.
  foo="John Doe"           # Re-assign variable 'foo'.
  -echo "Hello "${foo}" !" # Display it on the output stream.

Running this example generates :

$ gmic -test
[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Set local variable foo='Mary'.
[gmic]-0./test/ Hello Mary !
[gmic]-0./test/ Set local variable foo='John Doe'.
[gmic]-0./test/ Hello John Doe !
[gmic]-0./ End G'MIC parser.

Note that the expressions $foo and ${foo} are G'MIC substituted expressions, therefore they won't be evaluated when placed between double quotes. That's why we break the string to allow the evaluation of the variable $foo in the -echo command.

Valid characters for variable names are a-z, A-Z ,0-9 and _. A variable name cannot start with a digit. The length of a variable name is limited to 255 characters.

2.1.2. Using numerical variables

G'MIC variables are always strings, so there are no "numerical variables" at all. But of course, you can assign numbers to variables and do some computations with them, as in this example :

test :
  angle=0
  -v - -do
    rad={$angle*pi/180}
    cos={cos($rad)}
    sin={sin($rad)}
    -v + -echo "cos("$angle"°)^2+sin("$angle"°)^2 = "{$cos^2+$sin^2} -v -
    angle={$angle+45}
  -while {$angle<360} -v +

will generate :

$ gmic -testhttp://xkcd.com/1145/

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Set local variable angle='0'.
[gmic]-0./test/*do/ cos(0°)^2+sin(0°)^2 = 1
[gmic]-0./test/*do/ cos(45°)^2+sin(45°)^2 = 1
[gmic]-0./test/*do/ cos(90°)^2+sin(90°)^2 = 1
[gmic]-0./test/*do/ cos(135°)^2+sin(135°)^2 = 1
[gmic]-0./test/*do/ cos(180°)^2+sin(180°)^2 = 1
[gmic]-0./test/*do/ cos(225°)^2+sin(225°)^2 = 1
[gmic]-0./test/*do/ cos(270°)^2+sin(270°)^2 = 1
[gmic]-0./test/*do/ cos(315°)^2+sin(315°)^2 = 1
[gmic]-0./ End G'MIC parser.

In this example, we use (several times) the substituting expression {expr} to compute numerical values involving variables. Once you start writing your own G'MIC scripts, it's very likely you'll have to use this kind of expression.

2.1.3 Scopes and order of evaluation.

By default, a G'MIC variable is local, which means that its scope is limited to the command where it is defined.
The example below :

test : 
  name="Mary".
  -try_to_change_name
  -echo "My name is "$name

try_to_change_name :
  -echo "I'd like to be John Doe, not '"$name"' !"
  name="John Doe"

will actually output :

$ gmic -test

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Set local variable name='Mary.'.
[gmic]-0./test/try_to_change_name/ I'd like to be John Doe, not '' !
[gmic]-0./test/try_to_change_name/ Set local variable name='John Doe'.
[gmic]-0./test/ My name is Mary.
[gmic]-0./ End G'MIC parser.

Here, the first line of the command -try_to_change_name tries to access to the variable $name which is not defined. The evaluation of this non-defined variable therefore returns an empty string.

If we really want the command -try_to_change_name to change the value of the name variable, we must consider a global variable for the name instead. A global variable can be read and written anywhere and is shared by all the commands.
A global variable is just like a local variable but has a name starting with an underscore _. The example above must be then modified like this :

test : 
  _name="Mary".
  -try_to_change_name
  -echo "My name is "$_name

try_to_change_name :
  -echo "I'd like to be John Doe, not '"$_name"' !"
  _name="John Doe"

And the result becomes :

$ gmic -test

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Set global variable _name='Mary.'.
[gmic]-0./test/try_to_change_name/ I'd like to be John Doe, not Mary. !
[gmic]-0./test/try_to_change_name/ Set global variable _name='John Doe'.
[gmic]-0./test/ My name is John Doe
[gmic]-0./ End G'MIC parser.

Simple right ?

Now, there are two remaining things to know about variables :

  • If you try to access the value of a variable that is not defined, but whose name is the one of an image of the list, the returned value will be the position of this image in the list.
  • If the variable name does not exist and is not the name of an image of the list, but is defined as an environment variable of the system, then the value of this environment variable is returned. For instance, on Linux, the following command:
test :
  -testimage2d 300 -name[-1] test_image
  -echo "Test image is image \["$test_image"\]."
  -echo "Home directory is '"$HOME"'."
  -remove

will output:

$ gmic -test

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Input 2d synthetic image of size 300x300x3.
[gmic]-1./test/ Set name of image [0] to 'test_image'.
[gmic]-1./test/ Test image is image [0].
[gmic]-1./test/ Home directory is '/home/username'.
[gmic]-1./test/ Remove image [0] (0 images left).
[gmic]-0./ End G'MIC parser.

2.2. Arrays of variables

2.2.1. Simple arrays

G'MIC does not have explicit mechanisms for creating arrays of variables, but it can be done easily with a simple trick : you just define as many numbered variables you need to store your array of values, like this :

test :
  -repeat 5 array_$>={round(?(100))} -done   # Create array of 10 variables with random values
  sum=0
  -repeat 5 sum={$sum+${array_$>}} -done     # Compute the sum of all values of the array.

will output :

$ gmic -test

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/*repeat/ Set local variable array_0='68'.
[gmic]-0./test/*repeat/ Set local variable array_1='90'.
[gmic]-0./test/*repeat/ Set local variable array_2='23'.
[gmic]-0./test/*repeat/ Set local variable array_3='24'.
[gmic]-0./test/*repeat/ Set local variable array_4='0'.
[gmic]-0./test/ Set local variable sum='0'.
[gmic]-0./test/*repeat/ Set local variable sum='68'.
[gmic]-0./test/*repeat/ Set local variable sum='158'.
[gmic]-0./test/*repeat/ Set local variable sum='181'.
[gmic]-0./test/*repeat/ Set local variable sum='205'.
[gmic]-0./test/*repeat/ Set local variable sum='205'.
[gmic]-0./ End G'MIC parser.

As the name of the variables to access are evaluated on the fly, you can do these kinds of things.

2.2.2. Connecting commands arguments and variables

There is one useful expression that can be used in a G'MIC command to automatically generate an 'array of variables' from the given commands arguments. This expression is $=varname. This simple example shows what it does :

test :
  $=argument_

then,

$ gmic -test Mary,John,Jenny,Marc

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/ Set local variable argument_0='test'.
[gmic]-0./test/ Set local variable argument_1='Mary'.
[gmic]-0./test/ Set local variable argument_2='John'.
[gmic]-0./test/ Set local variable argument_3='Jenny'.
[gmic]-0./test/ Set local variable argument_4='Marc'.
[gmic]-0./ End G'MIC parser.

It initializes an array of local or global variables with all the commands arguments passed to the command. This is interesting, because as variable names are evaluated on the fly, you can now also get a command argument whose number is not known when writing the command. For instance, the following command output 5 random strings picked from the list of given arguments :

test :
  -v - $=argument
  -repeat 5
    -v + -echo ${argument{round(?(1,$#))}} -v -
  -done -v +

gives :

$ gmic -test Mary,John

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/*repeat/ John
[gmic]-0./test/*repeat/ John
[gmic]-0./test/*repeat/ Mary
[gmic]-0./test/*repeat/ John
[gmic]-0./test/*repeat/ Mary
[gmic]-0./ End G'MIC parser.

but also

$ gmic -test Mary,John,Angus,Brian,Joly,Pamela,Paul

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/*repeat/ Paul
[gmic]-0./test/*repeat/ Pamela
[gmic]-0./test/*repeat/ Joly
[gmic]-0./test/*repeat/ Brian
[gmic]-0./test/*repeat/ Joly
[gmic]-0./ End G'MIC parser.

This kind of dynamic reading of commands arguments could not be possible without connecting them to variables.

2.2.3. Creating associated arrays

The same kind of techniques applies for creating associative arrays, where the key of each element is put in a variable name. Here we benefit from the flexibility of the way command arguments are managed by G'MIC, to create an associated array _gender :

test :
  -create_gender_table "John","Male",\
                       "Mary","Female",\
                       "Henry","Male",\
                       "Stephen","Male",\
                       "Jenny","Female"

create_gender_table :
  -v - $=var -v +
  -repeat {$#/2} _gender_${var{2*$>+1}}=${var{2*$>+2}} -done

will output :

$ gmic -test

[gmic]-0./ Start G'MIC parser.
[gmic]-0./test/create_gender_table/*repeat/ Set global variable _gender_John='Male'.
[gmic]-0./test/create_gender_table/*repeat/ Set global variable _gender_Mary='Female'.
[gmic]-0./test/create_gender_table/*repeat/ Set global variable _gender_Henry='Male'.
[gmic]-0./test/create_gender_table/*repeat/ Set global variable _gender_Stephen='Male'.
[gmic]-0./test/create_gender_table/*repeat/ Set global variable _gender_Jenny='Female'.
[gmic]-0./ End G'MIC parser.