def test_rgb_to_od(self):

        np.testing.assert_array_almost_equal(
            np.round(rgb_to_od(np.zeros((3, 3, 3)) + 117.0), 4),
            np.zeros((3, 3, 3)) + 35.6158)

        # check corner cases
        np.testing.assert_array_almost_equal(
            rgb_to_od(np.zeros((3, 3, 3)) + 255.0), np.zeros((3, 3, 3)))

        np.testing.assert_array_almost_equal(rgb_to_od(np.zeros((3, 3, 3))),
                                             np.zeros((3, 3, 3)) + 255.0)
    def test_rgb_to_od_to_rgb(self):

        np.random.seed(1)

        im_rand = np.random.randint(0, 255, (10, 10, 3))

        np.testing.assert_array_almost_equal(od_to_rgb(rgb_to_od(im_rand)),
                                             im_rand)
    def test_rgb_to_od(self):

        np.testing.assert_array_almost_equal(
            np.round(rgb_to_od(np.zeros((3, 3, 3)) + 117.0), 4),
            np.zeros((3, 3, 3)) + 35.6158
        )

        # check corner cases
        np.testing.assert_array_almost_equal(
            rgb_to_od(np.zeros((3, 3, 3)) + 255.0),
            np.zeros((3, 3, 3))
        )

        np.testing.assert_array_almost_equal(
            rgb_to_od(np.zeros((3, 3, 3))),
            np.zeros((3, 3, 3)) + 255.0
        )
    def test_rgb_to_od_to_rgb(self):

        np.random.seed(1)

        im_rand = np.random.randint(0, 255, (10, 10, 3))

        np.testing.assert_array_almost_equal(
            od_to_rgb(rgb_to_od(im_rand)),
            im_rand
        )
示例#5
0
def color_convolution(im_stains, w):
    """Performs Color Convolution
    Reconstructs a color image from the stain matrix `w` and
    the individual images stored as channels in `im_stains` and generated
    by ColorDeconvolution.

    Parameters
    ----------
    im_stains : array_like
        An RGB image where in each channel contains image of one stain
    w : array_like
        A 3x3 matrix containing the stain colors in its columns.
        In the case of two stains, the third column is zero and will be
        complemented using cross-product. The matrix should contain a
        minumum two nonzero columns.

    Returns
    -------
    im_rgb : array_like
        Reconstructed RGB image with intensity values ranging from [0, 255],
        suitable for display.

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.complement_stain_matrix,
    histomicstk.preprocessing.color_deconvolution.color_deconvolution
    histomicstk.preprocessing.color_conversion.rgb_to_od
    histomicstk.preprocessing.color_conversion.od_to_rgb
    """

    # transform 3D input stain image to 2D stain matrix format
    m = im_stains.shape[0]
    n = im_stains.shape[1]
    im_stains = np.reshape(im_stains, (m * n, 3))

    # transform input stains to optical density values, convolve and
    # tfm back to stain
    im_stains = im_stains.astype(dtype=np.float32)
    ODfwd = color_conversion.rgb_to_od(im_stains)
    ODdeconv = np.dot(ODfwd, np.transpose(w))
    ODinv = color_conversion.od_to_rgb(ODdeconv)

    # reshape output, transform type
    im_rgb = np.reshape(ODinv, (m, n, 3))
    im_rgb[im_rgb > 255] = 255
    im_rgb = im_rgb.astype(np.uint8)

    return im_rgb
