The content of this page has not been vetted since shifting away from MediaWiki. If you’d like to help, check out the how to help guide!
The following article describes a method of ImageJ 1.x/ImageJ2 integration we explored in 2010, revolving around an ij.process.ImageProcessor
extension called ImgLibProcessor
which would enable additional transparent usage of ImgLib2 from within ImageJ 1.x, thus greatly expanding the available pixel types and storage strategies. However, after discussion with Wayne Rasband, we settled on a different method of backwards compatibility known as ImageJ Legacy. The text below is preserved only for historical reasons.
Design
As much as possible ImgLibProcessor
utilizes operations to implement its functionality. Operation is not a class here but just a concept. If you imagine a processor class the operations are really the methods that act upon the processor’s data and live as separate classes rather than within the processor class methods. This was done to reduce the complexity of ImgLibProcessor
. Originally there was a motivation of having operations that could be chained together. (Not sure how well this motivation was realized)
An operation ties together the concept of iteration and action on data over a user specified region. As an example of a typical operation I’ll discuss imagej.process.operation.SingleCursorRoiOperation
. This abstract class is responsible for managing the iteration over a single imglib Image. Here is its main iteration loop:
/** runs the operation. does the iteration and calls subclass methods as appropriate */
public void execute()
{
if (this.observer != null)
observer.init();
final LocalizableByDimCursor<T> imageCursor =
this.image.createLocalizableByDimCursor();
final RegionOfInterestCursor<T> imageRoiCursor =
new RegionOfInterestCursor<T>( imageCursor, this.origin, this.span );
beforeIteration(imageRoiCursor.getType());
//iterate over all the pixels, of the selected image plane
for (T sample : imageRoiCursor)
{
// note that the include() method call below passes null as position. This
// operation is not positionally aware for efficiency. Use a positional
// operation in the imagej.process.operation package if needed.
if ((this.selector == null) ||
(this.selector.include(null, sample.getRealDouble())))
insideIteration(sample);
if (this.observer != null)
observer.update();
}
afterIteration();
imageRoiCursor.close();
imageCursor.close();
if (this.observer != null)
observer.done();
}
The key ideas include:
- The iteration is encapsulated in this class. Subclasses of this class implement
beforeIteration()
,insideIteration()
, andafterIteration()
. (I’ll discuss inheritance later) - The iteration can be constrained by a function that uses the current value and position of the sample pointed to by the iterator to determine whether to call
insideIteration()
. - The iteration can be observed by other classes
As an example we’ll look at imagej.process.operation.MinMaxOperation
. Here is its entire implementation:
public class MinMaxOperation<T extends RealType<T>> extends SingleCursorRoiOperation<T>
{
private double min, max, negInfinity, posInfinity;
public MinMaxOperation(Image<T> image, int[] origin, int[] span)
{
super(image,origin,span);
}
public double getMax() { return this.max; }
public double getMin() { return this.min; }
@Override
protected void beforeIteration(RealType<T> type)
{
this.min = type.getMaxValue();
this.max = type.getMinValue();
// CTR: HACK: Workaround for compiler issue with instanceof and generics.
//if (type instanceof FloatType)
if (FloatType.class.isAssignableFrom(type.getClass()))
{
this.posInfinity = Float.POSITIVE_INFINITY;
this.negInfinity = Float.NEGATIVE_INFINITY;
}
else
{
this.posInfinity = Double.POSITIVE_INFINITY;
this.negInfinity = Double.NEGATIVE_INFINITY;
}
}
@Override
protected void insideIteration(RealType<T> sample)
{
double value = sample.getRealDouble();
if (value >= this.posInfinity) return;
if (value <= this.negInfinity) return;
if ( value > this.max )
this.max = value;
if ( value < this.min )
this.min = value;
}
@Override
protected void afterIteration()
{
}
}
You can see that a MinMaxOperation
is pretty simple. It finds the current min and max values of the iterated values. Defining operations in this way is simple and they end up being well encapsulated. However, striving for composition over inheritance I’ve tried to minimize the number of operation classes.
Using composition to enhance the capabilities of the various operations becomes possible with the definition of SelectionFunctions
. Recall from the execute()
method of SingleCursorRoiOperation
that you can constrain which samples will have further processing done on them using a selector. The selector in the loop is a SelectionFunction
. Its signature is defined in imagej.selection.SelectionFunction
:
public interface SelectionFunction
{
boolean include(int[] position, double sample);
}
You can define any function you like that discriminates samples based upon their value and position within their parent Image
. SelectionFunctions
are then attached to an operation via operation.setSelector(selectionFunction)
.
Note that SelectionFunction
is not in the imagej.process
package. I view the ability to discriminate samples based upon their value and position as a broad need in ImageJ. I can see how this would be useful in the support of Rois. There is currently code for composing composite selection functions in the imagej.selection
package. So arbitrarily complex selections can be made.
Once we define selection functions we can utilize them with operations in powerful ways. For example define this selection function:
class Selector implements SelectionFunction
{
public boolean include(int[] position, double sample)
{
if (sample value in range I desire)
if (position within a rotated ellipse centered at x,y with params z)
return true;
return false;
}
}
Create a MinMaxOperation
and attach this selection function. When you run operation.execute()
you can get the min and max values found within your selection criteria.
There are operations that change the underlying data as well. Imagej.process.operation.UnaryTransformOperation
is one example. It will change an underlying Image’s data by replacing the data with the computation of a function using the current Image
as input. The function is defined as a UnaryFunction
and is passed in to the UnaryTransformOperation
. Imagej.process.function.unary.UnaryFunction
looks like this:
public interface UnaryFunction {
double compute(double input);
}
An example of a UnaryFunction would be a sqr()
function whose compute()
method would return the square of its input. To square the values of an image you would create a UnaryTransformOperation
passing it a sqr()
UnaryFunction and then run operation.execute()
. It is important to note that a UnaryFunction
can be arbitrarily complex with its own sets of parameters provided it relies on one input value from an Image.
There are operations defined that do not change data. Imagej.process.operation.QueryOperation is such an operation. A QueryOperation
applies an InfoCollector
function to the user specified data. Imagej.process.query.InfoCollector
looks like this:
/** the InfoCollector interface is used to define queries that can be passed to an
* imagej.process.operation.QueryOperation.*/
public interface InfoCollector
{
/** this method called before the actual query takes place allowing the InfoCollector to initialize itself */
void init();
/** this method is called at each position of the original dataset allowing data to be collected */
void collectInfo(int[] position, double value);
/** this method is called when the query is done allowing cleanup and tabulation of results */
void done();
}
When one uses a QueryOperation
one can then collect information in whatever way desired.
Note that all operations (transforms, queries, etc.) can be modified to only work on a user defined region and then further constrained by value and position selection functions. Any function a user can define can be applied.
Operations are not limited to one dataset. There are operation classes defined that work with various combinations of synchronized datasets (1, 2, and N).
These concepts are utilized throughout the implementation of ImgLibProcessor
:
A simple unary transform operation -
public void abs()
{
AbsUnaryFunction function = new AbsUnaryFunction();
nonPositionalTransform(function); // a private method that does quickest SingRoiOp
}
A more complex example that uses a selection function -
/** fills the current ROI area of the current plane of data with the fill color wherever the input mask is nonzero */
@Override
public void fill(ImageProcessor mask)
{
if (mask==null) {
fill();
return;
}
int[] origin = originOfRoi();
int[] span = spanOfRoiPlane();
byte[] byteMask = (byte[]) mask.getPixels();
FillUnaryFunction fillFunction = new FillUnaryFunction(this.fillColor);
UnaryTransformPositionalOperation<T> transform =
new UnaryTransformPositionalOperation<T>(this.imageData, origin, span,
fillFunction);
SelectionFunction selector = new MaskOnSelectionFunction(origin, span, byteMask);
transform.setSelectionFunction(selector);
transform.execute();
}
A two Image operation that uses a SelectionFunction
-
/** sets the current ROI area data to that stored in the snapshot wherever the mask is nonzero */
@Override
public void reset(ImageProcessor mask)
{
if (mask==null || this.snapshot==null)
return;
Rectangle roi = getRoi();
if ((mask.getWidth() != roi.width) || (mask.getHeight() != roi.height))
throw new IllegalArgumentException(maskSizeError(mask));
Image<T> snapData = this.snapshot.getStorage();
int[] snapOrigin = Index.create(roi.x, roi.y,
new int[snapData.getNumDimensions()-2]);
int[] snapSpan = Span.singlePlane(roi.width, roi.height,
snapData.getNumDimensions());
int[] imageOrigin = originOfRoi();
int[] imageSpan = spanOfRoiPlane();
CopyInput2BinaryFunction copyFunction = new CopyInput2BinaryFunction();
BinaryTransformPositionalOperation<T> resetOp =
new BinaryTransformPositionalOperation<T>(this.imageData, imageOrigin,
imageSpan, snapData, snapOrigin, snapSpan, copyFunction);
MaskOffSelectionFunction maskOff =
new MaskOffSelectionFunction(imageOrigin, imageSpan, (byte[])mask.getPixels());
resetOp.setSelectionFunctions(maskOff, null);
resetOp.execute();
if (!this.isUnsignedByte)
{
this.min = this.snapshotMin;
this.max = this.snapshotMax;
}
}
ImgLibProcessor
also exposes a functional API for further use. Specifically the various assign()
and transform()
methods allows one to change an ImgLibProcessor
’s Image data passing functions as needed.
This can all be tied together in a plugin demo. The following code works on a float image whose values range between 0 and 1. When the plugin is run the data of the current window image is transformed. Someone who knows more of what a user would really like to do on an image can extend this as desired.
import ij.IJ;
import ij.ImagePlus;
import ij.WindowManager;
import ij.plugin.PlugIn;
import imagej.function.UnaryFunction;
import imagej.ij1bridge.process.ImgLibProcessor;
import imagej.selection.SelectionFunction;
import java.util.Random;
public class FunctionalPlugin implements PlugIn {
private class MyFunction implements UnaryFunction
{
Random rng = new Random();
public double compute(double value)
{
return rng.nextDouble();
}
}
private class MySelector implements SelectionFunction
{
public boolean include(int[] position, double sample)
{
if (sample < 0.2) return false;
if (sample > 0.8) return false;
if (position[0] % 3 != 0) return false;
if (position[1] % 2 != 0) return false;
return true;
}
}
public void run(String arg) {
ImagePlus imp = WindowManager.getCurrentImage();
ImgLibProcessor<?> proc = (ImgLibProcessor<?>)imp.getProcessor();
MyFunction function = new MyFunction();
MySelector selector = new MySelector();
proc.transform(function, selector);
imp.updateAndDraw();
}
}
Miscellaneous notes
- if desired we can likely eliminate inheritance from the operations (in
SingleCursorRoiOperation
for example) by passing it a class that implements an interface that doesbefore()
,inside()
, andafter()
. This is similar toObserver
andInfoCollector
and we may be able to do some simplification here - there are a number of different operations based upon how you are iterating and how many datasets you are simultaneously working with. There is also the built in limitation that iterators are synchronized. I have written proof of concept code to generalize iteration, allowing composition of iterators into either synchronized or nested iterators, eliminating the split between Unary/Binary/NAry functions, etc. Unfinished/untested but close to working.
- We may want to break out SelectionFunction into
ValueFunction
andPositionFunction
. Need to think about more
Required changes to IJ1 to accommodate ImgLibProcessor
This document describes changes required to ImageJ 1.x source code that will faciitate correct behavior when passed ImgLib-backed data. It is divided into 5 sections.
Section 1 outlines changes we’ve already made to our local copy of IJ 1.44l9 source code. These changes can be integrated into baseline ImageJ as needed.
Section 2 outlines further changes needed to fully support the new Image type ImagePlus.OTHER
.
Section 3 outlines further changes needed to compatibly support a new processor type.
Section 4 outlines further changes needed related to case logic switching on ImagePlus::getBitDepth()
.
Section 5 contains miscellaneous notes
CHANGES ALREADY MADE to allow ImgLib data to be correctly updated by IJ1
Reflects source code changes as of 12-17-10
Package ij:
- ImagePlus
Added another image type :
ImagePlus.OTHER
UpdatedgetBitDepth()
to calc bits per pixel for OTHER type images
UpdatedgetBytesPerPixel()
to calc number of bytes per pixel for OTHER type images
Added doublegetActualBytesPerPixel()
to support non-byte-aligned pixel types
UpdatedsetType()
to allow OTHER type
UpdatedgetFileInfo()
to populate self when dealing with OTHER type images
Updatedcopy(boolean cut)
to usegetActualBytesPerPixel()
in data byte use calculations
UpdatedgetPixel()
to encode pixel data for OTHER type images
Package ij.gui:
-
ImageCanvas
UpdatedsetDrawingColor()
to have a subcase for OTHER type images -
ImageWindow
UpdatedcreateSubtitle()
to calc bit depth and image size from ImagePlus rather than by type -
Wand
Change code to not use primitive array access for obtaining pixel values. To do so needed to make
minor changes to constructor, minor change toautoOutline()
, and rewrotegetPixel()
.
Package ij.io:
-
FileInfo
Added file typeGRAY64_SIGNED
ModifiedgetBytesPerPixel()
to supportGRAY64_SIGNED
andGRAY12_UNSIGNED
ModifiedgetType()
to return values forGRAY64_SIGNED
andGRAY12_UNSIGNED
-
ImportDialog
Added “12-bit Unsigned” to static class variable “types”.
UpdatedgetFileInfo()
to identifyGRAY12_UNSIGNED
type files
Package ij.measure:
- Calibration
Added a method calledisSameAs(Calibration other)
. We rely on this for numerous tests.
Package ij.plugin:
-
FolderOpener
Made minor change to therun()
method to support OTHER type images
ModifiedsetStackInfo()
to use newbytesPerPixel
calculation methods -
ListVirtualStack
UpdatedshowDialog()
to use newbytesPerPixel
calculation methods
Package ij.plugin.filter:
-
ImageMath
Many small edits to usesetf()
/getf()
rather than directfloat[]
access. Also rather thaninstanceof
FloatProcessor
useip.isFloatingType()
.
ModifyapplyMacro
case logic to testinstanceof SomeProcessor
rather than usinggetBitDepth()
-
ParticleAnalyzer
Added a type called OTHER. Made many small edits to support.
Moved away from direct primitive array access for pixel values and rather usegetf()
/etc. as needed.
There some places tagged with “WAYNE PLEASE CHECK” for further review
ChangedsetThresholdLevels()
to identify images of OTHER type and also setfillColor
correctly
ChangedgetStatistics()
to delegate toip.getStatistics()
rather than checking image type -
PluginFilterRunner
UpdatedcheckImagePlus()
to have a switch case for images of type OTHER
Package ij.plugin.frame:
- ContrastAdjuster
Minor edit of
setupNewImage()
case logic to support OTHER type images
Minor edit ofreset()
case logic to support OTHER type images
Update the calculation of decimal places to display for OTHER type images insetMinAndMax()
Update the calculation of decimal places to display for OTHER type images insetWindowLevel()
Package ij.process:
- ImageProcessor
Changed visibility of showProgress to public. We have a ProgressTracker class in IJ2 that updates an ip’s progress indicator.
Changed visibility ofgetBilinearInterpolatedPixel()
to public
Changed visibility ofresetPixels()
to protected
Changed visibility ofcreate8BitImage()
to protected
Added abstract methods for all processors to support:int getBitDepth(); double getBytesPerPixel(); ImageStatistics getStatistics(int mOptions, Calibration cal); boolean isFloatingType(); boolean isUnsignedType(); double getMinimumAllowedValue(); double getMaximumAllowedValue(); String getTypeName(); double getd(int x, int y); double getd(int index);
Added a couple set/get methods so our new ImageProcessor
type can manipulate instance variables as needed
protected boolean getSnapshotCopyMode()
public int getFgColor()
public void setFgColor()
public Color getDrawingColor()
Added a method that is only called on processors of OTHER type by ImagePlus::getPixel()
public void encodePixelInfo(int[] destination, int x, int y)
-
ByteProcessor
implementation of the new abstract methods of the ImageProcessor interface
-
ColorProcessor
implementation of the new abstract methods of the ImageProcessor interface -
FloatProcessor implementation of the new abstract methods of the ImageProcessor interface
-
ShortProcessor
implementation of the new abstract methods of the ImageProcessor interface -
ImageStatistics Made a few methods with package access into protected methods
calculateStdDev()
,setup()
,fitEllipse()
,calculateMedian()
ChangedgetStatistics()
to delegate to passed in ImageProcessor’sgetStatistics()
method rather than
switching on processor type and hatching a type appropriate ImageStatistics -
TypeConverter
Added support for OTHER image types with new package level access methods:ByteProcessor convertOtherToByte() ShortProcessor convertOtherToShort() FloatProcessor convertOtherToFloat().
PLACES WHERE ImagePlus::getType() USES NEED UPDATING
ij.gui.Roi
-showStatus()
number of decimal places of display would be incorrect for someImglib
types without a simple fix.ij.io.FileOpener
-setCalibration()
minor change needed to make sure min and max set correctly for the processor.ij.io.FileSaver
–saveAsJpeg()
, andgetDescriptionString()
need minor case logic changes. Should check that the varioussaveAsXXX()
plugins work forImgLibProcessor
backed types.ij.macro.Functions
-setPixel()
andgetpixel()
- need minor changes to case logic to support OTHER typeij.measure.Calibration
-setImage()
needs minor case logic change to support OTHER typeij.plugin.filter.Calibrator
-run()
,calibrate()
, anddoCurveFitting()
- minor changes to case logic needed to support OTHER typeij.plugin.filter.Filters
-setup()
has minor case logic change needed to support OTHER typeij.plugin.filter.Info
-getInfo()
needs a subcase forImagePlus::OTHER
. Small localized change.ij.plugin.filter.RankFilters
-showDialog()
needs minor case logic change for setting number of decimal places if image is a float typeij.plugin.frame.ContrastAdjuster
–updatelabels()
,plotHistogram()
, maybeapply()
need small case logic adjustmentsij.plugin.filter.ThresholdAdjuster
-setup()
- minor change to determine not an 8 bit imageij.plugin.Concatenator
needs more thorough type checking to support OTHER type images. As it stands now it is possible to try and concat two images of type OTHER who have totally different pixel formats. Also cannot concat aImagePlus::GRAY16
and aImagePlus::OTHER
with backing data that is 16 bit.ij.plugin.GelAnalyzer
-plotLanes()
has one line that needs to be changed to support OTHER typesij.plugin.RGBStackConverter
–run()
needs some nontrivial changes to support OTHER typesij.plugin.Slicer
–run()
needs a float check rather thanGRAY32
. Simple fix.ij.plugin.StackCombiner
has issues similar toConcatenator
.ij.plugin.StackInserter
has issues similar toConcatenator
ij.plugin.Thresholder
–applyThreshold()
needs minor change to support OTHER typesij.plugin.XYCoordinates
–run()
testsGRAY32
rather thanisFloat()
. Simple to fix.ij.process.ImageConverter
needs a good amount of work to support OTHER typesij.process.StackConverter
needs a good amount of work to support OTHER typesij.IJ
: -doWand()
needs minor change (fromGRAY32
test toisFloatingType()
test)ij.Menus
– menu entries needed for OTHER types. AndaddWindowMenuItem()
should use new byte use calculation routines.
PLACES WHERE instanceof SomeProcessor USES NEED UPDATING
ij.io.TextEncoder
– minor change needed (use!ip.isFloatingType()
) to support OTHER typesij.macro.Functions
–getStatistics()
assumes you only have 8 & 16 bit images/histograms. Needs some reworking to support OTHER types.ij.plugin.filter.BackgroundSubtracter
– needs some substantial work to be extended to support processors of OTHER typeij.plugin.Convolver
– various methods make assumptions about which kinds of processors can exist. Also seems to rely onFloatProcessor
. Needs some nontrivial work to support OTHER typesij.plugin.filter.ImageMath
– inrun()
method there is an unsafe check for signed data. Simple fix. There are also a couple unsafe checks for floating point data. Again a simple fix.ij.plugin.filter.MaximumFinder
– simple fix needed for determing whether data is floating typeij.plugin.filter.ParticleAnalyzer
– makes unsafe assumptions. I have mostly updated it already. Wayne may need to make bigger changes. Will talk to Wayne about this one.ij.plugin.filter.PluginFilterRunner
– tests versusFloatProcessor
. May not need any changes. May just work but may be inefficient forImgLibProcessors
of float type. Study more.ij.plugin.frame.ContrastAdjuster
– makes some type assumptions. I think I’ve fixed it in already\_ij1-patches
.ij.plugin.frame.ThresholdAdjuster
–updateLabels()
does a test onShortProcessor
. May need to be fixed.DoSet()
needs to replace instanceofFloatProcessor
withip.isFloatingType()
.setHistogram()
needs to replace instanceof FloatProcessor withip.isFloatingType()
.ij.plugin.ContrastEnhancer
– makes a few type assumptions. May need some larger rework.ij.plugin.FITS_Writer
– setup of header relies on eitherShort
orFloat
. May need to ask what is needed for OTHER types.WriteData()
only does float and short. Will not support OTHER type images backed by floats and shorts.ij.plugin.OrthogonalViews
– makes many assumptions on processor type. Needs nontrivial updates to support OTHER types.ij.plugin.ZProjector
– makes assumptions that only the current processors will ever exist. Needs nontrivial updating to work.ij.process.FloodFiller
– simple change needed to constructor to useip.isFloatingType()
ij.process.ImageStatistics
– I think I already made all changes needed and in\_ij1-patches
ij.process.TypeConverter
– I think I already made all changes needed and in\_ij1-patches
ij.ImagePlus
- I think I already made all changes needed and in\_ij1-patches
PLACES WHERE ImagePlus::getBitDepth() USES NEED UPDATING
ij.io.FileSaver
–okForFits()
should testimp.getType()
rather thanimp.getBitDepth()
. Simple.ij.io.ImportDialog
– rather than testbitDepth()
it should testgetType()
notByte
orColor
. Simple.ij.macro.Functions
–setColor()
needs minor case logic change for 16-bit signed data to avoid throwing an exception unneccesarily.GetHistogram()
,setLut()
andsetMinAndMax()
should testgetType()
and notgetBitDepth()
. Simple.ij.plugin.filter.FFTCustomFilter
–doInverseTransform()
makes some assumptions about bit depth implying certain types of processors. Needs a closer look.ij.plugin.filter.FFTFilter
–filter()
makes some assumptions about bit depth implying certain types of processors. Needs a closer look.ij.plugin.filter.ImageMath
– “div” case ofrun()
assumes 32-bit implies Floating type data. Simple fix.ApplyMacro()
andshowDialog()
should test versusimp.getType()
rather thangetBitDepth()
. Simple.ij.plugin.filter.Info
–getInfo()
should testip.isFloatingType()
rather thanimp.getBitDepth()
. Simple.ij.plugin.filter.ParticleAnalyzer
–setup()
tests bit depth when it should testimp.getType()
. Simple.ij.plugin.filter.RankFilters
-run()
tests bit depth when it should testimp.getType()
. Simple.ij.plugin.filter.RGBStackSplitter
-setup()
tests bit depth when it should testimp.getType()
. Simple.ij.plugin.filter.Rotator
– uses bitDepth when it should usegetType()
in a few places. Simple fixes.ij.plugin.frame.Channels
–itemStateChanged()
assumes 24-bit impliesColor
. Simple fix.ij.plugin.frame.ColorThresholder
–sample()
,checkImage()
,windowActivated()
,RGBTpLab()
, andRGBToYUV()
test bit depth when they should testimp.getType()
. Simple.ij.plugin.frame.ContrastAdjuster
–setMinAndMax()
andsetWindowLevel()
assume 32-bit is Float. Simple fixes to useisFloatingType()
.ij.plugin.frame.ThresholdAdjuster
– constructor should testgetType()
.DoSet()
andapply()
assume 32-bit is float. UseisFloatingType()
instead. Simple.ij.plugin.AVI_Reader
–run()
has some very minor special case logic for 16-bit. Not sure why. Will need to investigate further.ij.plugin.BatchConverter
–run()
usesgetBitDepth()
when it should usegetType()
. Simple.ij.plugin.BatchProcessor
–processVirtualStack()
andprocessFolder()
usegetBitDepth()
when they should usegetType()
. Simple.ij.plugin.BMP_Writer - writeImage()
usesgetBitDepth()
when it should usegetType()
. Simple.ij.plugin.CompositeConverter - run()
usesgetBitDepth()
when it should usegetType()
. Simple.ij.plugin.ContrastEnhancer
– some issues. investigate furtherij.plugin.FFT - doInverseTransform()
usesgetBitDepth()
when it should usegetType()
. Still a bit more broken as it usesfht.bitDepth
which is copied from elsewhere. We want to remove reliance on bit depth determining what kind of processor we have.ij.plugin.FITS_Writer – run()
usesbitDepth
when it could usegetType()
. This method documented problems with instanceof. Method requires closer inspection.ij.plugin.FolderOpener – run()
relies on bitDepth numerous times. Need to investigate further.ij.plugin.Histogram
–run()
relies on bitdepth when it should usegetType()
. Simple to fix.ij.plugin.HyperStackConverter
–convertStackToHS()
relies on bitdepth when it should usegetType()
. Simple to fix.ij.plugin.HyperStackReducer
– will not work for images of type OTHER as it relies onIJ.createImage()
which only knows the few predefined image types. Might need way to override the IJ that is in place so we can hook in our owncreateImage()
. Also relies onImagePlus::createHyperStack()
which also has limited bit depth support. OtherwisebitDepth
use is fine for this class.ij.plugin.ImageCalculator
–calculate()
relies on bit depth rather thangetProcessor().isFloatingType()
. Simple to fix.ij.plugin.ImagesToStack
– a lot of reliance on bit depth. As is won’t work with OTHER type images. Look at this more closely.ij.plugin.ListVirtualStack
– a lot of reliance on bit depth. Assumes only a few processor types exist. Needs to create processors. We might need Wayne to create a processor factory that we can override. Look at this more closely.ij.plugin.LUT_Editor
–run()
uses bit depth when it can be pretty simply avoided.ij.plugin.PNM_Writer
–run()
uses bit depth when it can be pretty simply avoided.ij.plugin.Resizer
–zScale()
uses bit depth unnecessarily. Simple.ResizeZ()
andzScaleHyperStack()
both use bit depth to callIJ.createImage()
. So again we’ll need to override somehow.ij.plugin.RGBStackMerge
–mergeStacks()
andmergeHyperStacks()
need some bit depth access. But also assumes 24-bit is RGB. Simple to remove this assumption.ij.plugin.Scaler
–showDialog()
uses bit depth when it could usegetType()
. Simple.ij.plugin.Slicer
–resliceHyperStack()
callscreateHyperStack()
with bit depth. Need an override.CreateOutputStack()
callsNewImage.createImage()
with bit depth. Again an override needed.GetOutputStackSize()
usesbitDepth
to calculate data use sizes. Can use new byte calc methods.ij.plugin.Straightener
–straighten()
,straightenLine()
, androtateLine()
all assume 24 bit == RGB. Simple to fix.ij.plugin.SurfacePlotter – drawAndLabelAxis()
usesbitDepth
where it could usegetType()
. Simple.ij.plugin.Thresholder – convertSTackToBinary()
usesbitDepth
where it could usegetType()
. Simple.ij.plugin.WandToolsOptions - run()
usesbitDepth
where it could usegetType()
. Simple.ij.plugin.XYCoordinates - run()
usesbitDepth
where it could usegetType()
. Simple.ij.plugin.ZProjector - doHyperStackProjection()
usesbitDepth
where it could usegetType()
. Simple.ij.process.StackStatistics
– constructor anddoCalculations()
rely on bit depth. The 24 bit stuff can usegetType
instead. But the 8 & 16 cases may be fine. Investigate further.ij.CompositeImage
– constructor assumes 24-bit == RGB. Simple fix.ij.ImagePlus
–revert()
andshow()
use bit depth but 8 bit cases. So may be safe but best to replace. Simple fix.ij.VirtualStack
–getProcessor
switches on bit depth. Can only support 8, 16, 24, and 32. This is probably okay from the looks of it.
MISCELLANEOUS NOTES
Additional methods desired in ImageProcessor
and subclasses:
- double support: setting via
setd()
by (x,y) or by index - long support: getting/setting via
getl()
/setl()
by (x,y) or by index
Further changes:
- replace use of
ImagePlus::getBytesPerPixel()
withImagePlus::getActualBytesPerPixel()
where needed