Source code for tiling.const_stride

# -*- coding:utf-8 -*-
import logging
import math

try:
    from collections.abc import Sequence
except ImportError:
    from collections import Sequence

from tiling import BaseTiles, ceil_int

logger = logging.getLogger("tiling")


[docs]class ConstStrideTiles(BaseTiles): """Class provides tile parameters (offset, extent) to extract data from image. Examples: .. code-block:: python from tiling import ConstStrideTiles tiles = ConstStrideTiles(image_size=(500, 500), tile_size=(256, 256), stride=(100, 100), origin=(-100, -100), scale=1.0, include_nodata=True) print("Number of tiles: %i" % len(tiles)) for extent, out_size in tiles: x, y, width, height = extent data = read_data(x, y, width, height, out_width=out_size[0], out_height=out_size[1]) print("data.shape: {}".format(data.shape)) Args: image_size (list/tuple of int): input image size in pixels (width, height) tile_size (int or list/tuple of int): output tile size in pixels (width, height) stride (list/tuple of int): horizontal and vertical strides in pixels. Values need to be positive larger than 1 pixel. Stride value is impacted with scale and corresponds to a sliding over scaled image. scale (float): Scaling applied to the input image parameters before extracting tile's extent origin (list or tuple of int): point in pixels in the original image from where to start the tiling. Values can be positive or negative. include_nodata (bool): Include or not nodata. If nodata is included then tile extents have all the same size, otherwise tiles at boundaries will be reduced """ def __init__( self, image_size, tile_size, stride=(1, 1), scale=1.0, origin=(0, 0), include_nodata=True, ): super(ConstStrideTiles, self).__init__(image_size=image_size, tile_size=tile_size, scale=scale) if not (isinstance(stride, int) or (isinstance(stride, Sequence) and len(stride) == 2)): raise TypeError("Argument stride should be (sx, sy)") if isinstance(stride, int): stride = (stride, stride) # Apply scale on the stride stride = [int(math.floor(s / self.scale)) for s in stride] for v in stride: if v < 1: raise ValueError("Scaled stride values `floor(stride / scale)` should be larger than 1 pixel") self.stride = stride self.origin = origin self.include_nodata = include_nodata self.nx = ConstStrideTiles._compute_number_of_tiles( self.image_size[0], self.tile_extent[0], self.origin[0], self.stride[0] ) self.ny = ConstStrideTiles._compute_number_of_tiles( self.image_size[1], self.tile_extent[1], self.origin[1], self.stride[1] ) self._max_index = self.nx * self.ny def __len__(self): """Method to get total number of tiles """ return self._max_index @staticmethod def _compute_tile_extent(idx, tile_extent, stride, origin, image_size, include_nodata): """Method to compute tile extent: offset, extent for a given index """ offset = idx * stride + origin if not include_nodata: extent = max(offset + tile_extent, 0) - max(offset, 0) extent = min(extent, image_size - offset) offset = max(offset, 0) else: extent = tile_extent return offset, extent @staticmethod def _compute_out_size(computed_extent, tile_extent, tile_size, scale): """Method to compute tile output size for a given computed extent. """ if computed_extent < tile_extent: return ceil_int(1.0 * computed_extent * scale) return tile_size def __getitem__(self, idx): """Method to get the tile at index `idx` Args: idx: (int) tile index between `0` and `len(tiles)` Returns: (tuple) tile extent, output size in pixels Tile extent in pixels: x offset, y offset, x tile extent, y tile extent. If scale is 1.0, then x tile extent, y tile extent are equal to tile size Output size in pixels: output width, height. If include_nodata is False and other parameters are such that tiles can go outside the image, then tile extent and output size are cropped at boundaries. Otherwise, output size is equal the input tile size. """ if idx < -self._max_index or idx >= self._max_index: raise IndexError("Index %i is out of ranges %i and %i" % (idx, 0, self._max_index)) idx = idx % self._max_index # Handle negative indexing as -1 is the last x_index = idx % self.nx y_index = int(idx * 1.0 / self.nx) x_offset, x_extent = self._compute_tile_extent( x_index, self.tile_extent[0], self.stride[0], self.origin[0], self.image_size[0], self.include_nodata, ) y_offset, y_extent = self._compute_tile_extent( y_index, self.tile_extent[1], self.stride[1], self.origin[1], self.image_size[1], self.include_nodata, ) x_out_size = ( self.tile_size[0] if self.include_nodata else self._compute_out_size(x_extent, self.tile_extent[0], self.tile_size[0], self.scale) ) y_out_size = ( self.tile_size[1] if self.include_nodata else self._compute_out_size(y_extent, self.tile_extent[1], self.tile_size[1], self.scale) ) return (x_offset, y_offset, x_extent, y_extent), (x_out_size, y_out_size) @staticmethod def _compute_number_of_tiles(image_size, tile_extent, origin, stride): """Method to compute number of overlapping tiles """ max_extent = max(tile_extent, stride) return max(ceil_int(1 + (image_size - max_extent - origin) * 1.0 / stride), 1)