def signal_unpack_data(data=None, domain=None):
        domain_upk, range_upk = None, None
        if isinstance(data, Signal):
            domain_upk = data.domain
            range_upk = data.range
        elif (issubclass(type(data), Sequence) or
              isinstance(data, (tuple, list, np.ndarray, Iterator))):
            data = tsplit(list(data) if isinstance(data, Iterator) else data)
            assert data.ndim in (1, 2), (
                'User "data" must be a 1d or 2d array-like variable!')
            if data.ndim == 1:
                # TODO: Swap for np.arange(0, data.size + 1)
                domain_upk, range_upk = np.linspace(0, 1, data.size), data
            else:
                domain_upk, range_upk = data
        elif (issubclass(type(data), Mapping) or
              isinstance(data, (dict, OrderedDict))):
            domain_upk, range_upk = tsplit(sorted(data.items()))
        elif is_pandas_installed():
            if isinstance(data, Series):
                domain_upk = data.index.values
                range_upk = data.values

        if domain is not None and range_upk is not None:
            assert len(domain) == len(range_upk), (
                'User "domain" is not compatible with unpacked range!')
            domain_upk = domain

        return domain_upk, range_upk
    def signal_unpack_data(data=None, domain=None):
        domain_upk, range_upk = None, None
        if isinstance(data, Signal):
            domain_upk = data.domain
            range_upk = data.range
        elif (issubclass(type(data), Sequence)
              or isinstance(data, (tuple, list, np.ndarray, Iterator))):
            data = tsplit(list(data) if isinstance(data, Iterator) else data)
            assert data.ndim in (1, 2), (
                'User "data" must be a 1d or 2d array-like variable!')
            if data.ndim == 1:
                # TODO: Swap for np.arange(0, data.size + 1)
                domain_upk, range_upk = np.linspace(0, 1, data.size), data
            else:
                domain_upk, range_upk = data
        elif (issubclass(type(data), Mapping)
              or isinstance(data, (dict, OrderedDict))):
            domain_upk, range_upk = tsplit(sorted(data.items()))
        elif is_pandas_installed():
            if isinstance(data, Series):
                domain_upk = data.index.values
                range_upk = data.values

        if domain is not None and range_upk is not None:
            assert len(domain) == len(range_upk), (
                'User "domain" is not compatible with unpacked range!')
            domain_upk = domain

        return domain_upk, range_upk
    def multi_signal_unpack_data(data=None, domain=None, labels=None):
        domain_upk, range_upk, signals = None, None, None
        signals = OrderedDict()
        if isinstance(data, MultiSignal):
            signals = data.signals
        elif (issubclass(type(data), Sequence)
              or isinstance(data, (tuple, list, np.ndarray, Iterator))):
            data = tsplit(list(data) if isinstance(data, Iterator) else data)
            assert data.ndim in (1, 2), (
                'User "data" must be a 1d or 2d array-like variable!')
            if data.ndim == 1:
                # TODO: Swap for np.arange(0, data.size + 1)
                signals[0] = Signal(data, np.linspace(0, 1, data.size))
            else:
                domain_upk, range_upk = ((data[0],
                                          data[1:]) if domain is None else
                                         (domain, data))
                for i, range_upk_c in enumerate(range_upk):
                    signals[i] = Signal(range_upk_c, domain_upk)
        elif (issubclass(type(data), Mapping)
              or isinstance(data, (dict, OrderedDict))):
            domain_upk, range_upk = tsplit(sorted(data.items()))
            for i, range_upk in enumerate(tsplit(range_upk)):
                signals[i] = Signal(range_upk, domain_upk)
        elif is_pandas_installed():
            if isinstance(data, Series):
                signals[0] = Signal(data)
            elif isinstance(data, DataFrame):
                # Check order consistency.
                domain_upk = data.index.values
                signals = OrderedDict(
                    ((label, Signal(data[label], domain_upk, name=label))
                     for label in data))

        if domain is not None and signals is not None:
            for signal in signals.values():
                assert len(domain) == len(signal.domain), (
                    'User "domain" is not compatible with unpacked signals!')
                signal.domain = domain

        if labels is not None and signals is not None:
            assert len(labels) == len(signals), (
                'User "labels" is not compatible with unpacked signals!')
            signals = OrderedDict([
                (labels[i], signal)
                for i, (_key, signal) in enumerate(signals.items())
            ])

        if len(signals) == 0:
            signals[0] = Signal()

        return signals
    def multi_signal_unpack_data(data=None, domain=None, labels=None):
        domain_upk, range_upk, signals = None, None, None
        signals = OrderedDict()
        if isinstance(data, MultiSignal):
            signals = data.signals
        elif (issubclass(type(data), Sequence) or
              isinstance(data, (tuple, list, np.ndarray, Iterator))):
            data = tsplit(list(data) if isinstance(data, Iterator) else data)
            assert data.ndim in (1, 2), (
                'User "data" must be a 1d or 2d array-like variable!')
            if data.ndim == 1:
                # TODO: Swap for np.arange(0, data.size + 1)
                signals[0] = Signal(data, np.linspace(0, 1, data.size))
            else:
                domain_upk, range_upk = ((data[0], data[1:])
                                         if domain is None else (domain, data))
                for i, range_upk_c in enumerate(range_upk):
                    signals[i] = Signal(range_upk_c, domain_upk)
        elif (issubclass(type(data), Mapping) or
              isinstance(data, (dict, OrderedDict))):
            domain_upk, range_upk = tsplit(sorted(data.items()))
            for i, range_upk in enumerate(tsplit(range_upk)):
                signals[i] = Signal(range_upk, domain_upk)
        elif is_pandas_installed():
            if isinstance(data, Series):
                signals[0] = Signal(data)
            elif isinstance(data, DataFrame):
                # Check order consistency.
                domain_upk = data.index.values
                signals = OrderedDict(((label, Signal(
                    data[label], domain_upk, name=label)) for label in data))

        if domain is not None and signals is not None:
            for signal in signals.values():
                assert len(domain) == len(signal.domain), (
                    'User "domain" is not compatible with unpacked signals!')
                signal.domain = domain

        if labels is not None and signals is not None:
            assert len(labels) == len(signals), (
                'User "labels" is not compatible with unpacked signals!')
            signals = OrderedDict(
                [(labels[i], signal)
                 for i, (_key, signal) in enumerate(signals.items())])

        if len(signals) == 0:
            signals[0] = Signal()

        return signals
