Imglib2 development: a question about Img<T>

Hi, I’m developing a very simple plug-in in ij2. I’m not very familiar with Imglib2, so there is a problem in my code:
I have writter a median filterting function, however, the type of data cannot be recognized (Img ).

Here is my code:

package com.HIT.Weisong;
import net.imagej.Dataset;
import net.imagej.ImageJ;
import net.imagej.ops.OpService;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.Img;
import net.imglib2.type.numeric.RealType;
import org.scijava.command.Command;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.ui.UIService;

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * This example illustrates how to create an ImageJ {@link Command} plugin.
 * <p>
 * The code here is a simple Gaussian blur using ImageJ Ops.
 * </p>
 * <p>
 * You should replace the parameter fields with your own inputs and outputs,
 * and replace the {@link run} method implementation with your own logic.
 * </p>
 */
@Plugin(type = Command.class, menuPath = "Plugins>Gauss Filtering")
public class SparseHessianReconstruction<T extends RealType<T>> implements Command {
	//
	// Feel free to add more parameters here...
	//

	@Parameter
	private Dataset currentData;

	@Parameter
	private UIService uiService;

	@Parameter
	private OpService opService;

	@Override
	public void run() {

		final Img<T> image = (Img<T>)currentData.getImgPlus();

		//
		// Enter image processing code here ...
		// The following is just a Gauss filtering example
		//

		List<RandomAccessibleInterval<T>> results = new ArrayList<>();
                 results.add(medianFiltering(image,3,3)); 


	// display result
	for (RandomAccessibleInterval<T> elem : results)
	{
		uiService.show(elem);
	}
}

public static int[] medianFiltering(int pix[], int w, int h) {  
	int newpix[] = new int[w*h];  
	int[] temp = new int[9];  
	ColorModel cm = ColorModel.getRGBdefault();  
	int r=0;  
	for(int y=0; y<h; y++) {  
		for(int x=0; x<w; x++) {  
			if(x!=0 && x!=w-1 && y!=0 && y!=h-1) {  
				//g = median[(x-1,y-1) + f(x,y-1)+ f(x+1,y-1)  
				//  + f(x-1,y) + f(x,y) + f(x+1,y)  
				//  + f(x-1,y+1) + f(x,y+1) + f(x+1,y+1)]                     
				temp[0] = cm.getRed(pix[x-1+(y-1)*w]);   
				temp[1] = cm.getRed(pix[x+(y-1)*w]);  
				temp[2] = cm.getRed(pix[x+1+(y-1)*w]);  
				temp[3] = cm.getRed(pix[x-1+(y)*w]);  
				temp[4] = cm.getRed(pix[x+(y)*w]);  
				temp[5] = cm.getRed(pix[x+1+(y)*w]);  
				temp[6] = cm.getRed(pix[x-1+(y+1)*w]);  
				temp[7] = cm.getRed(pix[x+(y+1)*w]);  
				temp[8] = cm.getRed(pix[x+1+(y+1)*w]);  
				Arrays.sort(temp);  
				r = temp[4];  
				newpix[y*w+x] = 255<<24 | r<<16 | r<<8 |r;  
			} else {  
				newpix[y*w+x] = pix[y*w+x];  
			}  
		}  
	}  
	return newpix;  
}   


/**
 * This main function serves for development purposes.
 * It allows you to run the plugin immediately out of
 * your integrated development environment (IDE).
 *
 * @param args whatever, it's ignored
 * @throws Exception
 */
public static void main(final String... args) throws Exception {
	// create the ImageJ application context with all available services
	final ImageJ ij = new ImageJ();
	ij.ui().showUI();

	// ask the user for a file to open
	final File file = ij.ui().chooseFile(null, "open");

	if (file != null) {
		// load the dataset
		final Dataset dataset = ij.scifio().datasetIO().open(file.getPath());

		// show the image
		ij.ui().show(dataset);

		// invoke the plugin
		ij.command().run(SparseHessianReconstruction.class, true);
	}
}

}

How can I change the medianFiltering method to work on Img objects instead of int arrays; and if there is a numpy type operation, to handle the three dimensional data like a[100][100][20].

Many thanks!

Hi @WeisongZhao,

If you’re going to be programming with imglib2 in earnest, I highly recommend you start at the beginning, and work through these examples.

This example: (NeighborhoodExample.java), computes the maximum over local neighborhoods, and is not far from what you’ll want. However, it may be tough to understand without knowing the philosophy and design decisions of imglib2.

The introductory examples above are very helpful to get there.

After that diversion, the “straightforward answer” is that, to get the value of the image img at (x,y,z) you can do this:

