Imagej/example-legacy-plugin: basic questions and problems

Hi to all!
I am having major problems using the above quoted git project as a starting point for the development of ImageJ 1.0 plugins. Although it claims to be an “ideal starting point to develop new ImageJ 1.x plugins in an IDE of your choice” it turned out to be very difficult to handle for me.
I am using eclipse, and maven and git work (after fixing several issues).
Now the example-legacy-plugin as well as my personal plugin can be built within eclipse, but when I want to transfer plugins to another ImageJ environment, I have to change some things like e.g. the package import (In the project I left “com.mycompany.imagej” which was inherited by the example-legacy-plugin-project. Therefore I had to go with “package com.mycompany.imagej;”).
I guess a more experienced developer could explain the philosophy of the imagej/example-legacy-plugin-project.
Best regards
Richard

Hello Richard -

Let me offer a minimalist, minority viewpoint on how to make
your life simpler.

I find that all of the eclipses and gits and POMs and BOMs
and MOMs (and MANIFESTs), while perhaps useful for enterprise
software, tend to obscure and complicate the simple. Each
one is one more thing that has to be configured, and one more
opportunity for something to go wrong.

You don’t need them for a single, simple plugin. (“Don’t use
a sledgehammer to nail a tack; use your head, instead.”)

I’ve tweaked the example-plugin code. I commented out the
package declaration (to avoid the need to set up package
subdirectories) and commented out the main function, as
I wasn’t using it. (Code appears below.)

We then compile the .java file to produce a .class file:

javac -cp .:/<path_to_fiji_installation>/Fiji.app/jars/* Process_Pixels.java

and then copy the .class file to Fiji / ImageJ’s plugins
directory:

cp Process_Pixels.class /<path_to_fiji_installation>/Fiji.app/plugins

(We assume that the command “javac” is in your path. “cp” is
the unix copy command – on windows you would use “copy” and
backslashes for the directory separators.)

If you now launch Fiji / ImageJ you should find Process Pixels
in the Plugins menu, i.e.:
Plugins > Process Pixels.

(You have to relaunch Fiji / ImageJ after you add or update
a plugin so that it will load the current version.)

You can also use a .jar file if you want – by preference,
or if your plugin uses additional files (other .class files,
plugins.config, etc.).

To do this we create a .jar file containing Process_Pixels.class
and copy it to Fiji / ImageJ’s plugins directory:

jar cfM Process_Pixels.jar Process_Pixels.class
cp Process_Pixels.jar /<path_to_fiji_installation>/Fiji.app/plugins

(You will want to have either Process_Pixels.class or
Process_Pixels.jar in the plugins directory, but not both.)

Here is the tweaked Process_Pixels.java code:

// tweaked version of Process_Pixels.java
/*
 * To the extent possible under law, the ImageJ developers have waived
 * all copyright and related or neighboring rights to this tutorial code.
 *
 * See the CC0 1.0 Universal license for details:
 *     http://creativecommons.org/publicdomain/zero/1.0/
 */

// for simiplicity, don't specify a package
// the (an) "unnamed" package will be used by default
// package com.mycompany.imagej;

import ij.IJ;
import ij.ImageJ;
import ij.ImagePlus;
import ij.gui.GenericDialog;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;

/**
 * A template for processing each pixel of either
 * GRAY8, GRAY16, GRAY32 or COLOR_RGB images.
 *
 * @author Johannes Schindelin
 */
public class Process_Pixels implements PlugInFilter {
	protected ImagePlus image;

	// image property members
	private int width;
	private int height;

	// plugin parameters
	public double value;
	public String name;

	@Override
	public int setup(String arg, ImagePlus imp) {
		if (arg.equals("about")) {
			showAbout();
			return DONE;
		}

		image = imp;
		return DOES_8G | DOES_16 | DOES_32 | DOES_RGB;
	}

	@Override
	public void run(ImageProcessor ip) {
		// get width and height
		width = ip.getWidth();
		height = ip.getHeight();

		if (showDialog()) {
			process(ip);
			image.updateAndDraw();
		}
	}

	private boolean showDialog() {
		GenericDialog gd = new GenericDialog("Process pixels");

		// default value is 0.00, 2 digits right of the decimal point
		gd.addNumericField("value", 0.00, 2);
		gd.addStringField("name", "John");

		gd.showDialog();
		if (gd.wasCanceled())
			return false;

		// get entered values
		value = gd.getNextNumber();
		name = gd.getNextString();

		return true;
	}