Beispiel #5
0
    def __setattr__(self, attribute, value):
        """
        Reimplements the :meth:`MutableSequence.__getattr__` method.

        Parameters
        ----------
        attribute : unicode
            Attribute to set the value.
        value : object
            Value to set.
        """

        if hasattr(Image, attribute):
            if attribute == 'data':
                data = tsplit(value)
                for i, image in enumerate(self):
                    image.data = data[i]
            else:
                for i, image in enumerate(self):
                    setattr(image, attribute, value[i])
        elif hasattr(Metadata, attribute):
            for i, image in enumerate(self):
                setattr(image.metadata, attribute, value[i])
        else:
            super(ImageStack, self).__setattr__(attribute, value)
def spectral2XYZ_img_vectorized(cmfs, R):
    """
    
    Parameters
    ----------
    cmfs
    R:   np.ndarray (nb_pixels, 3) in [0., 1.]

    Returns
    -------

    """

    x_bar, y_bar, z_bar = colour.tsplit(cmfs)  # tested: OK. x_bar is the double one, the rightmost one (red). z_bar is the leftmost one (blue)
    plt.close('all')
    plt.plot(np.array([z_bar, y_bar, x_bar]).transpose())
    plt.savefig('cmf_cie1964_10.png')
    plt.close('all')
    # illuminant. We assume that the captured R is reflectance with illuminant E (although it really is not, it is reflected radiance with an unknown illuminant, but the result is the same)
    S = colour.ILLUMINANTS_RELATIVE_SPDS['E'].values[20:81:2] / 100.  # Equal-energy radiator (ones) sample_spectra_from_hsimg 300 to xxx with delta=5nm
    # print S

    # dw = cmfs.shape.interval
    dw = 10

    k = 100 / (np.sum(y_bar * S) * dw)

    X_p = R * x_bar * S * dw  # R(N,31) * x_bar(31,) * S(31,) * dw(1,)
    Y_p = R * y_bar * S * dw
    Z_p = R * z_bar * S * dw

    XYZ = k * np.sum(np.array([X_p, Y_p, Z_p]), axis=-1)
    XYZ = np.rollaxis(XYZ, 1, 0)  # th2tf() but for 2D input

    return XYZ
