Creating a thermal image- setting pixel values

jython
imageplus
Tags: #<Tag:0x00007fa306d0f3f8> #<Tag:0x00007fa306d0f268>

#1

Hello
I am quite new to scripting and new to image processing using imageJ
I am trying to convert my images into thermal images using a predetermined non linear Look Up Table. I am having difficulty with just writing a new image with the pixel values based on the original image and the LUT.
I have opened my original image as an imageplus object and i have managed to get all the pixel values from the image and do the maths to convert them to the values I want but how do I now write these new values to the same pixel location? my code look like this so far

imp = IJ.openImage(os.path.join(currentDir, fileName))                                       #open the original image
dimensions = imp.getDimensions()                                                                      #get size of original image
impT = IJ.createImage("T_image", "16-bit", dimensions[0], dimensions[1], 1)     #make a new image with same dimensions                    
for pixelx in range(dimensions[0]):                                                                         #loop over all pixels
    for pixely in range(dimensions[1]):
      sig = imp.getPixel(pixelx,pixely)[0]                                                                     #get original pixel value
      impT.setPixel(pixelx,pixely,temp[sig])                                                                 #write new pixel value to new image

setPixel does not exist in imagePlus and i cant find an alternative that does the same job also i believe this stepwise pixel manipulation is very slow can someone recommend how i should be going about this?
Thanks


#2

Hello Leigh -

Let me give you two answers:

First answer:

In general, to work with pixels, you will want to use an
ImageProcessor rather than an ImagePlus. (Roughly speaking,
the ImageProcessor is the container for the pixels, with a bunch
of utility functions for manipulating them, while the ImagePlus is
a higher-level construct that is a container for the ImageProcessor
and various metadata, and various higher-level manipulation
functions.

ImageProcessor.putPixel (int x, int y, int value)
is basically the setPixel() function you were looking for that
doesn’t exist in ImagePlus.

Here is your script, tweaked to us your ipmT's ImageProcessor:

import ij.IJ as IJ
imp = IJ.createImage("imp", "16-bit ramp", 256, 256, 1)
imp.show()
#
dimensions = imp.getDimensions()
impT = IJ.createImage("T_image", "16-bit", dimensions[0], dimensions[1], 1)
#
ip = impT.getProcessor()
#
for pixelx in range(dimensions[0]):
    for pixely in range(dimensions[1]):
      sig = imp.getPixel(pixelx,pixely)[0]
      #
      ip.putPixel (pixelx, pixely, sig / 2)
      #
impT.show()

If you have an ImagePlus (such as your impT), you can call
ImagePlus.getProcessor() to get its ImageProcessor.

In my tweaked script, sig / 2 is just a placeholder for whatever
processing you want to do on the pixel value, such as indexing
it into a look-up table.

Second answer:

If your main goal is to add a look-up table to your image, it might
be simpler to use ImageJ’s built-in support for look-up tables. You
can try opening your image and then play around with ImageJ’s
look-up table commands. For example, from the (Fiji) gui you can
run:

Image > Lookup Tables > Fire

to apply the “Fire” look-up table to your image. You might also look
at Image > Color > Show LUT and Image > Color > Edit LUT....
You can use your own custom LUTs – you’re not limited to those that
ship with ImageJ / Fiji.

Thanks , mm


#3

Hi mountain_man

Thanks a lot for your response that is exactly what I was looking for.

In regard to your suggestion of applying a non linear colour based LUT.
For visualization purposes that would be fine but I want to do further analysis on the images based on the temperatures rather than the radiance values so I think that means I need the pixels to have the units of temperature first…
it is possible i could do all the analysis based on radiance and then convert it but it is conceptually easier for me to work with the Temperature values.


#4

As your question involves concerns about speed, probably because of nested loops, I guess you would have achieved the same result using the command changeValues(v1, v2, v3) in a loop over all values (or value ranges) present in your source image, replacing radiance values v1-v2 to the desired corresponding temperature values v3 in a duplicated image. With the appropriate supporting variables and a lookup table filled with radiance -> temperature, this would be an ImageJ macro code snippet to this end:

for(radiance = minRadiance; radiance<=maxRadiance;intensity++){
	changeValues(radiance,radiance,my_temperature_lookup[radiance]);
}

#5

Hi eljonco and Leigh -

If you are concerned about speed, you should avoid this suggested
use of changeValues. The implementation of this ImageJ macro
function, changeValues, is written in java (as is basically all of
ImageJ). It, itself, uses nested loops to iterate over the x and y
coordinates of the image, and calls getPixel(x, y). This approach
calls changeValues number-of-radiances times, and so, will
have a cost of number-of-radiances * number-of-pixels,
rather than just number-of-pixels.

I wouldn’t worry too much about further optimizing your original
approach – not because performance doesn’t matter, but
because, no matter what you do, you have to touch each pixel
in the image (at a cost of number-of-pixels). You’re already
doing the right thing: the potentially expensive part – converting
the radiance to temperature – you’re doing by indexing into
a pre-computed look-up table, which is about as cheap as you
can get.

If you want to tweak things further, there is some overhead with
the getPixel() call, so you would probably save a little time if
you worked directly with the underlying pixel array, which you
can access with getPixels().

Here is a script that illustrates this:

import ij.IJ as IJ
imp = IJ.createImage("imp - 16-bit ramp", "16-bit ramp", 256, 256, 1)
pixels = imp.getProcessor().getPixels()
for ind in range (len(pixels) / 2):  # only change half the image
    sig = pixels[ind]
    pixels[ind] = ((sig + 65536) % 65536) / 2   # signed--unsigned
imp.show()

The only minor issue is that the underlying java data type for a
16-bit pixel is a java short, which (like all integral java types) is
signed. The nonsense with + 65536) % 65536) is just to do
the correct unsigned arithmetic when dividing by two, before
storing the result back in the pixel. You will probably have to
do something similar to make sure sig is non-negative when
you use it to index into your look-up table.

If performance is super-critical, you could write this part of
the code in java to avoid the modest overhead of the jython
interpreter and type conversions / type wrapping. But I would
imagine that, by the time you drill down to iterating over
elements of an array, the jython interpreter should be quite
good, so I wouldn’t expect that you would save a lot.

Thanks, mm


#6

Thanks for these insights @mountain_man!
Gone are the NIH-Image days where a mere apply_lut would solve, probably assembler-coded, all of your (8-bit) problems :sunglasses: :

The other option is to change the pixel values, and use the original linear LUT. This can be done using the Process - Apply LUT command and is necessary if the image is to be combined with another image with a different LUT. The appearance of the image will not change after using Apply LUT , but the color bar in the LUT window will change back to the original ramp, and the graph in the Map window will change back to the diagonal straight line.