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 )
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