fabio icon indicating copy to clipboard operation
fabio copied to clipboard

Tifimage: problem to use getframe function for stacks of single image files

Open osholm opened this issue 2 years ago • 11 comments

Apparently, there is a problem in tif image when using getframe to jump to another image. It seems related to tifimage expecting a multi-image file and not a series of images (possibly therefore it doesn't inherit the fabioutils version of getframe).

Example:

imtif = tifimage.tifimage() imtif.read('Rasterscan-ACT_Raster10deg-2000mu_Export_0010.tiff') imtif.getframe(30) Leads to this: File ~/opt/miniconda3/envs/MyPySide-conda/lib/python3.10/site-packages/fabio/tifimage.py:231, in TifImage.getframe(self, num) 229 tiff_header = self._tiffio.getInfo(num) 230 return self._create_frame(image_data, tiff_header) --> 231 raise Exception("getframe out of range")

Exception: getframe out of range

it ends up here in tifimage.py:

   if 0 <= num < self.nframes:
        image_data = self._tiffio.getData(num)
        tiff_header = self._tiffio.getInfo(num)
        return self._create_frame(image_data, tiff_header)
    raise Exception("getframe out of range")

where nframes is 1 because of only having one image in the file.

The code has changed quite a bit since I last looked at it, so I am unsure if I am expected to use another function for jumping to another file in the stack of tiffs but generally it should capture that it is a series (actually .next() and .previous() does work) and therefore try to goto to another file if there is no stack of images in the file itself, or?

osholm avatar Nov 19 '22 22:11 osholm

The module was re-written by @vallsv some time ago ... he is the one knowing best the changes he made (4 years back ...)

kif avatar Dec 01 '22 08:12 kif

Indeed this is a bug since the fabio is expected to search in fileseries... but I think I remember that there is now a file-series class to handle explicitly the case.

kif avatar Dec 01 '22 08:12 kif

I had a chat with @vallsv : the behaviour has changed on purpose and to play within file-series, there is a specific class which makes things cleaner. It is documented here: http://www.silx.org/doc/fabio/latest/getting_started.html?highlight=file%20series#fabio-file-series

kif avatar Dec 07 '22 08:12 kif

Hi Jerome,

Thanks for the update and it might be me, but currently I find it a bit complex for the average Joe to use the get_frame/getframe to jump forth and back in file series. It might be that is just an update of the documentation (the page you linked to in your last comment) that is needed. In the section under "FabIO old-fashion file series" there is this example:

import fabio
im100 = fabio.open('Quartz_0100.tif') # Open image file
print(im0.data[1024,1024])            # Check a pixel value
im101 = im100.next()                  # Open next image
im270 = im100.getframe(270)           # Jump to file number 270: Quartz_0270.tif

Is this purely a historic use of fabio? When I read the documentation it appears to me that it should still work, but there are better ways to handle this. If this example is not working anymore, I think we need to mark this more clearly.

Please, don't get me wrong, I think it is great that the code has developed and new and faster ways of dealing with reading large amount of data have been introduced. But our original vision was also to make it really easy to open a file a move around in a stack of images without knowing too many Python tricks.

Instead of the old way, I also tried the new "random" way.

import fabio
# The first filename of consecutive filenames while foobar_xxxx.edf exists
filename = "foobar_0000.edf"
with fabio.open_series(first_filename=filename) as series:
    frame1 = series.get_frame(1)
    frame100 = series.get_frame(100)
    frame19 = series.get_frame(19)

I can see that I can use this fairly similar to the old way: series = fabio.open_series(first_filename='Raster10deg-2000mu_Export_0010.tiff') and then do, e.g.: image10 = series.get_frame(10) but it appears that some functionality is broken, e.g. image10.getmin() gives this error:

File ~/opt/miniconda3/envs/MyPySide-conda/lib/python3.10/site-packages/fabio/fabioimage.py:160, in _FabioArray.getmin(self)
    158 def getmin(self):
    159     """ Find min value in self.data, caching for the future """
--> 160     if self.minval is None:
    161         if self.data is not None:
    162             self.minval = self.data.min()

apparently because TiffFrame is something different from Tiffimage. A similar situation is the same for all the "gets" .getstddev, .getmean etc

Sequential reading: I managed to read the file series sequentially. Although the example merely "scrolls" through the data. I think it would be good to add an example, which shows how use the sequentially read data in straight forward way.

If I follow the example below:

# Sequencial access
import fabio
# The first filename of consecutive filenames while foobar_xxxx.edf exists
filename = "foobar_0000.edf"
with fabio.open_series(first_filename=filename) as series:
    for frame in series.frames():
        frame.data
        frame.header
        frame.index                    # frame index inside the file series
        frame.file_index               # frame index inside the edf file
        frame.file_container.filename  # name of the source file

I get Attribute errors for index, file_index, file_container, e.g.

AttributeError: 'TifImage' object has no attribute 'file_container' So it seems there might be a some bugs for the new files series handling of tiff files.

Sorry, this got a bit long and possibly some this should have been move to a separate issue.

BR Henning

osholm avatar Dec 07 '22 10:12 osholm

A small update. I played a bit more with the sequential reading and I believe this example might help the user:

myframes = {}
i=0

with fabio.open_series(first_filename=filename) as series:
     for frame in series.frames():
          i += 1
          myframes[i] = frame

the objects in myframes seems to working in the usual way. E.g.

myframes[3].getmax() provides the maximum value of the frame.

osholm avatar Dec 07 '22 10:12 osholm

There are bugs in the documentation which deserve correction ...

Reopen this as iteration over fileseries.frames() sometimes gives FabioImage sometimes FabioFrame

kif avatar Dec 08 '22 15:12 kif

Sounds good, Jerome. Thanks.

osholm avatar Dec 08 '22 15:12 osholm

Do you want to have a look at #516 ? It should make the doc consistent with the code ... I tested locally with edf and tiff and in the doc with mar345

kif avatar Dec 08 '22 15:12 kif

Of course. I will have a look.

osholm avatar Dec 09 '22 08:12 osholm

As mentioned in my comment (to reviewing #516) the doc is now consistent with the code.

I have testet tiff files (both including single images and multiple images) and I find that the present function is a bit inconsistent. Wouldn't it make sense that all formats worked the same even if some has a possibility of being multiple image files. I believe that most data sets of tiff files (or edf files) or stored as single image files, despite the ability to store more images in one file. The example now given in the documentation with the mar2300 data can be used:

im1 = fabio.open("200mMmgso4_001.mar2300")

im4 = im1.getframe(4) or im2 = im1.next()

whereas im1 = fabio.open("Export_0001.tiff") im2 = im1.next() but

im1.getframe(4)

results in the following error despite im1.nframes is 1:

Exception Traceback (most recent call last) Cell In [93], line 1 ----> 1 im2.getframe(4)

File ~/Documents/Github/fabio/build/lib.macosx-11.0-arm64-cpython-310/fabio/tifimage.py:231, in TifImage.getframe(self, num) 229 tiff_header = self._tiffio.getInfo(num) 230 return self._create_frame(image_data, tiff_header) --> 231 raise Exception("getframe out of range")

Exception: getframe out of range

What do you think?

osholm avatar Jan 23 '23 09:01 osholm

To me next and the transparent file switch are both part of the old API which should be removed and not used anymore.

That's unefficient and it mix concepts of frames and file series.

That's in the end, difficult to maintain, difficult to predict, and difficult to adapt.

vallsv avatar Jan 24 '23 09:01 vallsv