def get_cmfs(cmf_name='cie1964_10',
             nm_range=(400., 701.),
             nm_step=10,
             split=True):
    if cmf_name == 'cie1931_2':
        cmf_full_name = 'CIE 1931 2 Degree Standard Observer'
    elif cmf_name == 'cie2012_2':
        cmf_full_name = 'CIE 2012 2 Degree Standard Observer'
    elif cmf_name == 'cie2012_10':
        cmf_full_name = 'CIE 2012 10 Degree Standard Observer'
    elif cmf_name == 'cie1964_10':
        cmf_full_name = 'CIE 1964 10 Degree Standard Observer'
    else:
        raise AttributeError('Wrong cmf name')
    cmfs = colour.STANDARD_OBSERVERS_CMFS[cmf_full_name]

    # subsample and trim range
    ix_wl_first = np.where(cmfs.wavelengths == nm_range[0])[0][0]
    ix_wl_last = np.where(cmfs.wavelengths == nm_range[1] + 1.)[0][0]
    cmfs = cmfs.values[ix_wl_first:ix_wl_last:int(nm_step), :]

    if split:
        x_bar, y_bar, z_bar = colour.tsplit(cmfs)
        return x_bar, y_bar, z_bar
    else:
        return cmfs
def spectral2XYZ_img_vectorized(cmfs, R):
    x_bar, y_bar, z_bar = colour.tsplit(cmfs)  #
    plt.close('all')
    plt.plot(np.array([z_bar, y_bar, x_bar]).transpose())
    plt.savefig('cmf_cie1964_10.png')
    plt.close('all')
    # illuminant.
    S = colour.ILLUMINANTS_RELATIVE_SPDS['E'].values[0:31] / 100.
    dw = 10
    k = 100 / (np.sum(y_bar * S) * dw)

    X_p = R * x_bar * S * dw
    Y_p = R * y_bar * S * dw
    Z_p = R * z_bar * S * dw

    XYZ = k * np.sum(np.array([X_p, Y_p, Z_p]), axis=-1)
    XYZ = np.rollaxis(XYZ, 1, 0)
    return XYZ
def samples_Grossberg2003(image_stack, samples=1000, n=256):
    """
    Returns the samples for given image stack intensity histograms using
    Grossberg (2003) method.

    Parameters
    ----------
    image_stack : array_like
        Stack of single channel or multi-channel floating point images.
    samples : int, optional
        Samples count.
    n : int, optional
        Histograms bins count.

    Returns
    -------
    ndarray
        Intensity histograms samples.
    """

    image_stack = np.asarray(image_stack)

    if image_stack.ndim == 3:
        channels_c = 1
    else:
        channels_c = image_stack.shape[-2]

    cdf_i = []
    for image in tsplit(image_stack):
        histograms = tstack(
            [np.histogram(image[..., c], n, range=(0, 1))[0]
             for c in np.arange(channels_c)])
        cdf = np.cumsum(histograms, axis=0)
        cdf_i.append(cdf.astype(np.float_) / np.max(cdf, axis=0))

    samples_cdf_i = np.zeros((samples, len(cdf_i), channels_c))
    samples_u = np.linspace(0, 1, samples)
    for i in np.arange(samples):
        for j in np.arange(channels_c):
            for k, cdf in enumerate(cdf_i):
                samples_cdf_i[i, k, j] = np.argmin(np.abs(cdf[:, j] -
                                                          samples_u[i]))

    return samples_cdf_i