def ColorConvolution(I, W):
    """Performs Color Convolution
    Reconstructs a color image from the stain matrix `W` and
    the individual images stored as channels in `I` and generated
    by ColorDeconvolution.

    Parameters
    ----------
    I : array_like
        An RGB image where in each channel contains image of one stain
    W : array_like
        A 3x3 matrix containing the stain colors in its columns.
        In the case of two stains, the third column is zero and will be
        complemented using cross-product. The matrix should contain a
        minumum two nonzero columns.

    Returns
    -------
    IOut : array_like
        Reconstructed RGB image with intensity values ranging from [0, 255],
        suitable for display.

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.ComplementStainMatrix,
    histomicstk.preprocessing.color_deconvolution.ColorDeconvolution
    histomicstk.preprocessing.color_conversion.rgb_to_od
    histomicstk.preprocessing.color_conversion.od_to_rgb
    """

    # transform 3D input stain image to 2D stain matrix format
    m = I.shape[0]
    n = I.shape[1]
    I = np.reshape(I, (m * n, 3))

    # transform input stains to optical density values, convolve and
    # tfm back to stain
    I = I.astype(dtype=np.float32)
    ODfwd = color_conversion.rgb_to_od(I)
    ODdeconv = np.dot(ODfwd, np.transpose(W))
    ODinv = color_conversion.od_to_rgb(ODdeconv)

    # reshape output, transform type
    IOut = np.reshape(ODinv, (m, n, 3))
    IOut[IOut > 255] = 255
    IOut = IOut.astype(np.uint8)

    return IOut
def color_deconvolution(im_rgb, w):
    """Performs color deconvolution.
    The given RGB Image `I` is first first transformed into optical density
    space, and then projected onto the stain vectors in the columns of the
    3x3 stain matrix `W`.

    For deconvolving H&E stained image use:

    `w` = array([[0.650, 0.072, 0], [0.704, 0.990, 0], [0.286, 0.105, 0]])

    Parameters
    ----------
    im_rgb : array_like
        Input RGB Image that needs to be deconvolved.
    w : array_like
        A 3x3 matrix containing the color vectors in columns.
        For two stain images the third column is zero and will be
        complemented using cross-product. Atleast two of the three
        columns must be non-zero.

    Returns
    -------
    Stains : array_like
        An rgb image where in each channel contains the image of the
        stain of the corresponding column in the stain matrix `W`.
        The intensity range of each channel is [0, 255] suitable for
        displaying.
    StainsFloat : array_like
        An intensity image of deconvolved stains that is unbounded,
        suitable for reconstructing color images of deconvolved stains
        with color_convolution.
    wc : array_like
        A 3x3 complemented stain matrix. Useful for color image
        reconstruction with color_convolution.

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.complement_stain_matrix,
    histomicstk.preprocessing.color_deconvolution.color_convolution
    histomicstk.preprocessing.color_conversion.rgb_to_od
    histomicstk.preprocessing.color_conversion.od_to_rgb
    """

    # complement stain matrix if needed
    if numpy.linalg.norm(w[:, 2]) <= 1e-16:
        wc = complement_stain_matrix(w)
    else:
        wc = w.copy()

    # normalize stains to unit-norm
    for i in range(wc.shape[1]):
        Norm = numpy.linalg.norm(wc[:, i])
        if Norm >= 1e-16:
            wc[:, i] /= Norm

    # invert stain matrix
    Q = numpy.linalg.inv(wc)

    # transform 3D input image to 2D RGB matrix format
    m = im_rgb.shape[0]
    n = im_rgb.shape[1]
    if im_rgb.shape[2] == 4:
        im_rgb = im_rgb[:, :, (0, 1, 2)]
    im_rgb = numpy.reshape(im_rgb, (m * n, 3))

    # transform input RGB to optical density values and deconvolve,
    # tfm back to RGB
    im_rgb = im_rgb.astype(dtype=numpy.float32)
    im_rgb[im_rgb == 0] = 1e-16
    ODfwd = color_conversion.rgb_to_od(im_rgb)
    ODdeconv = numpy.dot(ODfwd, numpy.transpose(Q))
    ODinv = color_conversion.od_to_rgb(ODdeconv)

    # reshape output
    StainsFloat = numpy.reshape(ODinv, (m, n, 3))

    # transform type
    Stains = numpy.copy(StainsFloat)
    Stains[Stains > 255] = 255
    Stains = Stains.astype(numpy.uint8)

    # return
    Unmixed = collections.namedtuple('Unmixed',
                                     ['Stains', 'StainsFloat', 'Wc'])
    Output = Unmixed(Stains, StainsFloat, wc)

    return Output
