Set background color for rotation of a 16-bit image : ShortProcessor miss bgColor attribute

imagej
image-processor

#1

I am having a hard time trying to rotate a gray scale image and having the extra image space that results of the rotation to be in a given graylevel.

For 8-bit images, it is doable the ByteProcessor class comes with the setBackgroundValue() and rotate()method.
It’s a bit painful to adapt the size of the output image to prevent unexpected cropping but that’s still quite ok.

However for higher BitDepth (ex:ShortProcessor, the method setBackgroundValue is empty !
The rotate method always fill the background with 0 !
The documentation indeed states it but still it is quite an unexpected behaviour since the method is documented in the mother class ImageProcessor.

Is there a reason for that ?
From the code of the Byte Processor it does not seem to be much different, the bgColor attribute is missing and a few other stuff.
I could propose a Pull Request but I wont be able to test it, as I am not familiar with compiling my own ImageJ from source.

I am not using the Image>Transform>Rotate because the background value is only available as RGB.
There is a possible conversion from RGB to gray but again it is only defined in the source code for 8-bit images.


#2

Good day!

Only recently I stumbled over the same problem:
http://imagej.1557.x6.nabble.com/Image-Rotations-and-ip-setBackgroundValue-tp5021339.html

Michael Schmid provided very helpful suggestions:
http://imagej.1557.x6.nabble.com/Image-Rotations-and-ip-setBackgroundValue-tp5021339p5021340.html

Perhaps they help you as well.

Regards

Herbie


#3

Hello Laurent -

Not an answer to your main questions, but rather a brief comment
about “injecting” your own replacement class, below:

As an aside, if you wish to avoid the bulk of building a full ImageJ from
source, you can go for the “surgical strike” approach:

Get the ShortProcessor source, e.g., from the github link you posted.
Unjar the relevant jar. In my Fiji installation, ShortProcessor.class is in
ij-1.52h.jar, in my Fiji installation’s jars directory:

/<my_fiji_location>/Fiji.app/jars

Delete (or otherwise hide) the original ShortProcessor.class. Modify
your own copy of ShortProcessor.java, compile it, and the rejar
ij-1.52h.jar with your modified copy of ShortProcessor.class.

Launch and run ImageJ, and see if your version of ShortProcessor
works the way you want.

Thanks, mm


#4

Thanks for the suggestion, I will definitly try it !
For the recompiling of my own copy of the ShortProcessor.class, if I run javac ShortProcessor.java using the unjarred ij-1.52h.jar as working directory will this work ?


#5

Hello Laurent -

I don’t think running javac in your “unjar” directory will hurt anything,
but to guess what you’re driving at, I doubt that doing so would be
a substitute for setting the classpath you use when running javac.

What I do is point my classpath to my Fiji installation’s jars directory,
e.g.,

javac -cp .:/<my_fiji_location>/Fiji.app/jars/* ShortProcessor.java

(and to any other third-party jars I may happen to be using).

I prefer to compile my replacement class in its own directory, rather
than in the unjar directory. (It’s just a preference, but I find it’s easier
to keep track of things that way.) I then copy the resulting .class file
back into the appropriate package subdirectory of my unjar directory,
e.g. into /<my_unjar_directory>/ij/process (and rejar, and
copy the resulting jar back into my Fiji intallation’s jars directory).

This is all very straightforward – less complicated than it might sound.
If you have any questions, please ask.

Thanks, mm


#6

The imagej/ImageJA project is a mavenized, git-sanitized version of imagej1 and kept in sync with the latter automatically.
You already linked to the classes in ImageJA, so I think if you just make your changes in a clone of that repo, you should be able to build with Maven from the command line using mvn clean install :thinking:

With ImgLib2, you can rotate independent of type, with an arbitrary out-of-bounds strategy. Unfortunately, this is not straight forward (yet). See also this topic:


Two relevant pull requests are still waiting to be merged (/cc @ctrueden and @bnorthan):


#7

Here’s a Groovy script that rotates the input image (with respect to the origin at [0,0]) and fills the undefined space with samples of value 20:

#@ Img input

import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory
import net.imglib2.realtransform.AffineTransform2D
import net.imglib2.realtransform.RealViews
import net.imglib2.view.Views

transform = new AffineTransform2D()
transform.rotate(0.5)

println transform

value = input.firstElement().copy()
value.setReal(20)


result = Views.interval(
			RealViews.affine(
				Views.interpolate(
					Views.extendValue(input, value),
					new NLinearInterpolatorFactory()),
				transform),
			input)

#8

It works indeed :smiley: , I get my own ij-1.52h.jar, so I guess I just need to replace the original jar from the fiji installation folder to test my modifications. I will try asap.

I will also try the ops/ImgLib2 options, thanks !


#9

I started working on a fix for those background issues with the rotate, scale and translate methods.
So now it is possible to set a background value for 16 and 32-bit images.
I am just not sure about the gray level range for 32-bit images, is it indeed +/-3.4e38 ?

Back to the initial problem, Rotate works as expected now.

For scale, I modify the code accordingly but I don’t really understand when the scale is supposed to generate some background so the code compiles but I did not really tested it.

For the translate, I would need some hint.
As far as I understand, a copy of the image is stored into ip2, then this copy is used to copy back the pixels to the original image but shifted by the translation.
The translation currently results in some black regions around the initial image. Yet in the code, I dont get how we get them, why aren’t they equal to the original image value ?

I dont get what the ip2.setBackgroundValue(0.0) actually does since ip2 is just an untouched copy of the original image, and changing the value of this parameter has no effect.

I though that one could initiate a new empty image processor, fill it with the value of background needed, and then set the pixel value from the initial image using an offset like in the current code.

The last step would update the initial image with this translate version but I don’t know how to do it since there is no object for the initial object.
Or should I use This = "TranslatedVersion" or the setPixel method ?


#10

In the latest ImageJ daily build (1.52i38), ShortProcessor.rotate() and FloatProcessor.rotate() fill using the value set by setBackgroundValue(). Here is a JavaScript example:

  img = IJ.createImage("Rotate 16-bit", "16-bit ramp", 500, 500, 1);
  ip = img.getProcessor();
  ip.setBackgroundValue(32768);
  ip.rotate(45);
  img.show();

  img = IJ.createImage("Rotate 32-bit", "32-bit ramp", 500, 500, 1);
  ip = img.getProcessor();
  ip.setBackgroundValue(0.5);
  ip.rotate(45);
  img.show();