def mosaicing_CFA_Bayer(RGB, pattern='RGGB'):
    """
    Returns the *Bayer* CFA mosaic for a given *RGB* colourspace array.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    pattern : unicode, optional
        **{'RGGB', 'BGGR', 'GRBG', 'GBRG'}**,
        Arrangement of the colour filters on the pixel array.

    Returns
    -------
    ndarray
        *Bayer* CFA mosaic.

    Examples
    --------
    >>> RGB = np.array([[[0, 1, 2],
    ...                  [0, 1, 2]],
    ...                 [[0, 1, 2],
    ...                  [0, 1, 2]]])
    >>> mosaicing_CFA_Bayer(RGB)
    array([[0, 1],
           [1, 2]])
    >>> mosaicing_CFA_Bayer(RGB, pattern='BGGR')
    array([[2, 1],
           [1, 0]])
    """

    RGB = np.asarray(RGB)

    R, G, B = tsplit(RGB)
    R_m, G_m, B_m = masks_CFA_Bayer(RGB.shape[0:2], pattern)

    CFA = R * R_m + G * G_m + B * B_m

    return CFA
def get_cmfs(cmf_name='cie1964_10', nm_range=(400., 700.), nm_step=10, split=True):

    if cmf_name == 'cie1931_2':
        cmf_full_name = 'CIE 1931 2 Degree Standard Observer'
    elif cmf_name == 'cie1931_10':
        cmf_full_name = 'CIE 1931 10 Degree Standard Observer'
    elif cmf_name == 'cie1964_2':
        cmf_full_name = 'CIE 1964 2 Degree Standard Observer'
    elif cmf_name == 'cie1964_10':
        cmf_full_name = 'CIE 1964 10 Degree Standard Observer'
    else:
        raise AttributeError('Wrong cmf name')
    cmfs = colour.STANDARD_OBSERVERS_CMFS[cmf_full_name]

    # subsample and trim range
    ix_wl_first = np.where(cmfs.wavelengths == nm_range[0])[0][0]
    ix_wl_last = np.where(cmfs.wavelengths == nm_range[1] + 1.)[0][0]
    cmfs = cmfs.values[ix_wl_first:ix_wl_last:int(nm_step), :]  # make sure the nm_step is an int

    if split:
        x_bar, y_bar, z_bar = colour.tsplit(cmfs)  #tested: OK. x_bar is the double one, the rightmost one (red). z_bar is the leftmost one (blue)
        return x_bar, y_bar, z_bar
    else:
        return cmfs
