A script consists of the script body, optionally preceded by one or more special blocks which are used to declare variables and control runtime options. We’ll return to these blocks later (skip ahead to Special blocks if you can’t wait), but first let’s look at the general features of the language.
/*
* C-style block comments
* are supported
* in jiffle
*/
// As are line comments
Jiffle supports the following types of variables:
- Scalar
- A single value. In Jiffle all scalar values correspond to Java type Double.
- Array
- A dynamically sized array of scalar values.
- Image
- A variables that stands for a source or destination image in a script.
Note
Support for multi-dimensional arrays is yet to be added.
Jiffle uses lazy declaration for scalar variables. In other words, you can just start using a variable name in the script body. In this snippet:
// The variable val required no prior declaration
val = max(0, image1 - image2);
In contrast, array variables must be declared before use so that Jiffle can distinguish them from scalars:
// declares an empty array
foo = [];
// declares an array with initial values
bar = [1, 2, 42];
Unlike languages such as Ruby, it is invalid to change the type type of a variable within a script:
// Create an array variable
foo = [1, 2, 3];
// Now if you try to use it as a scalar you will get a compile-time error
foo = 42;
// Creating a scalar variable bar, then attempting to do an array operation
// with it (
Variable names must begin with a letter, optionally followed by any combination of letters, digits, underscores and dots. Letters can be upper or lower case. Variable names are case-sensitive.
See also Reserved words
All scalar and list variables which first appear in the body of the script have pixel-scope: their values are discarded after each destination pixel is processed. Variables declared in the init block, when present, have image-scope: their values persist between pixels:
init {
// An image-scope variable with an initial value
foo = 0;
}
// A variable which first appears in the script body
// has pixel scope
bar = 0;
Symbol | Description |
---|---|
^ | Raise to power |
* | Multiply |
/ | Divide |
% | Modulo (remainder) |
+ | Add |
- | Subtract |
= | Assignment |
+= | Additive assignment |
-= | Subtractive assignment |
*= | Multiplicative assignment |
/= | Divisive assignment |
%= | Modulo assignment |
Symbol | Description |
---|---|
&& | logical AND |
|| | logical OR |
^| | logical XOR |
== | equality test |
!= | inequality test |
> | greater than |
>= | greater than or equal to |
less than | |
less than or equal to | |
! | logical complement |
Example:
// set foo to 1 if bar > 10; otherwise 0
foo = bar > 10 ? 1 : 0;
See also Logical functions
You can use the familiar if-else statement in a Jiffle script:
if (foo > 0) n++ ;
if (bar == 42) {
result = 1;
} else {
result = 0;
}
One of the features of Jiffle that makes for concise scripts is that you don’t need to write the code to loop through source and destination images because the runtime system does that for you. So many of your scripts will not need any loop statements. However, Jiffle does provide loop constructs which are useful when working with pixel neighbourhoods or performing iterative calculations.
Probably most of the times when you need to use a loop in a Jiffle script it will be a foreach loop. The general form is:
foreach (var in elements) target
var is a scalar variable that will be set to each value of elements in turn;
elements is an array or sequence (see below);
target is a single statement or a block of code delimited by curly brackets.
This example iterates through a 3x3 pixel neighbourhood and counts the number of values that are greater than a threshold value. It uses sequence notation, which has the form lowValue:highValue. Each loop variable is set to -1, 0, 1 in turn. The loop variables are then used to access a relative pixel position in the source image (see Absolute pixel position):
// Iterate through pixels in a 3x3 neighbourhood
n = 0;
foreach (dy in -1:1) {
foreach (dx in -1:1) {
n += srcimage[dx, dy] > someValue;
}
}
Here is the same example, but this time using the array form of the foreach loop:
// Iterate through pixels in a 3x3 neighbourhood
delta = [-1, 0, 1];
n = 0;
foreach (dy in delta) {
foreach (dx in delta) {
n += srcimage[dx, dy] > someValue;
}
}
A conditional loop which executes the target statement or block while its conditional expression is non-zero:
ynbr = y() - 500;
total = 0;
while (ynbr
A conditional loop which executes the target statement or block until its conditional expression is non-zero:
ynbr = y() - 500;
total = 0;
until (ynbr > y() + 500) {
xnbr = x() - 500;
until (xnbr > x() + 500) {
total += srcimage[$xnbr, $ynbr];
xnbr += 100;
}
ynbr += 100;
}
Jiffle provides the break statement to unconditionally exit a loop:
n = 0;
foreach (i in 1:10) {
if (foo[i] != null) {
n++ ;
} else {
break;
}
}
There is also a breakif statement:
n = 0;
foreach (i in 1:10) {
breakif(foo[i] == null);
n++ ;
}
Name | Description | Arguments | Returns | Notes |
---|---|---|---|---|
abs(x) | Absolute value | double value | absolute value of x | |
acos(x) | Arc-cosine | value in range [-1,1] | angle in radians | |
asin(x) | Arc-sine | value in range [-1,1] | angle in radians | |
atan(x) | Arc-tangent | value in range [-1,1] | angle in radians | |
cos(x) | Cosine | angle in radians | cosine [-1, 1] | |
degToRad(x) | Degrees to radians | angle in radians | angle in degrees | |
exp(x) | Exponential | double value | e to the power x | |
floor(x) | Floor | double value | integer part of x as a double | |
isinf(x) | Is infinite | double value | 1 if x is positive or negative infinity; 0 otherwise | |
isnan(x) | Is NaN | double value | 1 if x is equal to Java’s Double.NaN; 0 otherwise | |
isnull(x) | Is null | double value | 1 if x is null; 0 otherwise | Equivalent to isnan(x) |
log(x) | Natural logarithm | positive value | logarithm to base e | |
log(x, b) | General logarithm | x: positive value; b: base | logarithm to base b | |
radToDeg(x) | Radians to degrees | angle in radians | angle in degrees | |
rand(x) | Pseudo-random number | double value | value in range [0, x) | Volatile function |
randInt(x) | Pseudo-random number | double value | integer part of value in range [0, x) | Equivalent to floor(rand(x)) |
round(x) | Round | double value | rounded value | |
round(x, n) | Round to multiple of n | x: double value; n: whole number | value rounded to nearest multiple of n | E.g. round(44.5, 10) returns 40 |
sin(x) | Sine | angle in radians | sine [-1, 1] | |
sqrt(x) | Square-root | non-negative value | square-root of x | |
tan(x) | Tangent | angle in radians | double value |
Name | Description | Arguments | Returns |
---|---|---|---|
con(x) | Conditional | double value | 1 if x is non-zero; 0 otherwise |
con(x, a) | Conditional | double values | a if x is non-zero; 0 otherwise |
con(x, a, b) | Conditional | double values | a if x is non-zero; b otherwise |
con(x, a, b, c) | Conditional | double values | a if x is positive; b if x is zero; c if x is negative |
Name | Description | Arguments | Returns |
---|---|---|---|
max(x, y) | Maximum | double values | maximum of x and y |
max(ar) | Maximum | array | maximum of array values |
mean(ar) | Mean | array | mean of array values |
min(x, y) | Minimum | double values | minimum of x and y |
min(ar) | Minimum | array | minimum of array values |
median(ar) | Median | array | median of array values |
mode(ar) | Mode | array | mode of array values |
range(ar) | Range | array | range of array values |
sdev(ar) | Standard deviation | array | sample standard deviation of array values |
sum(ar) | Sum | array | sum of array values |
variance(ar) | Variance | array | sample variance of array values |
Name | Returns |
---|---|
height() | Height of the processing area (world units) |
width() | Width of the processing area (world units) |
xmin() | Minimum X ordinate of the processing area (world units) |
ymin() | Minimum Y ordinate of the processing area (world units) |
xmax() | Maximum X ordinate of the processing area (world units) |
ymax() | Maximum Y ordinate of the processing area (world units) |
x() | X ordinate of the current processing position (world units) |
y() | Y ordinate of the current processing position (world units) |
xres() | Pixel width (world units) |
yres() | Pixel height (world units) |
Used to set options for Jiffle’s runtime behaviour. Presently, only the outside option is supported.
For example, this tells Jiffle to return a value of 0 for any pixel value request that falls outside the bounds of the source image:
options {
outside = 0;
}
The following script retrieves the maximum value in a 3x3 kernel centred on each source image and writes it to the destination image. It uses the outside option to treat kernel locations beyond the source image’s edge as null values which will be ignored by the max function:
// This script implements a max filter with a 3x3
// neighbourhood (kernel)
// Set option to treat locations outside the source image
// area as null values
options { outside = null; }
foreach (dy in -1:1) {
foreach (dx in -1:1) {
values
If the outside option is not set, any request for a value beyond an image’s bounds will cause a JiffleRuntimeException.
Used to associate variables with source (read-only) and destination (write-only) images. Example:
images {
foo = read;
bar = read;
result = write;
}
As shown in the above snippet, the block contains declarations of the form name = (read | write). If this block is provided, the Jiffle compiler expects that it contains declarations for all image variables used in the script. It not provided, variable names can be defined as representing source or destination images using methods provided by the Jiffle and JiffleBuilder classes. These methods are described further in The Jiffle run-time system.
This block declares variables that will have image scope during processing (as discussed in Scope).
Each variable can optionally be assigned an intial value as foo is here:
init {
foo = 42;
bar;
}
If an initial value is not provided, one must be injected at run-time. See XXXX for more details.
Pixel position and image band are specified using square bracket notation.
Absolute positions are specified using a $ prefix (similar to the syntax used in some spreadsheet programs):
// Example: access the value at x=50 y=42
value = srcimage[ $50, $42 ];
Variables and expressions can also appear in the brackets:
value = srcimage[ $xpos, $(min(width() - 1, y() + 10)) ];
When values are not prefixed they are treated as offsets, relative to the current processing position:
// Example: access the value at x+2, y-1
value = srcimage[ 2, -1 ];
As with absolute positions, variables and expressions can also be used:
value = srcimage[ dx, dy ];
The image band is specified as a single value, variable or expression in square brackets. It is always treated as an absolute specifier:
// Get value from band 2 at the current processing position
value = srcimage[ 2 ];
As with pixel position, the band can be specified using a variable or an expression.
When specifying both band and pixel position, the band comes first:
// Get the value for band 1, pixel position x=50, y=42
value = srcimage[ 1 ][ $50, $42 ]
// Get the value for band 1 at offset dx=-1, dy=3
value = srcimage[ 1 ][ -1, 3 ]
The following are reserved words in Jiffle and may not be used as variable names:
- boolean^{a}
- break
- breakif
- con
- double^{a}
- else
- false
- float^{a}
- foreach
- if
- images
- in
- init
- int^{a}
- null
- options
- read
- true
- until
- while
- write
^{a} reserved for future use