Source code for pyctools.components.fft.tile

#!/usr/bin/env python
#  Pyctools - a picture processing algorithm development kit.
#  http://github.com/jim-easterbrook/pyctools
#  Copyright (C) 2014-20  Pyctools contributors
#
#  This program is free software: you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation, either version 3 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see
#  <http://www.gnu.org/licenses/>.

__all__ = ['Tile', 'UnTile']
__docformat__ = 'restructuredtext en'

import numpy

from pyctools.core.base import Transformer
from pyctools.core.config import ConfigInt


[docs] class Tile(Transformer): """Arrange image in overlapping tiles. This can be used with the :py:class:`~pyctools.components.fft.fft.FFT` component if you need FFTs of overlapping tiles, so you can reconstruct an image later on without visible tile edges. The ``xoff`` and ``yoff`` configuration sets the distance from the edge of one tile to the same edge on the next. For complete overlap they are usually set to half the tile width & height. These parameters are added to the output frame's metadata for use by :py:class:`UnTile`. ========= === ==== Config ========= === ==== ``xtile`` int Horizontal tile size. ``ytile`` int Vertical tile size. ``xoff`` int Horizontal tile offset. Typically set to xtile // 2. ``yoff`` int Vertical tile offset. Typically set to ytile // 2. ========= === ==== """ def initialise(self): self.config['xtile'] = ConfigInt(min_value=1) self.config['ytile'] = ConfigInt(min_value=1) self.config['xoff'] = ConfigInt(min_value=1) self.config['yoff'] = ConfigInt(min_value=1) def transform(self, in_frame, out_frame): self.update_config() x_tile = self.config['xtile'] y_tile = self.config['ytile'] x_off = self.config['xoff'] y_off = self.config['yoff'] audit = out_frame.metadata.get('audit') audit += 'data = Tile(data)\n' audit += ' size: %d x %d, offset: %d x %d\n' % ( y_tile, x_tile, y_off, x_off) out_frame.metadata.set('audit', audit) in_data = in_frame.as_numpy() y_len, x_len = in_data.shape[:2] tile_params = eval(out_frame.metadata.get('tile', '[]')) tile_params.append((y_tile, x_tile, y_off, x_off, y_len, x_len)) out_frame.metadata.set('tile', repr(tile_params)) if x_tile in (1, x_off): # no overlap, so nothing to do x_tile = x_len x_off = x_tile if y_tile in (1, y_off): # no overlap, so nothing to do y_tile = y_len y_off = y_tile x_mgn = (x_tile - 1) // x_off y_mgn = (y_tile - 1) // y_off x_blk = (x_len + x_off - 1) // x_off y_blk = (y_len + y_off - 1) // y_off pad_width = ((y_mgn * y_off, y_tile + ((y_blk - 1) * y_off) - y_len), (x_mgn * x_off, x_tile + ((x_blk - 1) * x_off) - x_len), (0, 0)) x_blk += x_mgn y_blk += y_mgn if numpy.any(pad_width): in_data = numpy.pad(in_data, pad_width) out_data = numpy.empty( [y_blk, y_tile, x_blk, x_tile] + list(in_data.shape[2:]), dtype=in_data.dtype) y = 0 for j in range(y_blk): x = 0 for i in range(x_blk): out_data[j, ::, i, ::] = in_data[y:y+y_tile, x:x+x_tile] x += x_off y += y_off out_frame.data = out_data.reshape((y_blk * y_tile, x_blk * x_tile, -1)) return True
[docs] class UnTile(Transformer): """Rearrange overlapping tiles to form an image. Inverse operation of the :py:class:`Tile` component. The tile size and offset parameters are read from the input image's metadata. """ def transform(self, in_frame, out_frame): in_data = in_frame.as_numpy() tile_params = eval(out_frame.metadata.get('tile', '[]')) if not tile_params: self.logger.error('Input has no "tile" metadata') return False y_tile, x_tile, y_off, x_off, y_len, x_len = tile_params.pop() out_frame.metadata.set('tile', repr(tile_params)) audit = out_frame.metadata.get('audit') audit += 'data = UnTile(data)\n' audit += ' size: %d x %d, offset: %d x %d\n' % ( y_tile, x_tile, y_off, x_off) out_frame.metadata.set('audit', audit) if x_tile in (1, x_off): # no overlap, so nothing to do x_tile = x_len x_off = x_tile if y_tile in (1, y_off): # no overlap, so nothing to do y_tile = y_len y_off = y_tile x_mgn = (x_tile - 1) // x_off y_mgn = (y_tile - 1) // y_off x_blk = in_data.shape[1] // x_tile y_blk = in_data.shape[0] // y_tile out_data = numpy.zeros([y_tile + ((y_blk - 1) * y_off), x_tile + ((x_blk - 1) * x_off)] + list(in_data.shape[2:]), dtype=in_data.dtype) in_data = in_data.reshape((y_blk, y_tile, x_blk, x_tile, -1)) y = 0 for j in range(y_blk): x = 0 for i in range(x_blk): out_data[y:y+y_tile, x:x+x_tile] += in_data[j, ::, i, ::] x += x_off y += y_off x = x_mgn * x_off y = y_mgn * y_off out_frame.data = out_data[y:y+y_len, x:x+x_len, ::].copy() return True