Beispiel #12
0
def lut_interpolation(im, im_type, lut_file, lut_size, interp_type):

    inPixels = read_image(im)
    # print(np.amax(inPixels))
    max_cv = np.amax(inPixels)

    lut = np.loadtxt(lut_file, skiprows=7)

    lattice = np.reshape(lut, (lut_size, lut_size, lut_size, 3), order='F')

    if interp_type == 'trilinear':
        n = lattice.shape[0] - 1
        inPixels = np.asarray(inPixels) / np.amax(inPixels)
        theShape = inPixels.shape
        inPixels = np.ravel(inPixels)
        pixels = inPixels.size / 3
        inPixels = np.reshape(inPixels, (pixels, 3))
        R, G, B = tsplit(inPixels)
        rLow = np.floor(R * n).astype(np.int_)
        rHigh = np.clip(rLow + 1, 0, n)
        gLow = np.floor(G * n).astype(np.int_)
        gHigh = np.clip(gLow + 1, 0, n)
        bLow = np.floor(B * n).astype(np.int_)
        bHigh = np.clip(bLow + 1, 0, n)
        V000 = lattice[rLow, gLow, bLow]
        V001 = lattice[rLow, gLow, bHigh]
        V010 = lattice[rLow, gHigh, bLow]
        V011 = lattice[rLow, gHigh, bHigh]
        V100 = lattice[rHigh, gLow, bLow]
        V101 = lattice[rHigh, gLow, bHigh]
        V110 = lattice[rHigh, gHigh, bLow]
        V111 = lattice[rHigh, gHigh, bHigh]
        fR = n * R - rLow
        fG = n * G - gLow
        fB = n * B - bLow
        fR = np.reshape(fR, (pixels, 1))
        fG = np.reshape(fG, (pixels, 1))
        fB = np.reshape(fB, (pixels, 1))
        fR = np.tile(fR, 3)
        fG = np.tile(fG, 3)
        fB = np.tile(fB, 3)
        W000 = (1 - fR) * (1 - fG) * (1 - fB)
        W001 = (1 - fR) * (1 - fG) * fB
        W010 = (1 - fR) * fG * (1 - fB)
        W011 = (1 - fR) * fG * fB
        W100 = fR * (1 - fG) * (1 - fB)
        W101 = fR * (1 - fG) * fB
        W110 = fR * fG * (1 - fB)
        W111 = fR * fG * fB
        outPixels = V000 * W000 + V001 * W001 + V010 * W010 + V011 * W011 + V100 * W100 + V101 * W101 + V110 * W110 + V111 * W111
        outPixels = np.reshape(outPixels, theShape) * 255 * max_cv

    if interp_type == 'tetrahedral':

        n = lattice.shape[0] - 1
        inPixels = np.asarray(inPixels) / max_cv
        theShape = inPixels.shape
        inPixels = np.ravel(inPixels)
        pixels = inPixels.size / 3
        inPixels = np.reshape(inPixels, (pixels, 3))
        R, G, B = tsplit(inPixels)
        rLow = np.floor(R * n).astype(np.int_)
        rHigh = np.clip(rLow + 1, 0, n)
        gLow = np.floor(G * n).astype(np.int_)
        gHigh = np.clip(gLow + 1, 0, n)
        bLow = np.floor(B * n).astype(np.int_)
        bHigh = np.clip(bLow + 1, 0, n)
        V000 = lattice[rLow, gLow, bLow]
        V001 = lattice[rLow, gLow, bHigh]
        V010 = lattice[rLow, gHigh, bLow]
        V011 = lattice[rLow, gHigh, bHigh]
        V100 = lattice[rHigh, gLow, bLow]
        V101 = lattice[rHigh, gLow, bHigh]
        V110 = lattice[rHigh, gHigh, bLow]
        V111 = lattice[rHigh, gHigh, bHigh]
        fR = n * R - rLow
        fG = n * G - gLow
        fB = n * B - bLow
        fR = np.reshape(fR, (pixels, 1))
        fG = np.reshape(fG, (pixels, 1))
        fB = np.reshape(fB, (pixels, 1))

        outPixels = (1 - fG) * V000 + (fG - fR) * V010 + (
            fR - fB) * V110 + fB * V111
        outPixels = np.where(np.logical_and(fR > fG, fG > fB),
                             (1 - fR) * V000 + (fR - fG) * V100 +
                             (fG - fB) * V110 + fB * V111, outPixels)
        outPixels = np.where(np.logical_and(fR > fG, fR > fB),
                             (1 - fR) * V000 + (fR - fB) * V100 +
                             (fB - fG) * V101 + fG * V111, outPixels)
        outPixels = np.where(np.logical_and(fR > fG, fB >= fR),
                             (1 - fB) * V000 + (fB - fR) * V001 +
                             (fR - fG) * V101 + fG * V111, outPixels)
        outPixels = np.where(np.logical_and(fG >= fR, fB > fG),
                             (1 - fB) * V000 + (fB - fG) * V001 +
                             (fG - fR) * V011 + fR * V111, outPixels)
        outPixels = np.where(np.logical_and(fG >= fR, fB > fR),
                             (1 - fG) * V000 + (fG - fB) * V010 +
                             (fB - fR) * V011 + fR * V111, outPixels)
        outPixels = np.clip(outPixels, 0., np.inf)

        outPixels = np.reshape(outPixels, theShape) * 255 * max_cv

