Macro help for simple automated image arithmetic

Hey everyone,

I was trying to program a macro for some simple image arithmetics, but unfortunately I have no clue, how to extend it for application on several images.

The case is the following:
I have all images in one folder somewhere on my computer. I want the macro to open the first one (image) and to apply the minimum filter with a radius of 1 (image_1). Afterwards, the minimum image should be multiplied with the original image (image_2). The resulting image should be substracted from the original image (image_3). This image should then be saved into a new folder.
After this, the second image should be opend and the procedure starts again.

I tried using the macro recorder, which gave me the following text:

open("C:\\2_000.jpg");
run("Minimum...", "radius=1");
open("C:\\2_000.jpg");
imageCalculator("Multiply create", "2_000-1.jpg","2_000.jpg");
selectWindow("Result of 2_000-1.jpg");
imageCalculator("Subtract create", "2_000-1.jpg","Result of 2_000-1.jpg");
selectWindow("Result of 2_000-1.jpg");

This can be applied for the stated image “2_000.jpg”. How can I automate the macro in a way stated above?
I tried to build this routine in KNIME, but somehow the results for “image_3” (as named above) differ significantly, which is why KNIME won’t work (at least so far) not for this case…

It would be great, if someone could help :slight_smile:

Kind regards,
Björn

Dear @Bjoern,

Could you provide an example workflow that demonstrates this issue?

With respect to the ImageJ1 macro, you will have to get the name of the currently active image (since that is the one ImageJ operates on):

inputTitle = getTitle();
run("Duplicate...", " ");
run("Minimum...", "radius=1");
rename(inputTitle+"-minimum");
selectWindow(inputTitle);
imageCalculator("Multiply create", inputTitle+"-minimum", inputTitle);
rename(inputTitle+"-minimum-multiply");
imageCalculator("Subtract create", inputTitle, inputTitle+"-minimum-multiply");

You can use this macro with the Process > Batch > Macro option (more details: http://imagej.net/docs/guide/146-29.html#toc-Subsection-29.12).

Best,
Stefan

1 Like

There is a page in the wiki explaining how to apply the same operation to a folder of images, maybe that helps?

Hello Bjoern, it just so happens I wrote a similar script to what you want (I think) yesterday. I cut out the pieces you need. See if this works.

Copy paste this code to the script editor in Fiji and select language Jython, then run it.

from ij import IJ
from ij.plugin import ImageCalculator
from ij.plugin import Duplicator
import os

def imagecalc():

	# Define directories.
	source = IJ.getDirectory("Select source directory")
	destination = IJ.getDirectory("Select destination directory")

	# Iterate through folder and do stuff.
	for root, directories, filenames in os.walk(source):
		for filename in filenames:
			imp = IJ.openImage(os.path.join(root, filename))
			imp_orig = Duplicator().run(imp)

			IJ.run(imp, "Minimum...", "radius=1")
			multiplied_imp = ImageCalculator().run("Multiply create 32-bit", imp, imp_orig)
			final_imp = ImageCalculator().run("Subtract create 32-bit", imp_orig, multiplied_imp)

			IJ.saveAs(final_imp, "Tiff", os.path.join(destination, filename))
			
imagecalc()
1 Like

Thanks to all of your for your replies.

@stelfrich: my KNIME workflow is given here: Image_arithmetics.knwf (3.1 MB)
The resulting image for the same routine in ImageJ is given below. As you can see, the results differ significantly, although the images between the final result (min image, image product) look exactly the same…

@iarganda: I will have a look at the tutorial, thanks for the hint!

@Sverre: thanks for your code. I will give it a try and give you feedback :wink:

Best regards,
Björn

.

1 Like

I guess the looks are misleading here.

The issue you have stumbled over actually has to do with the handling of result values of computations wrt to original type (i.e. bit depth) of the input image. An 8-bit grayscale image can contain values in the range [0, 255] for each pixel. When you are doing the multiplication with the Image Calculator without enabling the 32-bit (float) result option, the resulting image will have a bit depth of 8-bit. The values computed during the multiplication, e.g. 240*245=58800, exceed the 8-bit range; the resulting pixel values are to set 255 (maximum of the 8-bit range).
The same argumentation applies for the subtraction, just that pixels with negative values from the computation are set to 0 in this case.

In contrast to ImageJ, there seems to be an issue with those conversions in the Image Calculator node in KNIP. You can work around this limitation, for now, by doing the computations with floating point precision (32-bit) and converting bit depths in between computations. This workflow demonstrates what I am talking about:

BilderstapelErzeugenFixed.knwf (3.2 MB)

Best,
Stefan

1 Like

As far as I know, it was a deliberate decision of the developers to not “clip” any intensity values when doing calculations in the various Types that Imglib2 (the backing framework of both KNIME Image Processing and ImageJ2) provides.

Instead, values that exceed the maximum of the given type will simply wrap around, i.e. for UnsignedByteType (which is the equivalent of 8-bit type in ImageJ), 255+1=0, 250+100=(350-2^8)=94, and 10-20=(2^8 - 10)=246.

In conclusion, when doing any calculations, you’ll have to make sure that the used image type can accomodate the resulting values.

Also, you’ve hit another ImageJ peculiarity in your routine: when opening jpeg files (which are by default stored in RGB format), ImageJ checks whether all three channels contain the same values, and if yes, will display a single 8-bit greyscale channel. KNIME, on the other hand, displays a true 3-channel image.

2 Likes

I had completely forgotten about that discussion, thanks for reminding me @imagejan! That also explains the “inconsistency” with the explicit conversion in between… :thumbsup:

@stelfrich and @imagejan: Thanks for clearing up the problem and for the workaround solution in KNIME. This works perfectly!

@Sverre: Thanks again for your provided code. It does exactly what I needed. Thanks for your help! :slight_smile:

1 Like