def ColorDeconvolution(I, W):
    """Performs color deconvolution.
    The given RGB Image `I` is first first transformed into optical density
    space, and then projected onto the stain vectors in the columns of the
    3x3 stain matrix `W`.

    For deconvolving H&E stained image use:

    `W` = array([[0.650, 0.072, 0], [0.704, 0.990, 0], [0.286, 0.105, 0]])

    Parameters
    ----------
    I : array_like
        Input RGB Image that needs to be deconvolved.
    W : array_like
        A 3x3 matrix containing the color vectors in columns.
        For two stain images the third column is zero and will be
        complemented using cross-product. Atleast two of the three
        columns must be non-zero.

    Returns
    -------
    Stains : array_like
        An rgb image where in each channel contains the image of the
        stain of the corresponding column in the stain matrix `W`.
        The intensity range of each channel is [0, 255] suitable for
        displaying.
    StainsFloat : array_like
        An intensity image of deconvolved stains that is unbounded,
        suitable for reconstructing color images of deconvolved stains
        with ColorConvolution.
    Wc : array_like
        A 3x3 complemented stain matrix. Useful for color image
        reconstruction with ColorConvolution.

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.ComplementStainMatrix,
    histomicstk.preprocessing.color_deconvolution.ColorConvolution
    histomicstk.preprocessing.color_conversion.rgb_to_od
    histomicstk.preprocessing.color_conversion.od_to_rgb
    """

    # complement stain matrix if needed
    if numpy.linalg.norm(W[:, 2]) <= 1e-16:
        Wc = ComplementStainMatrix(W)
    else:
        Wc = W.copy()

    # normalize stains to unit-norm
    for i in range(Wc.shape[1]):
        Norm = numpy.linalg.norm(Wc[:, i])
        if Norm >= 1e-16:
            Wc[:, i] /= Norm

    # invert stain matrix
    Q = numpy.linalg.inv(Wc)

    # transform 3D input image to 2D RGB matrix format
    m = I.shape[0]
    n = I.shape[1]
    if I.shape[2] == 4:
        I = I[:, :, (0, 1, 2)]
    I = numpy.reshape(I, (m * n, 3))

    # transform input RGB to optical density values and deconvolve,
    # tfm back to RGB
    I = I.astype(dtype=numpy.float32)
    I[I == 0] = 1e-16
    ODfwd = color_conversion.rgb_to_od(I)
    ODdeconv = numpy.dot(ODfwd, numpy.transpose(Q))
    ODinv = color_conversion.od_to_rgb(ODdeconv)

    # reshape output
    StainsFloat = numpy.reshape(ODinv, (m, n, 3))

    # transform type
    Stains = numpy.copy(StainsFloat)
    Stains[Stains > 255] = 255
    Stains = Stains.astype(numpy.uint8)

    # return
    Unmixed = collections.namedtuple('Unmixed',
                                     ['Stains', 'StainsFloat', 'Wc'])
    Output = Unmixed(Stains, StainsFloat, Wc)

    return Output