# return outPixels

    # print(outPixels)
    cv2.imwrite('test_tetrahedral.tiff', outPixels.astype(np.uint8))

    return outPixels
Beispiel #13
0
def image_stack_to_radiance_image(
        image_stack,
        weighting_function=weighting_function_Debevec1997,
        weighting_average=False,
        camera_response_functions=None):
    """
    Generates a HDRI / radiance image from given image stack.

    Parameters
    ----------
    image_stack : ImageStack
        Stack of single channel or multi-channel floating point images. The
        stack is assumed to be representing linear values except if
        ``camera_response_functions`` argument is provided.
    weighting_function : callable, optional
        Weighting function :math:`w`.
    weighting_average : bool, optional
         Enables weighting function :math:`w` computation on channels average
         instead of on a per channel basis.
    camera_response_functions : array_like, optional
        Camera response functions :math:`g(z)` of the imaging system / camera
        if the stack is representing non linear values.

    Returns
    -------
    ndarray
        Radiance image.
    """

    image_c = None
    weight_c = None
    for image in image_stack:
        if image_c is None:
            image_c = np.zeros(image.data.shape)
            weight_c = np.zeros(image.data.shape)

        L = average_luminance(
            image.metadata.f_number,
            image.metadata.exposure_time,
            image.metadata.iso)

        if weighting_average and image.data.ndim == 3:
            weight = weighting_function(np.average(image.data, axis=-1))
            weight = np.rollaxis(weight[np.newaxis], 0, 3)
        else:
            weight = weighting_function(image.data)

        image_data = image.data
        if camera_response_functions is not None:
            samples = np.linspace(0, 1, camera_response_functions.shape[0])

            R, G, B = tsplit(image.data)
            R = np.interp(R, samples, camera_response_functions[..., 0])
            G = np.interp(G, samples, camera_response_functions[..., 1])
            B = np.interp(B, samples, camera_response_functions[..., 2])
            image_data = tstack((R, G, B))

        image_c += weight * image_data / L
        weight_c += weight

    if image_c is not None:
        image_c /= weight_c
        image_c[np.isnan(image_c)] = 0

    return image_c
