Hi all
How do I handle multiple bit depths in a plugin? I am writing a super-simple plugin to calculate volume fraction, which is just the ratio of thresholded pixels to all pixels in a selected volume. I've tried a few things, but always get caught out by declaring the pixels[] array in an if statement like: if (imp.getBitDepth == 8){byte[] pixels = (byte[])stack.getPixels(s)}; or by casting in a wrong way. I've looked around for examples but haven't come up with much. Cheers Mike --------------- /*Volume fraction of Bone * Plugin to calculate volume fraction in stacks */ import java.awt.Rectangle; import ij.IJ; import ij.ImagePlus; import ij.ImageStack; import ij.process.ImageProcessor; import ij.plugin.filter.PlugInFilter; import ij.measure.ResultsTable; import ij.gui.*; public class Volume_Fraction implements PlugInFilter { ImagePlus imp; protected ImageStack stack; public int setup(String arg, ImagePlus imp) { stack = imp.getStack(); this.imp = imp; return DOES_8G + DOES_16 + DOES_32 + STACK_REQUIRED; } public void run(ImageProcessor ip) { ResultsTable rt = ResultsTable.getResultsTable(); rt.reset(); String title = imp.getTitle(); IJ.run("Threshold..."); new WaitForUserDialog("Set the threshold, then click OK.").show(); short minT = (short)ip.getMinThreshold(); short maxT = (short)ip.getMaxThreshold(); int startSlice = 1; int endSlice = stack.getSize(); int w = stack.getWidth(); GenericDialog gd = new GenericDialog("Limit Slices"); gd.addNumericField("Start Slice:",startSlice,0); gd.addNumericField("End Slice:",endSlice,0); gd.showDialog(); if (gd.wasCanceled()) { IJ.error("PlugIn canceled!"); return; } startSlice = (int)gd.getNextNumber(); endSlice = (int)gd.getNextNumber(); Rectangle r = ip.getRoi(); int volTotal = r.height * r.width * (endSlice - startSlice + 1); int offset, i; int volBone = 0; for (int s=startSlice;s<=endSlice;s++) { double[] pixels = (double[])stack.getPixels(s); for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; if (pixels[i] >= minT && pixels[i] <= maxT){ volBone++; } } } } double p = (double) volBone / volTotal; rt.incrementCounter(); rt.addLabel("Label", title); rt.addValue("VfB", p); rt.show("Results"); } } |
Hi Mike,
I once did some extentions to the AVI Reader plugin, and I remember that in that case there are also different depths are handled. Maybe you find a hint for your problem in the source. see http://rsbweb.nih.gov/ij/plugins/avi-reader.html hth, Reinhard. On Fri, 10 Oct 2008 18:11:35 +0200, Michael Doube <[hidden email]> wrote: > Hi all > > How do I handle multiple bit depths in a plugin? I am writing a > super-simple plugin to calculate volume fraction, which is just the > ratio of thresholded pixels to all pixels in a selected volume. I've > tried a few things, but always get caught out by declaring the pixels[] > array in an if statement like: > > if (imp.getBitDepth == 8){byte[] pixels = (byte[])stack.getPixels(s)}; > > or by casting in a wrong way. I've looked around for examples but > haven't come up with much. > > Cheers > > Mike > --------------- > /*Volume fraction of Bone > * Plugin to calculate volume fraction in stacks > */ > > import java.awt.Rectangle; > import ij.IJ; > import ij.ImagePlus; > import ij.ImageStack; > import ij.process.ImageProcessor; > import ij.plugin.filter.PlugInFilter; > import ij.measure.ResultsTable; > import ij.gui.*; > > public class Volume_Fraction implements PlugInFilter { > ImagePlus imp; > protected ImageStack stack; > > public int setup(String arg, ImagePlus imp) { > stack = imp.getStack(); > this.imp = imp; > return DOES_8G + DOES_16 + DOES_32 + STACK_REQUIRED; > } > public void run(ImageProcessor ip) { > ResultsTable rt = ResultsTable.getResultsTable(); > rt.reset(); > String title = imp.getTitle(); > IJ.run("Threshold..."); > new WaitForUserDialog("Set the threshold, then click OK.").show(); > short minT = (short)ip.getMinThreshold(); > short maxT = (short)ip.getMaxThreshold(); > int startSlice = 1; > int endSlice = stack.getSize(); > int w = stack.getWidth(); > GenericDialog gd = new GenericDialog("Limit Slices"); > gd.addNumericField("Start Slice:",startSlice,0); > gd.addNumericField("End Slice:",endSlice,0); > gd.showDialog(); > if (gd.wasCanceled()) { > IJ.error("PlugIn canceled!"); > return; > } > startSlice = (int)gd.getNextNumber(); > endSlice = (int)gd.getNextNumber(); > Rectangle r = ip.getRoi(); > int volTotal = r.height * r.width * (endSlice - startSlice + 1); > int offset, i; > int volBone = 0; > for (int s=startSlice;s<=endSlice;s++) { > double[] pixels = (double[])stack.getPixels(s); > for (int y=r.y; y<(r.y+r.height); y++) { > offset = y*w; > for (int x=r.x; x<(r.x+r.width); x++) { > i = offset + x; > if (pixels[i] >= minT && pixels[i] <= maxT){ > volBone++; > } > } > } > } > double p = (double) volBone / volTotal; > rt.incrementCounter(); > rt.addLabel("Label", title); > rt.addValue("VfB", p); > rt.show("Results"); > } > } -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/ |
In reply to this post by Michael Doube
Hi Mike,
Unfortunately, this issue stems from a limitation with Java primitive types. The typical way to handle it is to use case logic. In your example, it would look something like: Object pix = stack.getPixels(s); if (pix instanceof byte[]) { byte[] pixels = (byte[]) pix; for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; if (pixels[i] >= minT && pixels[i] <= maxT) volBone++; } } }else if (pix instanceof short[]) { short[] pixels = (short[]) pix; for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; if (pixels[i] >= minT && pixels[i] <= maxT) volBone++; } } } else if (pix instanceof int[]) { int[] pixels = (int[]) pix; for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; if (pixels[i] >= minT && pixels[i] <= maxT) volBone++; } } } else if (pix instanceof float[]) { float[] pixels = (float[]) pix; for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; if (pixels[i] >= minT && pixels[i] <= maxT) volBone++; } } } else { System.out.println("Unsupported pixel type: " + pix.getClass().getName()); } If you look at the AVI_Reader as Reinhard suggests, you'll see that it also uses case logic (unpack8bit, unpackGray, unpackShort, etc.). An alternative would be to use reflection (one of Java's strengths) to handle it, but I'm not sure if it is as efficient as the case-by-case code. Give it a try and find out: import java.lang.reflect.Array; ... Object pixels = stack.getPixels(s); for (int y=r.y; y<(r.y+r.height); y++) { offset = y*w; for (int x=r.x; x<(r.x+r.width); x++) { i = offset + x; double val = Array.getDouble(pixels, i); if (val >= minT && val <= maxT) volBone++; } } There may be some other slick way to handle this in Java 1.6+, as I haven't kept up with the newest language features, but somehow I doubt it. -Curtis On Fri, Oct 10, 2008 at 11:11 AM, Michael Doube <[hidden email]>wrote: > Hi all > > How do I handle multiple bit depths in a plugin? I am writing a > super-simple plugin to calculate volume fraction, which is just the ratio of > thresholded pixels to all pixels in a selected volume. I've tried a few > things, but always get caught out by declaring the pixels[] array in an if > statement like: > > if (imp.getBitDepth == 8){byte[] pixels = (byte[])stack.getPixels(s)}; > > or by casting in a wrong way. I've looked around for examples but haven't > come up with much. > > Cheers > > Mike > --------------- > /*Volume fraction of Bone > * Plugin to calculate volume fraction in stacks > */ > > import java.awt.Rectangle; > import ij.IJ; > import ij.ImagePlus; > import ij.ImageStack; > import ij.process.ImageProcessor; > import ij.plugin.filter.PlugInFilter; > import ij.measure.ResultsTable; > import ij.gui.*; > > public class Volume_Fraction implements PlugInFilter { > ImagePlus imp; > protected ImageStack stack; > > public int setup(String arg, ImagePlus imp) { > stack = imp.getStack(); > this.imp = imp; > return DOES_8G + DOES_16 + DOES_32 + STACK_REQUIRED; > } > public void run(ImageProcessor ip) { > ResultsTable rt = ResultsTable.getResultsTable(); > rt.reset(); > String title = imp.getTitle(); > IJ.run("Threshold..."); > new WaitForUserDialog("Set the threshold, then click OK.").show(); > short minT = (short)ip.getMinThreshold(); > short maxT = (short)ip.getMaxThreshold(); > int startSlice = 1; > int endSlice = stack.getSize(); > int w = stack.getWidth(); > GenericDialog gd = new GenericDialog("Limit Slices"); > gd.addNumericField("Start Slice:",startSlice,0); > gd.addNumericField("End Slice:",endSlice,0); > gd.showDialog(); > if (gd.wasCanceled()) { > IJ.error("PlugIn canceled!"); > return; > } > startSlice = (int)gd.getNextNumber(); > endSlice = (int)gd.getNextNumber(); > Rectangle r = ip.getRoi(); > int volTotal = r.height * r.width * (endSlice - startSlice + > 1); > int offset, i; > int volBone = 0; > for (int s=startSlice;s<=endSlice;s++) { > double[] pixels = (double[])stack.getPixels(s); > for (int y=r.y; y<(r.y+r.height); y++) { > offset = y*w; > for (int x=r.x; x<(r.x+r.width); x++) { > i = offset + x; > if (pixels[i] >= minT && pixels[i] <= maxT){ > volBone++; > } > } > } > } > double p = (double) volBone / volTotal; > rt.incrementCounter(); > rt.addLabel("Label", title); > rt.addValue("VfB", p); > rt.show("Results"); > } > } > |
Hi Mike,
If you write the plugin a scripting language you can go around this limitation. For example in jython: ip = IJ.getImage().getProcessor() pix = ip.getPixels() count = 0 for i in range(len(pix)): if 200 == pix[i]: count += 1 print "counted ", count, " pixels of value 200" Or: for pixel in IJ.getImage().getProcessor().getPixels(): if 200 == pixel: count += 1 print "counted ", count, " pixels of value 200" To edit the pixels you would need to know of what type they are. Otherwise, setting pix[i] = 12.3 when pixels are a byte[] would fail. If, on the other hand, you'd like to edit the pixels as well, you can create a lisp macro in Clojure that will add the proper primitive type decorations to each type of pixels array, and do so (1) at native speed and (2) in a single function. Other scripting languages (like Jython and Javascript) will use reflection instead (slower, but not unbearable). Both Jython and Clojure languages are supported for plugins in ImageJ via Fiji ( http://pacific.mpi-cbg.de ). The clojure guide for ImageJ: http://pacific.mpi-cbg.de/wiki/index.php/Clojure_Scripting Other scripting: http://pacific.mpi-cbg.de/wiki/index.php/Scripting_Help http://pacific.mpi-cbg.de/wiki/index.php/Scripting_comparisons Other scripting languages supported in Fiji are JRuby, Javascript and Beanshell: http://pacific.mpi-cbg.de/wiki/index.php/Category:Scripting Albert -- Albert Cardona http://www.mcdb.ucla.edu/Research/Hartenstein/acardona |
In reply to this post by Michael Doube
Thanks everyone for your creative suggestions. The winner was Wayne's
suggestion to use a method, getPixel(x,y), that handles multiple bit depths so I don't have to worry about those sorts of details. In the end, the code just iterates through all pixels in the usual way, compares ipSlice.getPixel(x,y) to the thresholds and conditionally increments the counter. What could be simpler? http://doube.org/plugins.html#volfrac Cheers Mike Michael Doube wrote: > Hi all > > How do I handle multiple bit depths in a plugin? I am writing a > super-simple plugin to calculate volume fraction, which is just the > ratio of thresholded pixels to all pixels in a selected volume. I've > tried a few things, but always get caught out by declaring the pixels[] > array in an if statement like: > > if (imp.getBitDepth == 8){byte[] pixels = (byte[])stack.getPixels(s)}; > > or by casting in a wrong way. I've looked around for examples but > haven't come up with much. > > Cheers > > Mike > --------------- > /*Volume fraction of Bone > * Plugin to calculate volume fraction in stacks > */ > > import java.awt.Rectangle; > import ij.IJ; > import ij.ImagePlus; > import ij.ImageStack; > import ij.process.ImageProcessor; > import ij.plugin.filter.PlugInFilter; > import ij.measure.ResultsTable; > import ij.gui.*; > > public class Volume_Fraction implements PlugInFilter { > ImagePlus imp; > protected ImageStack stack; > > public int setup(String arg, ImagePlus imp) { > stack = imp.getStack(); > this.imp = imp; > return DOES_8G + DOES_16 + DOES_32 + STACK_REQUIRED; > } > public void run(ImageProcessor ip) { > ResultsTable rt = ResultsTable.getResultsTable(); > rt.reset(); > String title = imp.getTitle(); > IJ.run("Threshold..."); > new WaitForUserDialog("Set the threshold, then click OK.").show(); > short minT = (short)ip.getMinThreshold(); > short maxT = (short)ip.getMaxThreshold(); > int startSlice = 1; > int endSlice = stack.getSize(); > int w = stack.getWidth(); > GenericDialog gd = new GenericDialog("Limit Slices"); > gd.addNumericField("Start Slice:",startSlice,0); > gd.addNumericField("End Slice:",endSlice,0); > gd.showDialog(); > if (gd.wasCanceled()) { > IJ.error("PlugIn canceled!"); > return; > } > startSlice = (int)gd.getNextNumber(); > endSlice = (int)gd.getNextNumber(); > Rectangle r = ip.getRoi(); > int volTotal = r.height * r.width * (endSlice - startSlice + 1); > int offset, i; > int volBone = 0; > for (int s=startSlice;s<=endSlice;s++) { > double[] pixels = (double[])stack.getPixels(s); > for (int y=r.y; y<(r.y+r.height); y++) { > offset = y*w; > for (int x=r.x; x<(r.x+r.width); x++) { > i = offset + x; > if (pixels[i] >= minT && pixels[i] <= maxT){ > volBone++; > } > } > } > } > double p = (double) volBone / volTotal; > rt.incrementCounter(); > rt.addLabel("Label", title); > rt.addValue("VfB", p); > rt.show("Results"); > } > } |
Free forum by Nabble | Edit this page |