ome / bioformats

Bio-Formats is a Java library for reading and writing data in life sciences image file formats. It is developed by the Open Microscopy Environment. Bio-Formats is released under the GNU General Public License (GPL); commercial licenses are available from Glencoe Software.
https://www.openmicroscopy.org/bio-formats
GNU General Public License v2.0
381 stars 241 forks source link

How can i resize the input image into desired size? Is there any easiest way to to do? #3641

Closed Yuvi-416 closed 3 years ago

Yuvi-416 commented 3 years ago

Hi there, I have a ndpi image. I want to resize .ndpi image whose first stack has a size of 186496 x 36608 into 93248 x 18304. I don't want to save it. I just want to load it into memory and later wants to read the resized image in the form of a tile.

I study about write_image function but it's not letting me select the desired size.

Any help

dgault commented 3 years ago

Hi @Yuvi-416, are you looking to do this from an ImageJ macro? If so the below code snippets will need modified a little, I can help you with that if needed.

If you do not want to save the file again with all of the resolutions then it will mean downsampling the original on the fly. There is a SimpleImageScaler class which we use internally for downsampling. A good example of how to read a downsampled tile would be the getTile method here: https://github.com/ome/bioformats/blob/develop/components/bio-formats-tools/src/loci/formats/tools/ImageConverter.java#L1070

I have added a few additional comments to it below:

    // If the reader has enough resolutions then read the original data
    if (resolution < reader.getResolutionCount()) {
      reader.setResolution(resolution);
      return reader.openBytes(no, x, y, w, h);
    }

   // Otherwise set the reader to the highest resolution image
    reader.setResolution(0);

    // Create an ImageScaler
    IImageScaler scaler = new SimpleImageScaler();
    int scale = (int) Math.pow(pyramidScale, resolution);

   // Read the original full resolution tile
    byte[] tile =
      reader.openBytes(no, x * scale, y * scale, w * scale, h * scale);
    int type = reader.getPixelType();

   // Return the downsampled tile
    return scaler.downsample(tile, w * scale, h * scale, scale,
      FormatTools.getBytesPerPixel(type), reader.isLittleEndian(),
      FormatTools.isFloatingPoint(type), reader.getRGBChannelCount(),
      reader.isInterleaved());
Yuvi-416 commented 3 years ago

Hi there, Thanks for your quick reply. I was looking to perform downsampling to the original image in a python environment using Python-bioformats, not in an ImageJ. I don´t know whether there is a function like SimpleImageScaler or a getTile in a 4.0 version Python-bioformats. You can share some idea how can I achieve my goal on python env? Is there any function which can handle these things? The code which you have shared above looks cool and interesting too. can I use that code using a python-bioformats?

dgault commented 3 years ago

The first section of the code should be as normal for reading your image tile in python. The last bit, the call to downsample I'm afraid doesn't have an easy helper method yet. You should however be able to use the javabridge in a similar way to python-bioformats to wrap the java class and function to allow you to use it in python (https://pythonhosted.org/javabridge/highlevel.html#wrapping-java-objects-using-reflection). I have not yet tested it but it should look something similar to the below:

class SimpleImageScaler:
        new_fn = javabridge.make_new("loci/common/image/SimpleImageScaler", "()V")
        def __init__(self):
            self.new_fn()
        downsample = javabridge.make_method("downsample", "([B, I, I, D, I, Z, Z, I, Z)[B", "")
imagesc-bot commented 3 years ago

This issue has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/how-to-twitch-python-bioformats-code-so-that-it-will-show-all-dimensions-like-openslide-do/45950/9

Yuvi-416 commented 3 years ago

Hi there, I tried your suggestion but I get an unknown error related to self.o (I didn't understand much about this) Code:

class SimpleImageScaler:
    new_fn = javabridge.make_new("loci/common/image/SimpleImageScaler", "()V")

    def __init__(self):
        self.new_fn()

    downsample = javabridge.make_method("downsample", "([B, I, I, D, I, Z, Z, I, Z)[B", "")

##############################
# Read the Tiles
# Series---»z-stack---
##############################
for series in range(reader.rdr.getSeriesCount()):
    series += 3
    width = reader.rdr.getSizeX()
    height = reader.rdr.getSizeY()
    reader.rdr.setSeries(series)
    # read the z-stack
    for zstack in range(reader.rdr.getImageCount()):

        width = reader.rdr.getSizeX()
        height = reader.rdr.getSizeY()

        # Determined the number of tiles to read and write
        nXTiles = int(math.floor(width / tile_SizeX))
        nYTiles = int(math.floor(height / tile_SizeY))
        if nXTiles * tile_SizeX != width:
            nXTiles = nXTiles + 1
        if nYTiles * tile_SizeY != height:
            nYTiles = nYTiles + 1
        encoded_framed_items = []
        for y in range(nYTiles):
            for x in range(nXTiles):
                # The x and y coordinates for the current tile
                tileX = x * tile_SizeX
                tileY = y * tile_SizeY
                effTileSizeX = tile_SizeX
                if (tileX + tile_SizeX) >= width:
                    effTileSizeX = width - tileX
                effTileSizeY = tile_SizeY
                if (tileY + tile_SizeY) >= height:
                    effTileSizeY = height - tileY
                # Read and write tiles
                i = reader.rdr.openBytes(zstack, tileX, tileY, effTileSizeX, effTileSizeY)
                buf1 = reader.read(zstack, rescale=False, XYWH=(tileX, tileY, effTileSizeX, effTileSizeY))
                scale = 2
                ds = SimpleImageScaler
                ds. downsample(i, width * scale, height * scale, scale)

Error:

Traceback (most recent call last):
  File "/snap/pycharm-community/219/plugins/python-ce/helpers/pydev/pydevd.py", line 1448, in _exec
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/snap/pycharm-community/219/plugins/python-ce/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/home/yuvi/PycharmProjects/task-1/temp/Temp-2-others.py", line 99, in <module>
    ds. downsample(i, width * scale, height * scale, scale)
  File "/home/yuvi/anaconda3/envs/task-1/lib/python3.7/site-packages/javabridge/jutil.py", line 960, in method
    assert isinstance(self.o, _javabridge.JB_Object)
AttributeError: 'numpy.ndarray' object has no attribute 'o'
python-BaseException

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

Error part:

def make_method(name, sig, doc='No documentation', fn_post_process=None):
    '''Return a class method for the given Java class. When called,
    the method expects to find its Java instance object in ``self.o``,
    which is where ``make_new`` puts it.

    :param name: method name
    :param sig: calling signature
    :param doc: doc string to be attached to the Python method
    :param fn_post_process: a function, such as a wrapper, that transforms
                            the method output into something more useable.
    '''
    def method(self, *args):
        assert isinstance(self.o, _javabridge.JB_Object)
        result = call(self.o, name, sig, *args)
        if fn_post_process is not None:
            result = fn_post_process(result)
        return result

    method.__doc__ = doc
    return method

I have another question too. As my input image is 186496 x 36608 and I want the downsampled image in a size 93248 x 18304. And the extracted tile size is: 1024* 1024 So, what should be the value of scale, height and weight in the downsample function?