Monday, August 22, 2011

Using an iterator in ImageJ

Scanning all the pixels of an image to modify values or get some features is a common task when you develop scripts in ImageJ ... unfortunately, if your code contains too many loops, it will become difficult to read and debug . This can be improved using the function Process > Math > Macro... mimicking an iterator object found in modern programming languages.

Assuming that your algorithm contains something like that (written in pseudo-code):

for each pixel of the image
do
  process_pixel
end

1- Using loops

One or two loops can be used in ImageJ to scan and read each pixel value. The following script presents the two versions and modifies each pixel (in the function process_pixel).
+++ IJ snippet: script #1 +++

+++ end of IJ snippet +++

The function process_pixel() just calculates the remainder of a division by 7and is absolutely not interesting.

2- An image iterator in ImageJ

There is another alternative than loop(s): the iterator. In programming languages, the iterator is a way to scan entirely an array and get its contents, a sort of specialized loop. In Java, the iterator uses mainly two methods: next() to get the next element in a list and hasNext() to check if the end is reached (in this case, hasNext() returns false).

ArrayList arr = new ArrayList(); // Create a list
it = arr.iterator();             // Get the iterator from the list
while (it.hasNext() )            // Loop over the whole list
{
  process(it.next());            // Process the element
}                                // End of loop

Such a system of iterator exists in ImageJ for images (not for arrays) and is available in Process > Math > Macro... . As an element of comparison, the  script #1 becomes:

 run("Macro...", "  code=[process pixel]");

The code enclosed between brackets is executed for each pixel of the active image and you don't care about loops.

3- Syntax

In the function Macro..., specific keywords are used for manipulating the pixel(s):
  • The current pixel value is called 'v' and is defined by its three cartesian coordinates (x,y,z) or by its two polar coordinates (alpha, distance). 
  • Two other keywords defining the image dimensions: 'w' (width) and 'h' (height) (h), respectively.
All kind of operators and arithmetic functions (trigonometric,etc) are accepted. Moreover, this iterator is not limited to simple assignment of the pixel value, it also accepts the conditional statement 'if' allowing complex processes.
If your code contains space characters, we need to enclose it by brackets.

3- Examples

3-1- Vertical ramp
To fill a 32-bit 256x256 image with values from 0.0 to 256.0 for simulating a vertical ramp effect, the code is something like that:

Example 1.1: vertical_ramp.ijm                                                      
newImage("test", "32-bit Black", 50, 256, 1);
w=getWidth();
for (y=0;y<height;y++)
{
  for (x=0;x<width;x++)
  {
    pix=y;
    setPixel(x,y,pix);
  }
}

With the iterator, the same code is now:

Example 1.2: vertical_ramp_iterator.ijm                                        
newImage("test", "32-bit Black", 50, 256, 1);
run("Macro...", "code=v=y");

You only keep the code enclosed in the loops

3.2. Modify one row (or column)
By using a conditional statement, you can limit the modification to a given row (or column).

Example 1.3: row_iterator.ijm                                        
newImage("test", "32-bit Black", 50, 256, 1);
run("Macro...", "code=[if (y==20) v=y;]");


4- Conclusion


This system of iterator is really powerful to modify an image specially if you use an image as an array.

No comments:

Post a Comment