def sparse_color_deconvolution(im_rgb, w_init, beta):
    """Performs adaptive color deconvolution.

    Uses sparse non-negative matrix factorization to adaptively deconvolve a
    given RGB image into intensity images representing distinct stains.
    Similar approach to ``color_deconvolution`` but operates adaptively.
    The input RGB image `im_rgb` consisting of RGB values is first transformed
    into optical density space as a row-matrix, and then is decomposed as
    :math:`V = W H` where :math:`W` is a 3xk matrix containing stain vectors
    in columns and :math:`H` is a k x m*n matrix of concentrations for each
    stain vector. The system is solved to encourage sparsity of the columns
    of :math"`H` i.e. most pixels should not contain significant contributions
    from more than one stain. Can use a hot-start initialization from a color
    deconvolution matrix.

    Parameters
    ----------
    im_rgb : array_like
        An RGB image of type unsigned char, or a 3xN matrix of RGB pixel
        values.
    w_init : array_like
        A 3xK matrix containing the color vectors in columns. Should not be
        complemented with ComplementStainMatrix for sparse decomposition to
        work correctly.
    beta : double
        Regularization factor for sparsity of :math:`H` - recommended 0.5.

    Returns
    -------
    stains : array_like
        An rgb image with deconvolved stain intensities in each channel,
        values ranging from [0, 255], suitable for display.
    w : array_like
        The final 3 x k stain matrix produced by NMF decomposition.

    Notes
    -----
    Return values are returned as a namedtuple

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.ColorDeconvolution

    References
    ----------
    .. [1] J. Xu, L. Xiang, G. Wang, S. Ganesan, M. Feldman, N.N. Shih,
           H. Gilmore, A. Madabhushi, "Sparse Non-negative Matrix
           Factorization (SNMF) based color unmixing for breast
           histopathological image analysis," in IEEE Computer Graphics
           and Applications, vol.46,no.1,pp.20-9, 2015.
    """

    # determine if input is RGB or pixel-matrix format
    if len(im_rgb.shape) == 3:  # RBG image provided
        m = im_rgb.shape[0]
        n = im_rgb.shape[1]
        im_rgb = np.reshape(im_rgb, (m * n, 3)).transpose()
    elif len(im_rgb.shape) == 2:  # pixel matrix provided
        m = -1
        n = -1
        if im_rgb.shape[2] == 4:  # remove alpha channel if needed
            im_rgb = im_rgb[:, :, (0, 1, 2)]

    # transform input RGB to optical density values
    im_rgb = im_rgb.astype(dtype=np.float32)
    im_rgb[im_rgb == 0] = 1e-16
    ODfwd = color_conversion.rgb_to_od(im_rgb)

    if w_init is None:

        # set number of output stains
        K = 3

        # perform NMF without initialization
        Factorization = nimfa.Snmf(V=ODfwd,
                                   seed=None,
                                   rank=K,
                                   version='r',
                                   beta=beta)
        Factorization()

    else:

        # get number of output stains
        K = w_init.shape[1]

        # normalize stains to unit-norm
        for i in range(K):
            Norm = np.linalg.norm(w_init[:, i])
            if (Norm >= 1e-16):
                w_init[:, i] /= Norm
            else:
                print 'error'  # throw error

        # estimate initial H given p
        Hinit = np.dot(np.linalg.pinv(w_init), ODfwd)
        Hinit[Hinit < 0] = 0

        # perform regularized NMF
        Factorization = nimfa.Snmf(V=ODfwd,
                                   seed=None,
                                   W=w_init,
                                   H=Hinit,
                                   rank=K,
                                   version='r',
                                   beta=beta)
        Factorization()

    # extract solutions and make columns of "w" unit-norm
    w = np.asarray(Factorization.basis())
    H = np.asarray(Factorization.coef())
    for i in range(K):
        Norm = np.linalg.norm(w[:, i])
        w[:, i] /= Norm
        H[i, :] *= Norm

    # reshape H matrix to image
    if m == -1:
        stains_float = np.transpose(H)
    else:
        stains_float = np.reshape(np.transpose(H), (m, n, K))

    # transform type
    stains = np.copy(stains_float)
    stains[stains > 255] = 255
    stains = stains.astype(np.uint8)

    # build named tuple for outputs
    Unmixed = collections.namedtuple('Unmixed', ['Stains', 'W'])
    Output = Unmixed(stains, w)

    # return solution
    return Output
