from Query import Query
import numpy as np
[docs]class DataQuery(Query):
""" Describe ``INPUT.METHODS.IMAGE_LIST`` requests
Arguments
-----------
args: list
unused
keywords: dict
* `OUTPUT.INFO.PATH` (str) --
the path to the given :class:`Datasource`
* `INPUT.POSITION.Z` (int) --
the scaled resolution z start of the image request
* `INPUT.POSITION.Y` (int) --
the scaled resolution y start of the image request
* `INPUT.POSITION.X` (int) --
the scaled resolution x start of the image request
* `INPUT.POSITION.DEPTH` (int) --
the requested scaled depth of the image request
* `INPUT.POSITION.HEIGHT` (int) --
the requested scaled height of the image request
* `INPUT.POSITION.WIDTH` (int) --
the requested scaled width of the image request
* `INPUT.RESOLUTION.XY` (int) --
the number of halvings along the X and Y axes
* `INPUT.RESOLUTION.FORMAT` (str) --
the requested output image file format
* `INPUT.RESOLUTION.VIEW` (str) --
the requested coloring method for images
"""
#: default mime type for HTTP response
_basic_mime = 'image/{}'
def update_keys(self, keywords):
Query.update_keys(self, keywords)
for key in ['Z','Y','X']:
self.set_key(self.INPUT.POSITION, key, 0)
for key in ['DEPTH','HEIGHT','WIDTH']:
self.set_key(self.INPUT.POSITION, key, 1)
for key in ['VIEW','FORMAT','OFFSET']:
self.set_key(self.INPUT.IMAGE, key)
self.set_key(self.OUTPUT.INFO, 'PATH')
self.set_key(self.INPUT.RESOLUTION, 'XY', 0)
@property
def key(self):
""" return the key for the database
Returns
-------
str
the path value from ``OUTPUT.INFO``
"""
return self.OUTPUT.INFO.PATH.VALUE
@property
def dtype(self):
""" return actual numpy dtype object
Returns
-------
numpy.dtype
from the type value from ``OUTPUT.INFO``
"""
dtype = self.OUTPUT.INFO.TYPE.VALUE
return getattr(np,dtype, np.uint8)
@property
def source_off(self):
""" Get the data offset at full resolution
Returns
--------
numpy.ndarray
[z_off, y_off, x_off]
"""
return np.int64(self.INPUT.IMAGE.OFFSET.VALUE)
@property
def target_off(self):
""" Get the data offset at target resolution
Returns
--------
numpy.ndarray
[z_off, y_off, x_off]
"""
return np.int64(self.source_off // self.scales)
@property
def scales(self):
""" convert a number of halvings to a scale array
Returns
-------
numpy.ndarray
A 3x1 array of the target/source voxel ratios
"""
s_xy = 2 ** self.INPUT.RESOLUTION.XY.VALUE
return np.float32([1,s_xy,s_xy])
@property
def blocksizes(self):
""" get the sizes of each :class:`Datasource` tile
Returns
-------
numpy.ndarray
The Nx3 block values from ``RUNTIME.IMAGE``
"""
return np.uint32(self.RUNTIME.IMAGE.BLOCK.VALUE)
@property
def blocksize(self):
""" get the size of a :class:`Datasource` tile
Returns
-------
numpy.ndarray
The 3x1 block value for this resolution
"""
smallest = len(self.blocksizes) - 1
res_xy = self.INPUT.RESOLUTION.XY.VALUE
return self.blocksizes[min(res_xy, smallest)]
@property
def target_shape(self):
""" Scaled target size of the requested volume
Returns
-------
numpy.ndarray
The 3x1 block of DEPTH, HEIGHT, WIDTH \
values from `INPUT.POSITION`
"""
shapes = ['DEPTH','HEIGHT','WIDTH']
dhw = map(self.INPUT.POSITION.get, shapes)
return np.uint32([s.VALUE for s in dhw])
@property
def source_shape(self):
""" Full source size of the requested volume
Returns
--------
numpy.ndarray
The 3x1 :meth:`target_shape` multiplied by \
the voxel scales from :meth:`scales`.
"""
return np.uint32(self.target_shape * self.scales)
@property
def target_origin(self):
""" Scaled target offset of the requested origin
Returns
--------
numpy.ndarray
The 3x1 block of Z, Y, and X in `INPUT.POSITION`
"""
zyx = map(self.INPUT.POSITION.get,'ZYX')
origin = [c.VALUE for c in zyx] + self.target_off
return np.uint32(np.maximum(origin, [0,0,0]))
@property
def source_origin(self):
""" Full source offset of the requested origin
Returns
--------
numpy.ndarray
The 3x1 :meth:`target_origin` multiplied by \
the voxel scales from :meth:`scales`.
"""
return np.uint32(self.target_origin * self.scales)
@property
def target_bounds(self):
""" Scaled bounding box of the requested volume
Returns
--------
numpy.ndarray
The 2x3 joining of :meth:`target_origin` \
and :meth:`target_shape` transposed such that \
each row gives one of two bounding corners.
"""
z0y0x0 = self.target_origin
z1y1x1 = z0y0x0 + self.target_shape
return np.c_[z0y0x0, z1y1x1].T
@property
def tile_bounds(self):
""" Bounding box given by counting whole tiles
Returns
--------
numpy.ndarray
The 2x3 joining of the lowermost tile \
and the uppermost tile transposed such that \
a row gives the upper or lower limit of tiles.
"""
target_bounds = self.target_bounds
float_block = np.float32(self.blocksize)
float_bounds = target_bounds / float_block
# Find lowest tile index and highest tile index
bounds_start = np.uint32(np.floor(float_bounds[0]))
bounds_end = np.uint32(np.ceil(float_bounds[1]))
return np.c_[bounds_start, bounds_end].T
@property
def tile_shape(self):
""" The ranges of tiles needed for the request
Returns
--------
numpy.ndarray
The 3x1 difference of :meth:`tile_bounds` \
to give three numbers that count the number \
of tiles needed in all three directions.
"""
return np.diff(self.tile_bounds,axis=0)[0]
[docs] def some_in_all(self, t_origin, t_shape):
""" The target subvolume needed by a given tile
Arguments
----------
t_origin: numpy.ndarray
The 3x1 origin of the tile in the scaled \
target image
t_shape: numpy.ndarray
The 3x1 shape of the tile in the scaled \
target image
Returns
--------
numpy.ndarray
The 2x3 joining of ``t_origin`` and ``t_shape`` \
offset from :meth:`target_origin` and clipped by \
:meth:`target_shape` such that either row gives \
the upper or lower corner of the tile in the full \
scaled target volume.
"""
tile_bounds = t_origin + np.outer([0,1],t_shape)
some_in = tile_bounds - self.target_origin
return np.clip(some_in, 0, self.target_shape)
[docs] def all_in_some(self, t_index):
""" The tile region inside the scaled target volume
Arguments
----------
t_index: numpy.ndarray
The 3x1 tile offset of a tile from the origin
Returns
--------
numpy.ndarray
The 2x3 offset of :meth:`target_bounds` from \
the origin of ``tile_origin`` repetitions of \
:meth:`blocksize` such that either row gives \
an upper or lower corner of the total target \
volume within the requested ``t_index`` tile.
"""
tile_origin = self.blocksize * t_index
all_in = self.target_bounds - tile_origin
return np.clip(all_in, 0, self.blocksize)