#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2011 Glencoe Software, Inc. All rights reserved.
# Use is subject to license terms supplied in LICENSE.txt
[docs]
class TileLoopIteration(object):
"""
"Interface" which must be passed to forEachTile
"""
[docs]
def run(self, rps, z, c, t, x, y, tileWidth, tileHeight, tileCount):
raise NotImplemented()
[docs]
class TileData(object):
"""
"Interface" which must be returned by concrete TileLoop
implementations.
"""
[docs]
def getTile(self, z, c, t, x, y, w, h):
"""
:param z: int
:param c: int
:param t: int
:param y: int
:param w: int
:param h: int
:returns: byte[]
"""
raise NotImplementedError()
[docs]
def setTile(self, buffer, z, c, t, x, y, w, h):
"""
buffer: byte[]
:param z: int
:param c: int
:param t: int
:param y: int
:param w: int
:param h: int
"""
raise NotImplementedError()
[docs]
def close(self):
raise NotImplementedError()
[docs]
class TileLoop(object):
[docs]
def createData(self):
"""
Subclasses must provide a fresh instance of {@link TileData}.
The instance will be closed after the run of forEachTile.
:returns: TileData
"""
raise NotImplementedError()
[docs]
def forEachTile(self, sizeX, sizeY, sizeZ, sizeC, sizeT,
tileWidth, tileHeight, iteration):
"""
Iterates over every tile in a given Pixels object based on the
over arching dimensions and a requested maximum tile width and height.
:param sizeX: int
:param sizeY: int
:param sizeZ: int
:param sizeC: int
:param sizeT: int
:param iteration: Invoker to call for each tile.
:param pixel: Pixel instance
:param tileWidth: <b>Maximum</b> width of the tile requested.
The tile request itself will be smaller than the original tile
width requested if <code>x + tileWidth > sizeX</code>.
:param tileHeight: <b>Maximum</b> height of the tile requested.
The tile request itself will be smaller if
<code>y + tileHeight > sizeY</code>.
:returns: The total number of tiles iterated over.
"""
data = self.createData()
try:
tileCount = 0
for t in range(0, sizeT):
for c in range(0, sizeC):
for z in range(0, sizeZ):
for tileOffsetY in range(
0, ((sizeY + tileHeight - 1) // tileHeight)):
for tileOffsetX in range(
0, ((sizeX + tileWidth - 1) // tileWidth)):
x = tileOffsetX * tileWidth
y = tileOffsetY * tileHeight
w = tileWidth
if (w + x > sizeX):
w = sizeX - x
h = tileHeight
if (h + y > sizeY):
h = sizeY - y
iteration.run(data, z, c, t,
x, y, w, h, tileCount)
tileCount += 1
return tileCount
finally:
data.close()
[docs]
class RPSTileData(TileData):
"""
"""
def __init__(self, loop, rps):
self.loop = loop
self.rps = rps
self.pixels = None
[docs]
def getTile(self, z, c, t, x, y, w, h):
return self.rps.getTile(z, c, t, x, y, w, h)
[docs]
def setTile(self, buffer, z, c, t, x, y, w, h):
self.rps.setTile(buffer, z, c, t, x, y, w, h)
[docs]
def close(self):
pixels = self.rps.save()
self.loop.setPixels(pixels)
self.rps.close()
[docs]
class RPSTileLoop(TileLoop):
def __init__(self, session, pixels):
self.session = session
self.pixels = pixels
[docs]
def getSession(self):
return self.session
[docs]
def getPixels(self):
"""
After saving the binary data, the update event of the
{@link Pixels} instance will be updated and therefore
need to be reloaded. As a convenience the returned
value is accessible here.
"""
return self.pixels
[docs]
def setPixels(self, pixels):
"""
Used by RPSTileData to set a reloaded Pixels instance
for client use.
"""
self.pixels = pixels
[docs]
def createData(self):
rps = self.getSession().createRawPixelsStore()
data = RPSTileData(self, rps)
# 'false' is ignored here.
rps.setPixelsId(self.getPixels().getId().getValue(), False)
return data
[docs]
def forEachTile(self, tileWidth, tileHeight, iteration):
"""
Iterates over every tile in a given Pixels object based on the
over arching dimensions and a requested maximum tile width and height.
:param tileWidth: <b>Maximum</b> width of the tile requested. The tile
request itself will be smaller than the original tile width requested
if <code>x + tileWidth > sizeX</code>.
:param tileHeight: <b>Maximum</b> height of the tile requested.
The tile request itself will be smaller if
<code>y + tileHeight > sizeY</code>.
:param iteration: Invoker to call for each tile.
@return The total number of tiles iterated over.
"""
if self.pixels is None or self.pixels.id is None:
import omero
raise omero.ClientError("pixels instance must be managed!")
elif not self.pixels.loaded:
try:
srv = self.getSession().getPixelsService()
self.pixels = srv.retrievePixDescription(self.pixels.id.val)
except Exception as e:
import omero
raise omero.ClientError(
"Failed to load %s\n%s" % (self.pixels.id.val, e))
sizeX = self.pixels.getSizeX().getValue()
sizeY = self.pixels.getSizeY().getValue()
sizeZ = self.pixels.getSizeZ().getValue()
sizeC = self.pixels.getSizeC().getValue()
sizeT = self.pixels.getSizeT().getValue()
return TileLoop.forEachTile(
self, sizeX, sizeY, sizeZ, sizeC, sizeT,
tileWidth, tileHeight, iteration)