	/**
	 * Process an image.
	 * <p>
	 * Please provide this method even if {@link ij.plugin.filter.PlugInFilter} does require it;
	 * the method {@link ij.plugin.filter.PlugInFilter#run(ij.process.ImageProcessor)} can only
	 * handle 2-dimensional data.
	 * </p>
	 * <p>
	 * If your plugin does not change the pixels in-place, make this method return the results and
	 * change the {@link #setup(java.lang.String, ij.ImagePlus)} method to return also the
	 * <i>DOES_NOTHING</i> flag.
	 * </p>
	 *
	 * @param image the image (possible multi-dimensional)
	 */
	public void process(ImagePlus image) {
		// slice numbers start with 1 for historical reasons
		for (int i = 1; i <= image.getStackSize(); i++)
			process(image.getStack().getProcessor(i));
	}

	// Select processing method depending on image type
	public void process(ImageProcessor ip) {
		int type = image.getType();
		if (type == ImagePlus.GRAY8)
			process( (byte[]) ip.getPixels() );
		else if (type == ImagePlus.GRAY16)
			process( (short[]) ip.getPixels() );
		else if (type == ImagePlus.GRAY32)
			process( (float[]) ip.getPixels() );
		else if (type == ImagePlus.COLOR_RGB)
			process( (int[]) ip.getPixels() );
		else {
			throw new RuntimeException("not supported");
		}
	}

	// processing of GRAY8 images
	public void process(byte[] pixels) {
		for (int y=0; y < height; y++) {
			for (int x=0; x < width; x++) {
				// process each pixel of the line
				// example: add 'number' to each pixel
				pixels[x + y * width] += (byte)value;
			}
		}
	}

	// processing of GRAY16 images
	public void process(short[] pixels) {
		for (int y=0; y < height; y++) {
			for (int x=0; x < width; x++) {
				// process each pixel of the line
				// example: add 'number' to each pixel
				pixels[x + y * width] += (short)value;
			}
		}
	}

	// processing of GRAY32 images
	public void process(float[] pixels) {
		for (int y=0; y < height; y++) {
			for (int x=0; x < width; x++) {
				// process each pixel of the line
				// example: add 'number' to each pixel
				pixels[x + y * width] += (float)value;
			}
		}
	}

	// processing of COLOR_RGB images
	public void process(int[] pixels) {
		for (int y=0; y < height; y++) {
			for (int x=0; x < width; x++) {
				// process each pixel of the line
				// example: add 'number' to each pixel
				pixels[x + y * width] += (int)value;
			}
		}
	}

	public void showAbout() {
		IJ.showMessage("ProcessPixels",
			"a template for processing each pixel of an image"
		);
	}


// get rid of main -- debuggers are for folks who can't read the .class file
	// /**
	//  * Main method for debugging.
	//  *
	//  * For debugging, it is convenient to have a method that starts ImageJ, loads
	//  * an image and calls the plugin, e.g. after setting breakpoints.
	//  *
	//  * @param args unused
	//  */
	// public static void main(String[] args) throws Exception {
	// 	// set the plugins.dir property to make the plugin appear in the Plugins menu
	// 	// see: https://stackoverflow.com/a/7060464/1207769
	// 	Class<?> clazz = Process_Pixels.class;
	// 	java.net.URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
	// 	java.io.File file = new java.io.File(url.toURI());
	// 	System.setProperty("plugins.dir", file.getAbsolutePath());

	// 	// start ImageJ
	// 	new ImageJ();

	// 	// open the Clown sample
	// 	ImagePlus image = IJ.openImage("http://imagej.net/images/clown.jpg");
	// 	image.show();

	// 	// run the plugin
	// 	IJ.runPlugIn(clazz.getName(), "");
	// }
}

(By the way, I copied the code from the github web interface and
pasted it into my text editor.)

Thanks, mm

Thanks a lot for this long reply and all the effort you have put in to give a good and easy starting point. I agree that there are simpler and more efficient ways (especially for smaller projects) to setup the environment to generate an ImageJ plugin!
This page also provides some simpler approaches:
http://imagejdocu.tudor.lu/doku.php?id=howto:plugins:the_imagej_eclipse_howto
Thanks again
Richard