Jython ShortProcessor un/signed issue

jython
python
Tags: #<Tag:0x00007fa304590840> #<Tag:0x00007fa3045903b8>

#1

I am using Jython to create a plugin to read unsigned integers from a image file used in electron microscopy. I am proficient in Python, but fairly new to ImageJ development.

I ran across a potential bug with the ij.process.ShortProcessor and how it interacts with Python arrays, lists, tuples, etc. The images I want to read are almost always unsigned integer 16 bit values. This should be no problem for ShortProcessor as the documentation says it expects unsigned values. However, I am getting an error even when the data is in the proper (unsigned 16 bit) format. The example codes below show the problem as simply as possible:

This code works properly:

from ij import ImagePlus
from ij.process import ShortProcessor
import array

#Set up an python array.array() with signed 16-bit integer values 
min1 = -32000
max1 = 32000
#Create an array of signed 16 bit values
vals1 = array.array('h',range(min1,max1))
#Add the values to a ShortProcessor
fp1 = ShortProcessor((max1-min1)/100,100,vals1,None)
imp1 = ImagePlus('signed 16 bit', fp1)
imp1.show()

The image that is shown ramps from ~32000 to ~64000 and then starts at 0 to ~32000 from the center. This means that the signed integer values were read as unsigned integer values. I guess this is as expected.

The following code should work properly and display a ramp of values in the shown image from 0 to 65000. (See the Python array class for reference) However, this code gives an error (see after code):

from ij import ImagePlus
from ij.process import ShortProcessor
import array

#Set up an python array.array() with unsigned 16-bit integer values 
min2 = 0
max2 = 60000
#Create an array of unsigned 16-bit values
vals2 = array.array('H',range(min2,max2))

#Add the values to a ShortProcessor
fp2 = ShortProcessor((max2-min2)/100,100,vals2,None)
print("this should be False: {}".format(fp2.isSigned16Bit()))
imp2 = ImagePlus('unsigned 16 bit', fp2)
imp2.show()

The error thrown by the interpreter is:

Traceback (most recent call last):
    File "C:\Users\Peter\Desktop\shortProcessor_bug.py", line 26, in <module>
    fp2 = ShortProcessor((max2-min2)/100,100,vals2,None)
    TypeError: ij.process.ShortProcessor(): 3rd arg can't be coerced to short[]

Some other things to note:

  1. If you change max2 in the second code block to 32000 then the code works, because the array values fit into a signed short range of values (i.e. max is less than 32768).
  2. This problem also happens if you use Python lists or tuples instead of array.array(). So, its not an issue with the less used Python array class.
  3. I am able to get around this issue by using a tuple and FloatProcessor and then cast back to a ShortProcessor using convertToShortProcessor(). This does not work with array.array() and is memory inefficient.
  4. I want to use array.array() because it is about 10x faster than struct.unpack(), which gives back a tuple.
  5. The exact same same issue detailed above occurs with the ByteProcessor.

Am I using ShortProcessor wrong? Does anyone have any ideas why ShortProcessor is giving this error?


#2

What is your specific use case that requires writing low-level code to read in pixels? There’s a good chance that Bio-Formats or SCIFIO already can do what you’re trying to achieve.

The constructor of ShortProcessor takes a short[] input for the pixels array, and short in Java is always signed (-32,768 to 32,767).

As a ShortProcessor in ImageJ1 is always unsigned, the conversion (from and to int actually) happens in its get and set methods:

Does that clarify things for you?


#3

Bioformats does not implement this file type (FEIs EMI/SER file type for scanning transmission electron microscopy data).

I already have working code in Python to read the data. I thought it would be easy to get this working for myself and many users left without a nice way too read one or many files and all meta data.

I see how signed Java arrays are converted using two’s complement to unsigned ImageJ image arrays thanks to the code you pointed me to.

However, I can’t figure out a way to create an unsigned Python array.array() which can be accepted by ShortProcessor. If this is not possible then it seems like a limitation of the Python-ImageJ connection.


#4

I thought about the first response and did a little more research online. There is no way around having to do some unsigned too signed integer conversion for Jython in ImageJ.

I’ll post back when I work it out in case anyone else is interested in the future.


#5

The solution was actually very simple. Here is how I solved the issue to load raw unsigned integer data in Jython (Python) using array.fromfile() and transfer it to Fiji (ImageJ).

The solution was to simply use a signed array type to read the unsigned data directly from disk. Then there is no need for signed–>unsigned conversion.

import array #import the built-in python module called array
from ij import IJ, ImagePlus
from ij.process import ShortProcessor

with open(fname,'rb') as f:
    rawInt = array.array('i') #lower-case 'i' indicates signed integer 16-bit
    rawInt.fromfile(f,512*512) #read in as 1D array with the correct number of pixels for 512x512 image. Reads as Big Endian
    rawInt.swapbytes() #if the data is Little Endian then you need to swap the bytes
sp = ShortProcessor(512,512,rawInt,None) #create a short processor from the array.array
imp = ImagePlus('image title',sp) #create an ImagePlus
imp.show() #show the image in ImageJ

Thanks for your help with this @imagejan!


#6

Hi @ercius ,
maybe you are already out of this theme, but I am still working on something similar, so I decided to write about it here:

I am working with a Tecnai, so I am facing the same problem as everone: the .ser and .emi files. I created a macro (Macro to batch convert DM3 to TIFF) that is used to batch convert all the STEM images (both images and EDS spectra) to tif (or other formats) with their scalebar and two-column text file for spectra (along with the same procedure for TEM dm3 files for images, EELS spectra and eDIFF).

Now I am trying to go one step forward: I saw that the .emi files have a lot of information on them (from FEG voltage to tilting angle, all the quantifications performed while the file was open…). I want to extract this info.

I guess with python this would be easy (it is a matter of: find the name of the .ser file inside the .emi file; jump 14 characters forward, and copy the text between
<Objectinfo> and </Objectinfo>",
but doing this in Imagej seems to be tough.

Just in case you would have some recommendation for me to avoid hours of test-error procedure, I left this comment here. Also maybe someone have already done something similar and wants to share with the community :slight_smile:

Best regards !


#7

@Oliver_Dieste,

Thanks for the follow-up with this topic and thanks for pointing me to the macro you wrote.

I have a few suggestions on how to deal with these types of files.

  1. I ended up writing a full featured Jython plugin for Fiji that will read SER files and the EMI metadata. I implemented single file load/convert, whole directory load/convert, and EMI metadata loading. I have not release it publically yet, but please email me to get a beta copy. (This makes me think that I should release it more generally soon!). Here is a screenshot of the GUI:
    Clipboard01

  2. I maintain open source code for Python and Matlab for reading DM3/4 and SER files (among other file types). Check out ncempy on github and my older repository on bitbucket.

  3. I am not a Java developer but I suggest searching the EMI file as text for the tags which are written as XML. I used the xml.etree.ElementTree module in Python to parse the XML text. ImageJ should have a similar functionality.

Let me know if you have any other questions about reading these types of files.


#8

Hi!
First of all, thanks for answering.
Second, thanks for creating telling me about the python stuff that you created. Probably I will come to it soon :slight_smile:
Next, I am really willing to see your plugin. I am still starting to look for it, but could not figure it out how to get imagej to read the xml file. If it could be done as in python, that would be great, to save all the information in a text file with the same name as the image. Also to extract the quantification, that I still didn’t find how the hell can be done with TIA :frowning:

In case i found something useful, I will share here.

Best regards,