Creating tiled images from numpy arrays

Hi everyone,
I’m trying to create an OMERO image from a numpy array. I have been using the Blitzgateway’s ‘createImageFromNumpySeq’ function sucessfully but in some cases the images seem to be too large and I have to do this by creating an image, a rawPixelService and tiling. That I managed using the code found here and there.
What I basically do is check for the BlitzGateway.getMaxPlaneSize(). If my image dimensions are smaller than the MaxPlaneSize, I use createImageFromNumpySeq, if larger, I tile the array and save it like that.
So far things were working until I tried to save a large float32 array (through tiling) when I run into an exception:

serverExceptionClass = ome.conditions.InternalException message = Wrapped Exception: (java.lang.UnsupportedOperationException): ROMIO pixel buffer only supports full row writes. }

Interestingly creating an image from the very same array is working fine using ‘createImageFromNumpySeq’, even if larger than MaxPlaneSize.
I found this thread and some linked issues.
I looked into Josh’s sample script here to do tiling saving and I used it for debugging and I can reproduce the issue by working with a float array in stead of a int array. Here is the version that is not working. There are three lines replaced

from omero.gateway import *
from omero.model import *
from omero.rtypes import *
from omero.util.tiles import *
from numpy import fromfunction

user = 'facility_staff_1'
pw = 'facility_staff_1_pw'
host = '192.168.56.101'

conn = BlitzGateway(username=user,
                    passwd=pw,
                    host=host,
                    port=4064)
conn.connect()

sizeX = 4096
sizeY = 4096
sizeZ = 1
sizeC = 1
sizeT = 1
tileWidth = 1024
tileHeight = 1024
imageName = "testStitchBig4K-1Ktiles"
description = None
tile_max = 255

pixelsService = conn.getPixelsService()
queryService = conn.getQueryService()

# query = "from PixelsType as p where p.value='int8'"
query = "from PixelsType as p where p.value='float'"
pixelsType = queryService.findByQuery(query, None)
channelList = range(sizeC)
bytesPerPixel = pixelsType.bitSize.val / 8
iId = pixelsService.createImage(
   sizeX,
   sizeY,
   sizeZ,
   sizeT,
   channelList,
   pixelsType,
   imageName,
   description,
   conn.SERVICE_OPTS)

image = conn.getObject("Image", iId)
pid = image.getPixelsId()

def f(x, y):
    """
    create some fake pixel data tile (2D numpy array)
    """
    return (x * y)/(1 + x + y)

def mktile(w, h):
    tile = fromfunction(f, (w, h))
    # tile = tile.astype(int)
    tile = tile.astype(float)
    tile[tile > tile_max] = tile_max
    return list(tile.flatten())

# tile = fromfunction(f, (tileWidth, tileHeight)).astype(int)
tile = fromfunction(f, (tileWidth, tileHeight)).astype(float)
tile_min = float(tile.min())
tile_max = min(tile_max, float(tile.max()))


class Iteration(TileLoopIteration):

    def run(self, data, z, c, t, x, y, tileWidth, tileHeight, tileCount):
        tile2d = mktile(tileWidth, tileHeight)
        data.setTile(tile2d, z, c, t, x, y, tileWidth, tileHeight)

loop = RPSTileLoop(conn.c.sf, PixelsI(pid, False))
loop.forEachTile(256, 256, Iteration())

c = 0
pixelsService.setChannelGlobalMinMax(
   pid, c, tile_min, tile_max, conn.SERVICE_OPTS)

conn._closeSession()

print("Image", iId.val)

Any idea of what is going on?

Hi @juliomateos,

My original gist certainly didn’t take floating point into account. The minimum changes I could fine to make your version work are:

@@ -19,8 +19,8 @@ sizeY = 4096
 sizeZ = 1
 sizeC = 1
 sizeT = 1
-tileWidth = 1024
-tileHeight = 1024
+tileWidth = 4096
+tileHeight = 1
 imageName = "testStitchBig4K-1Ktiles"
 description = None
 tile_max = 255
@@ -73,7 +73,7 @@ class Iteration(TileLoopIteration):
         data.setTile(tile2d, z, c, t, x, y, tileWidth, tileHeight)

 loop = RPSTileLoop(conn.c.sf, PixelsI(pid, False))
-loop.forEachTile(256, 256, Iteration())
+loop.forEachTile(tileWidth, tileHeight, Iteration())

 c = 0

but I wouldn’t say these are ideal. This is likely related to a larger floating point issue, e.g. see https://github.com/ome/omero-romio/issues/24

~Josh

Thanks,
I have two questions:

  • I understand that tileWidth must be equal to the image sizeX. Am I correct?
  • Is there some kind of relation of maximal tile size in here? I mean, could I do:
tileWidth = 4096
tileHeight = 5

as long as tileWidth * tileHeight < eg maxPlaneSizeX * maxPlaneSizeY or some limits imposed by Ice message size…?

That’s my reading of the code, yeah.

The limits I know of (in decreasing severity and increasing likelihood) are:

  • Java array size (2B entries);
  • Ice message size (250 MB or so);
  • 3000 x 3000 though this may not be enforced which is the definition of a “big plane”.

Testing briefly with height=10, an upload worked fine. However, viewing in the browser seems to be loading by traditional tile sizes, so I’m not sure you’re going to see the performance you may want.

But give it a try and let us know.
~J