Img<T> img = ...
access = img.randomAccess();
access.setPosition( new int[]{x, y, z});
T value = access.get();

but it’s kind-of verbose, since the “imglib2-way” of doing things is often different - e.g. the Neighborhood example shows this.

John

5 Likes

Than you for your reply!, I have done my plug-in with ij1 earlier. imglib2 is a little complicated for me, so I have to use ij1. I have seen many plugins developed by ij2, and I want to do the same thing.
However, I cannot find examples (like ij1) such as :https://imagej.nih.gov/ij/plugins/download/Haar_wavelet_filter.java
Do you know how to find this kind of source code of ij2?

Please note that a similar version of this question was posted on GitHub here:

@WeisongZhao In addition to @bogovicj’s suggestions, which I wholeheartedly second, please be aware also of the ImageJ tutorial notebooks here:

https://imagej.github.io/tutorials/

Two in particular cover ImgLib2:

The “ImgLib2 in Detail” notebook is just a recasting of the ImgLib2 Examples linked above to a Jupyter notebook—but is very nice if you want to play with the code interactively without needing to install an IDE like Eclipse locally. (Although if you do want to play with the code in Eclipse, you can clone the imglib/imglib2-tutorials repository containing this same code, and run them that way.

3 Likes

@WeisongZhao: I made a function that calculates the median using imglib2. It wants an iterable interval as input. You could make an iterable interval on your image like this:

 import java.util.Arrays;
 import net.imglib2.IterableInterval;
 import net.imglib2.RandomAccessibleInterval;
 import net.imglib2.img.Img;
 import net.imglib2.type.numeric.RealType;
 import net.imglib2.view.Views;

 long[] offset = {0,0,0}; //corner of ROI {x,y,fr}
 long[] dimensions = {3,3,1}; //size of ROI {x,y,fr}
 RandomAccessibleInterval<T> myROI2 = Views.offsetInterval(image, offset,dimensions);
 IterableInterval<T> myIterableROI2 = Views.iterable( myROI2 );
double medianValue = computeMedian( myIterableROI2 );

public < T extends RealType< T > > double computeMedian( final Iterable< T > input )
    {
        int count = 0;
        for ( @SuppressWarnings("unused") final T type : input )
        {
            ++count;
        }
        double[] data = new double[count];
        count = 0;
        for ( final T type : input )
        {
        	data[count]=type.getRealDouble();
            ++count;
        }
        Arrays.sort(data);
        int middle = data.length/2;
        if (data.length%2 == 1) {
        	return data[middle];
        } else {
            return (data[middle-1] + data[middle]) / 2.0;
        }
    }

I hope this is more “the imglib2-way” but would welcome any advice.

2 Likes

That will be helpful. Does the imglib2 cut the ui and algorithm, so there is no code like the https://imagej.nih.gov/ij/plugins/download/Haar_wavelet_filter.java but the algorithm code and imglib2 core?

Thany you very much!! It will be helpful.

@rharkes Thanks for showing ImgLib2-based code. Much appreciated. I will point out though that your code makes a complete copy of the entire image, which will fail if the image is large and there is insufficient available RAM. With quantile / rank selection ops like median, there are potentially better ways to go, such as median-of-medians, quickselect or introselect. I found that Google Guava implements performant quantile operations along these lines and whipped up some code illustrating how to adapt ImgLib2 data structures such that it can be used:

This necessitated inventing adapter classes that go from RandomAccessibleInterval<RealType> to List<Double> and/or IterableInterval<RealType> to Collection<Double>, so that the standard collections objects can be passed to Guava. This code is just a proof of concept, but I opened a discussion with the ImgLib2 developers on Gitter to decide if this code might be useful to incorporate into the core ImgLib2 library somehow. In the meantime, please feel welcome to use it (consider it Unlicensed / public domain).

Relatedly, as an aside: the ImageJ Ops library has a stats.median op that invokes the stats.quantile op at 0.5, as one might expect. I figured the Ops code probably did something clever to avoid copying the entire image, but alas, it also makes a copy, and furthermore uses ArrayList<Double> which is memory inefficient. I filed imagej/imagej-ops#596 to document the need for improvements there.

4 Likes

Thank you! I think a bridge to Guava can be really usefull. My code indeed not only copies, but also converts everything to 64 bit values. I worked on a fast temporal median filter for 16-bit values that needed to operate on images as big as 400x400x14k pixels. My code on this forum would not work for sure. This one does, but is not imglib2 at all.

A generic median filter ops might need a different algorithm for floating point or integer values. With an odd window-size you can return the same type of data. A sliding median filter would again require a different algorithm.

2 Likes