def refining_step_Menon2007(RGB, RGB_m, M):
    """
    Performs the refining step on given *RGB* colourspace array.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    RGB_m : array_like
        *Bayer* CFA red, green and blue masks.
    M : array_like
        Estimation for the best directional reconstruction.

    Returns
    -------
    ndarray
        Refined *RGB* colourspace array.

    Examples
    --------
    >>> RGB = np.array([[[0.30588236, 0.35686275, 0.3764706],
    ...                  [0.30980393, 0.36078432, 0.39411766],
    ...                  [0.29607844, 0.36078432, 0.40784314],
    ...                  [0.29803923, 0.37647060, 0.42352942]],
    ...                 [[0.30588236, 0.35686275, 0.3764706],
    ...                  [0.30980393, 0.36078432, 0.39411766],
    ...                  [0.29607844, 0.36078432, 0.40784314],
    ...                  [0.29803923, 0.37647060, 0.42352942]]])
    >>> RGB_m = np.array([[[0, 0, 1],
    ...                    [0, 1, 0],
    ...                    [0, 0, 1],
    ...                    [0, 1, 0]],
    ...                   [[0, 1, 0],
    ...                    [1, 0, 0],
    ...                    [0, 1, 0],
    ...                    [1, 0, 0]]])
    >>> M = np.array([[0, 1, 0, 1],
    ...               [1, 0, 1, 0]])
    >>> refining_step_Menon2007(RGB, RGB_m, M)
    array([[[ 0.30588236,  0.35686275,  0.3764706 ],
            [ 0.30980393,  0.36078432,  0.39411765],
            [ 0.29607844,  0.36078432,  0.40784314],
            [ 0.29803923,  0.3764706 ,  0.42352942]],
    <BLANKLINE>
           [[ 0.30588236,  0.35686275,  0.3764706 ],
            [ 0.30980393,  0.36078432,  0.39411766],
            [ 0.29607844,  0.36078432,  0.40784314],
            [ 0.29803923,  0.3764706 ,  0.42352942]]])
    """

    R, G, B = tsplit(RGB)
    R_m, G_m, B_m = tsplit(RGB_m)
    M = np.asarray(M)

    # Updating of the green component.
    R_G = R - G
    B_G = B - G

    FIR = np.ones(3) / 3

    B_G_m = np.where(B_m == 1,
                     np.where(M == 1, _cnv_h(B_G, FIR), _cnv_v(B_G, FIR)), 0)
    R_G_m = np.where(R_m == 1,
                     np.where(M == 1, _cnv_h(R_G, FIR), _cnv_v(R_G, FIR)), 0)

    G = np.where(R_m == 1, R - R_G_m, G)
    G = np.where(B_m == 1, B - B_G_m, G)

    # Updating of the red and blue components in the green locations.
    # Red rows.
    R_r = np.transpose(np.any(R_m == 1, axis=1)[np.newaxis]) * np.ones(R.shape)
    # Red columns.
    R_c = np.any(R_m == 1, axis=0)[np.newaxis] * np.ones(R.shape)
    # Blue rows.
    B_r = np.transpose(np.any(B_m == 1, axis=1)[np.newaxis]) * np.ones(B.shape)
    # Blue columns
    B_c = np.any(B_m == 1, axis=0)[np.newaxis] * np.ones(B.shape)

    R_G = R - G
    B_G = B - G

    k_b = np.array([0.5, 0, 0.5])

    R_G_m = np.where(np.logical_and(G_m == 1, B_r == 1),
                     _cnv_v(R_G, k_b),
                     R_G_m)
    R = np.where(np.logical_and(G_m == 1, B_r == 1), G + R_G_m, R)
    R_G_m = np.where(np.logical_and(G_m == 1, B_c == 1),
                     _cnv_h(R_G, k_b),
                     R_G_m)
    R = np.where(np.logical_and(G_m == 1, B_c == 1), G + R_G_m, R)

    B_G_m = np.where(np.logical_and(G_m == 1, R_r == 1),
                     _cnv_v(B_G, k_b),
                     B_G_m)
    B = np.where(np.logical_and(G_m == 1, R_r == 1), G + B_G_m, B)
    B_G_m = np.where(np.logical_and(G_m == 1, R_c == 1),
                     _cnv_h(B_G, k_b),
                     B_G_m)
    B = np.where(np.logical_and(G_m == 1, R_c == 1), G + B_G_m, B)

    # Updating of the red (blue) component in the blue (red) locations.
    R_B = R - B
    R_B_m = np.where(B_m == 1,
                     np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)), 0)
    R = np.where(B_m == 1, B + R_B_m, R)

    R_B_m = np.where(R_m == 1,
                     np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)), 0)
    B = np.where(R_m == 1, R - R_B_m, B)

    return tstack((R, G, B))
