This notebook is part of the PyImageJ Tutorial Series, and assumes familiarity with the ImageJ API. Dedicated tutorials for ImageJ can be found here.S

12 Troubleshooting

This notebook covers some basic troubleshooting with PyImageJ. Let’s initalize ImageJ before exploring some troublehsooting options.

import imagej

# initialize ImageJ
ij = imagej.init()
print(f"ImageJ2 version: {ij.getVersion()}")
ImageJ2 version: 2.14.0/1.54f

12.1 I can’t pass my numpy image to an Op

ij.py is really good at converting numpy images into RandomAccessibleIntervals. However many Ops, like addPoissonNoise, take other forms of ImageJ images, like IterableInterval.

print(ij.op().help('filter.addPoissonNoise'))
Available operations:
	(RealType out) =
	net.imagej.ops.filter.addPoissonNoise.AddPoissonNoiseRealType(
		RealType out,
		RealType in,
		long seed?)
	(IterableInterval out) =
	net.imagej.ops.filter.addPoissonNoise.AddPoissonNoiseMap(
		IterableInterval out,
		IterableInterval in)

We can’t call this Op on a numpy array since it is a specialized type of RandomAccessibleInterval, which does not extend IterableInterval.

from skimage import io

# Create a numpy image using scikit
img = io.imread('https://imagej.net/images/clown.jpg')

ij.py.show(img)
print(type(ij.py.to_java(img)))
Operating in headless mode - the original ImageJ will have limited functionality.
_images/3a473d58f98e80bd372f7595b6f757d85a58b5a683fdc6f4a1bc56d60a2b78ae.png
<java class 'net.imagej.DefaultDataset'>

We can fix this by using transform.flatIterableView on both the input and output, which will convert the ReferenceGuardedRandomAccessibleIntervals into IterableIntervals, allowing us to pass our numpy image into addPoissonNoise:

import numpy as np

result = np.zeros(img.shape) # HINT: Uses float dtype, for more accurate noising.

imgIterable = ij.op().transform().flatIterableView(ij.py.to_java(img))
resIterable = ij.op().transform().flatIterableView(ij.py.to_java(result))

ij.op().filter().addPoissonNoise(resIterable, imgIterable)

ij.py.show(result)
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
_images/8a420508ee5358b99906fdcf8e610689074465e4d47c44d2ac4a5f8a83953910.png

Uh oh. Something’s off with the data values—see next section!

If you need to troubleshoot op workings, look for implementations that use only IterableIntervals or RandomAccessibleIntervals. To find the implementations use the print(ij.op().help()) function.

For the multiply function the implementation we used is second to last (net.imagej.ops.math.IIToRAIOutputII$Multiply)

If you need to troubleshoot op workings, look for implementations that use only IterableIntervals or RandomAccessibleIntervals. To find the implementations use the print(ij.op().help()) function.

For the multiply function the implementation we used is second to last (net.imagej.ops.math.IIToRAIOutputII$Multiply)