def SparseColorDeconvolution(I, Winit, Beta):
    """Performs adaptive color deconvolution.

    Uses sparse non-negative matrix factorization to adaptively deconvolve a
    given RGB image into intensity images representing distinct stains.
    Similar approach to ``ColorDeconvolution`` but operates adaptively.
    The input RGB image `I` consisting of RGB values is first transformed
    into optical density space as a row-matrix, and then is decomposed as
    :math:`V = W H` where :math:`W` is a 3xk matrix containing stain vectors
    in columns and :math:`H` is a k x m*n matrix of concentrations for each
    stain vector. The system is solved to encourage sparsity of the columns
    of :math"`H` i.e. most pixels should not contain significant contributions
    from more than one stain. Can use a hot-start initialization from a color
    deconvolution matrix.

    Parameters
    ----------
    I : array_like
        An RGB image of type unsigned char, or a 3xN matrix of RGB pixel
        values.
    Winit : array_like
        A 3xK matrix containing the color vectors in columns. Should not be
        complemented with ComplementStainMatrix for sparse decomposition to
        work correctly.
    Beta : double
        Regularization factor for sparsity of :math:`H` - recommended 0.5.

    Returns
    -------
    Stains : array_like
        An rgb image with deconvolved stain intensities in each channel,
        values ranging from [0, 255], suitable for display.
    W : array_like
        The final 3 x k stain matrix produced by NMF decomposition.

    Notes
    -----
    Return values are returned as a namedtuple

    See Also
    --------
    histomicstk.preprocessing.color_deconvolution.ColorDeconvolution

    References
    ----------
    .. [1] J. Xu, L. Xiang, G. Wang, S. Ganesan, M. Feldman, N.N. Shih,
           H. Gilmore, A. Madabhushi, "Sparse Non-negative Matrix
           Factorization (SNMF) based color unmixing for breast
           histopathological image analysis," in IEEE Computer Graphics
           and Applications, vol.46,no.1,pp.20-9, 2015.
    """

    # determine if input is RGB or pixel-matrix format
    if len(I.shape) == 3:  # RBG image provided
        m = I.shape[0]
        n = I.shape[1]
        I = np.reshape(I, (m * n, 3)).transpose()
    elif len(I.shape) == 2:  # pixel matrix provided
        m = -1
        n = -1
        if I.shape[2] == 4:  # remove alpha channel if needed
            I = I[:, :, (0, 1, 2)]

    # transform input RGB to optical density values
    I = I.astype(dtype=np.float32)
    I[I == 0] = 1e-16
    ODfwd = color_conversion.rgb_to_od(I)

    if Winit is None:

        # set number of output stains
        K = 3

        # perform NMF without initialization
        Factorization = nimfa.Snmf(V=ODfwd, seed=None, rank=K,
                                   version='r', beta=Beta)
        Factorization()

    else:

        # get number of output stains
        K = Winit.shape[1]

        # normalize stains to unit-norm
        for i in range(K):
            Norm = np.linalg.norm(Winit[:, i])
            if(Norm >= 1e-16):
                Winit[:, i] /= Norm
            else:
                print 'error'  # throw error

        # estimate initial H given p
        Hinit = np.dot(np.linalg.pinv(Winit), ODfwd)
        Hinit[Hinit < 0] = 0

        # perform regularized NMF
        Factorization = nimfa.Snmf(V=ODfwd, seed=None, W=Winit,
                                   H=Hinit, rank=K,
                                   version='r', beta=Beta)
        Factorization()

    # extract solutions and make columns of "W" unit-norm
    W = np.asarray(Factorization.basis())
    H = np.asarray(Factorization.coef())
    for i in range(K):
        Norm = np.linalg.norm(W[:, i])
        W[:, i] /= Norm
        H[i, :] *= Norm

    # reshape H matrix to image
    if m == -1:
        StainsFloat = np.transpose(H)
    else:
        StainsFloat = np.reshape(np.transpose(H), (m, n, K))

    # transform type
    Stains = np.copy(StainsFloat)
    Stains[Stains > 255] = 255
    Stains = Stains.astype(np.uint8)

    # build named tuple for outputs
    Unmixed = collections.namedtuple('Unmixed', ['Stains', 'W'])
    Output = Unmixed(Stains, W)

    # return solution
    return Output