Source code for pyctools.components.modulate

#!/usr/bin/env python
#  Pyctools - a picture processing algorithm development kit.
#  http://github.com/jim-easterbrook/pyctools
#  Copyright (C) 2014-19  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__ = ['Modulate']
__docformat__ = 'restructuredtext en'

import numpy

from pyctools.core.base import Transformer
from pyctools.core.types import pt_float

[docs] class Modulate(Transformer): """Modulate or sample an image. Multiplies each pixel value by a modulating value that can vary horizontally, vertically and temporally. The modulating function is supplied in a small "cell" whose dimensions should be the repeat period of the function in each direction. The ``cell`` input method is used to update the modulating function. No processing happens until a cell is received, and new cells can be applied while the component is running. The cell is supplied in a :py:class:`~pyctools.core.frame.Frame` object sent to the ``cell`` input. Unlike most other :py:class:`~pyctools.core.frame.Frame` objects the data must have 4 dimensions. If the first dimension is greater than unity then the modulation function can have a temporal variation. If the cell data's 4th dimension is unity then the same modulation is applied to each component of the input. Alternatively the cell data's 4th dimension should match the input's, allowing a different modulation to be applied to each colour. For example, a cell to simulate a `Bayer filter <http://en.wikipedia.org/wiki/Bayer_filter>`_ could look like this:: cell = Frame() cell.data = numpy.array([[[[0, 0, 1], [0, 1, 0]], [[0, 1, 0], [1, 0, 0]]]], dtype=numpy.float32) cell.type = 'cell' audit = cell.metadata.get('audit') audit += 'data = Bayer filter modulation cell\\n' cell.metadata.set('audit', audit) """ inputs = ['input', 'cell'] #: def initialise(self): self.cell_frame = None def get_cell(self, in_data): cell_frame = self.input_buffer['cell'].peek() if (cell_frame == self.cell_frame and self.cell_data.shape[1:] == in_data.shape): return True cell_data = cell_frame.as_numpy() if cell_data.ndim != 4: self.logger.error('Cell input must be 4 dimensional') self.input_buffer['cell'].get() return False if cell_data.shape[3] not in (1, in_data.shape[2]): self.logger.warning('Mismatch between %d cells and %d components', cell_data.shape[3], in_data.shape[2]) # repeat cell to frame dimensions if in_data.dtype == cell_data.dtype: dtype = cell_data.dtype else: dtype = pt_float repeated_cell = numpy.empty((cell_data.shape[0],) + in_data.shape, dtype) d_k, d_j, d_i, d_c = cell_data.shape for k in range(d_k): for j in range(d_j): for i in range(d_i): for c in range(d_c): repeated_cell[k, j::d_j, i::d_i, c::d_c] = cell_data[k, j, i, c] self.cell_frame = cell_frame self.cell_data = repeated_cell return True def transform(self, in_frame, out_frame): in_data = in_frame.as_numpy() if not self.get_cell(in_data): return False k = in_frame.frame_no % self.cell_data.shape[0] out_frame.data = in_data * self.cell_data[k] audit = out_frame.metadata.get('audit') audit += 'data = Modulate(data)\n' audit += ' cell: {\n' for line in self.cell_frame.metadata.get('audit').splitlines(): audit += ' ' + line + '\n' audit += ' }\n' out_frame.metadata.set('audit', audit) return True