12.2 Clipping input data to the valid range for imshow with RGB data ([0..1] for floats of [0..255] for integers

This message is given either because (as described in the warning)

  • The data consists of floats that are outside the range of [0..1]. This is pretty common and definitely is the case as to why the above data looks so bad.

  • The data consists of ints that are outside the range [0..255].

Let’s be certain which is the culprit by checking some values of result:

# grab the RGB values in a line from [0][5] to [0][10] in our image
print(result[0][5:10])
[[266. 148.  54.]
 [251. 169.  41.]
 [246. 168.  26.]
 [257. 148.  34.]
 [250. 168.  37.]]

Thus we not only have floats outside of [0..1] but also values outside of [0..255]; we are faulty of both points in the warning. We can fix this by first clipping the entire array within the integer range, then cast to uint8 so that the float range no longer applies:

ij.py.show(img.astype(int))
       
result = np.clip(result, 0, 255)
ij.py.show(result.astype(np.uint8))
_images/3a473d58f98e80bd372f7595b6f757d85a58b5a683fdc6f4a1bc56d60a2b78ae.png _images/d79ec8658715ab677fa280ed6c9e2f41af9f4d63ec0be5609018bd2900b20837.png

Now our noisy image displays nicely alongside the original. Note that depending on your data this might not be the right path for you, but using clip and astype are great tools for rearranging your data within these bounds.

If you need to troubleshoot op workings, look for implementations that use only IterableIntervals or RandomAccessibleIntervals. To find the implementations use the print(ij.op().help()) function.

For the multiply function the implementation we used is second to last (net.imagej.ops.math.IIToRAIOutputII$Multiply)

# print is required to render new lines
print(ij.op().help('multiply'))
Available operations:
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyByte(
		ArrayImg arg,
		byte value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyDouble(
		ArrayImg arg,
		double value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyFloat(
		ArrayImg arg,
		float value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyInt(
		ArrayImg arg,
		int value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyLong(
		ArrayImg arg,
		long value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyShort(
		ArrayImg arg,
		short value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyUnsignedByte(
		ArrayImg arg,
		byte value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyUnsignedInt(
		ArrayImg arg,
		int value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyUnsignedLong(
		ArrayImg arg,
		long value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImageP$MultiplyUnsignedShort(
		ArrayImg arg,
		short value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyByte(
		ArrayImg arg,
		byte value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyDouble(
		ArrayImg arg,
		double value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyFloat(
		ArrayImg arg,
		float value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyInt(
		ArrayImg arg,
		int value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyLong(
		ArrayImg arg,
		long value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyShort(
		ArrayImg arg,
		short value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyUnsignedByte(
		ArrayImg arg,
		byte value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyUnsignedInt(
		ArrayImg arg,
		int value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyUnsignedLong(
		ArrayImg arg,
		long value)
	(ArrayImg arg) =
	net.imagej.ops.math.ConstantToArrayImage$MultiplyUnsignedShort(
		ArrayImg arg,
		short value)
	(IterableInterval out?) =
	net.imagej.ops.math.IIToIIOutputII$Multiply(
		IterableInterval out?,
		IterableInterval in1,
		IterableInterval in2)
	(NumericType out?) =
	net.imagej.ops.math.NumericTypeBinaryMath$Multiply(
		NumericType out?,
		NumericType in1,
		NumericType in2)
	(int result) =
	net.imagej.ops.math.PrimitiveMath$IntegerMultiply(
		int a,
		int b)
	(long result) =
	net.imagej.ops.math.PrimitiveMath$LongMultiply(
		long a,
		long b)
	(float result) =
	net.imagej.ops.math.PrimitiveMath$FloatMultiply(
		float a,
		float b)
	(double result) =
	net.imagej.ops.math.PrimitiveMath$DoubleMultiply(
		double a,
		double b)
	(RealType out) =
	net.imagej.ops.math.BinaryRealTypeMath$Multiply(
		RealType out,
		RealType in1,
		RealType in2)
	(IterableInterval out?) =
	net.imagej.ops.math.ConstantToIIOutputII$Multiply(
		IterableInterval out?,
		IterableInterval in,
		NumericType value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyByte(
		PlanarImg arg,
		byte value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyDouble(
		PlanarImg arg,
		double value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyFloat(
		PlanarImg arg,
		float value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyInt(
		PlanarImg arg,
		int value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyLong(
		PlanarImg arg,
		long value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyShort(
		PlanarImg arg,
		short value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyUnsignedByte(
		PlanarImg arg,
		byte value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyUnsignedInt(
		PlanarImg arg,
		int value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyUnsignedLong(
		PlanarImg arg,
		long value)
	(PlanarImg arg) =
	net.imagej.ops.math.ConstantToPlanarImage$MultiplyUnsignedShort(
		PlanarImg arg,
		short value)
	(IterableInterval out?) =
	net.imagej.ops.math.IIToRAIOutputII$Multiply(
		IterableInterval out?,
		IterableInterval in1,
		RandomAccessibleInterval in2)
	(RandomAccessibleInterval out) =
	net.imagej.ops.math.ConstantToIIOutputRAI$Multiply(
		RandomAccessibleInterval out,
		IterableInterval in,
		NumericType value)