def refining_step_Menon2007(RGB, RGB_m, M):
    """
    Performs the refining step on given *RGB* colourspace array.

    Parameters
    ----------
    RGB : array_like
        *RGB* colourspace array.
    RGB_m : array_like
        *Bayer* CFA red, green and blue masks.
    M : array_like
        Estimation for the best directional reconstruction.

    Returns
    -------
    ndarray
        Refined *RGB* colourspace array.

    Examples
    --------
    >>> RGB = np.array([[[0.30588236, 0.35686275, 0.3764706],
    ...                  [0.30980393, 0.36078432, 0.39411766],
    ...                  [0.29607844, 0.36078432, 0.40784314],
    ...                  [0.29803923, 0.37647060, 0.42352942]],
    ...                 [[0.30588236, 0.35686275, 0.3764706],
    ...                  [0.30980393, 0.36078432, 0.39411766],
    ...                  [0.29607844, 0.36078432, 0.40784314],
    ...                  [0.29803923, 0.37647060, 0.42352942]]])
    >>> RGB_m = np.array([[[0, 0, 1],
    ...                    [0, 1, 0],
    ...                    [0, 0, 1],
    ...                    [0, 1, 0]],
    ...                   [[0, 1, 0],
    ...                    [1, 0, 0],
    ...                    [0, 1, 0],
    ...                    [1, 0, 0]]])
    >>> M = np.array([[0, 1, 0, 1],
    ...               [1, 0, 1, 0]])
    >>> refining_step_Menon2007(RGB, RGB_m, M)
    array([[[ 0.30588236,  0.35686275,  0.3764706 ],
            [ 0.30980393,  0.36078432,  0.39411765],
            [ 0.29607844,  0.36078432,  0.40784314],
            [ 0.29803923,  0.3764706 ,  0.42352942]],
    <BLANKLINE>
           [[ 0.30588236,  0.35686275,  0.3764706 ],
            [ 0.30980393,  0.36078432,  0.39411766],
            [ 0.29607844,  0.36078432,  0.40784314],
            [ 0.29803923,  0.3764706 ,  0.42352942]]])
    """

    R, G, B = tsplit(RGB)
    R_m, G_m, B_m = tsplit(RGB_m)
    M = np.asarray(M)

    # Updating of the green component.
    R_G = R - G
    B_G = B - G

    FIR = np.ones(3) / 3

    B_G_m = np.where(B_m == 1,
                     np.where(M == 1, _cnv_h(B_G, FIR), _cnv_v(B_G, FIR)), 0)
    R_G_m = np.where(R_m == 1,
                     np.where(M == 1, _cnv_h(R_G, FIR), _cnv_v(R_G, FIR)), 0)

    G = np.where(R_m == 1, R - R_G_m, G)
    G = np.where(B_m == 1, B - B_G_m, G)

    # Updating of the red and blue components in the green locations.
    # Red rows.
    R_r = np.transpose(np.any(R_m == 1, axis=1)[np.newaxis]) * np.ones(R.shape)
    # Red columns.
    R_c = np.any(R_m == 1, axis=0)[np.newaxis] * np.ones(R.shape)
    # Blue rows.
    B_r = np.transpose(np.any(B_m == 1, axis=1)[np.newaxis]) * np.ones(B.shape)
    # Blue columns
    B_c = np.any(B_m == 1, axis=0)[np.newaxis] * np.ones(B.shape)

    R_G = R - G
    B_G = B - G

    k_b = np.array([0.5, 0, 0.5])

    R_G_m = np.where(np.logical_and(G_m == 1, B_r == 1), _cnv_v(R_G, k_b),
                     R_G_m)
    R = np.where(np.logical_and(G_m == 1, B_r == 1), G + R_G_m, R)
    R_G_m = np.where(np.logical_and(G_m == 1, B_c == 1), _cnv_h(R_G, k_b),
                     R_G_m)
    R = np.where(np.logical_and(G_m == 1, B_c == 1), G + R_G_m, R)

    B_G_m = np.where(np.logical_and(G_m == 1, R_r == 1), _cnv_v(B_G, k_b),
                     B_G_m)
    B = np.where(np.logical_and(G_m == 1, R_r == 1), G + B_G_m, B)
    B_G_m = np.where(np.logical_and(G_m == 1, R_c == 1), _cnv_h(B_G, k_b),
                     B_G_m)
    B = np.where(np.logical_and(G_m == 1, R_c == 1), G + B_G_m, B)

    # Updating of the red (blue) component in the blue (red) locations.
    R_B = R - B
    R_B_m = np.where(B_m == 1,
                     np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)), 0)
    R = np.where(B_m == 1, B + R_B_m, R)

    R_B_m = np.where(R_m == 1,
                     np.where(M == 1, _cnv_h(R_B, FIR), _cnv_v(R_B, FIR)), 0)
    B = np.where(R_m == 1, R - R_B_m, B)

    return tstack((R, G, B))