# HG changeset patch # User imgteam # Date 1741284733 0 # Node ID 387414aa64960ef3b431134266ab83cbf3434846 # Parent 29110ca1b63abe35993b509eeb2bde0a7970fbc4 planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/color_deconvolution/ commit f546b3cd5cbd3a8613cd517975c7ad1d1f83514e diff -r 29110ca1b63a -r 387414aa6496 color-deconvolution.xml --- a/color-deconvolution.xml Mon Jul 22 04:55:58 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ - - Color deconvolution - - scikit-image - pillow - scikit-learn - numpy - tifffile - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - **What it does** - - This tools performs several color deconvolution techniques. - - 10.7717/peerj.453 - @inproceedings{sklearn_api, - author = {Lars Buitinck and Gilles Louppe and Mathieu Blondel and - Fabian Pedregosa and Andreas Mueller and Olivier Grisel and - Vlad Niculae and Peter Prettenhofer and Alexandre Gramfort - and Jaques Grobler and Robert Layton and Jake VanderPlas and - Arnaud Joly and Brian Holt and Ga{\"{e}}l Varoquaux}, - title = {{API} design for machine learning software: experiences from the scikit-learn - project}, - booktitle = {ECML PKDD Workshop: Languages for Data Mining and Machine Learning}, - year = {2013}, - pages = {108--122}, -} - - diff -r 29110ca1b63a -r 387414aa6496 color_deconvolution.py --- a/color_deconvolution.py Mon Jul 22 04:55:58 2019 -0400 +++ b/color_deconvolution.py Thu Mar 06 18:12:13 2025 +0000 @@ -1,87 +1,115 @@ import argparse import sys import warnings + +import giatools.io import numpy as np -import skimage.io import skimage.color +import skimage.io import skimage.util -from sklearn.decomposition import PCA, NMF, FastICA, FactorAnalysis +import tifffile +from sklearn.decomposition import FactorAnalysis, FastICA, NMF, PCA + +# Stain separation matrix for H&E color deconvolution, extracted from ImageJ/FIJI +rgb_from_he = np.array([ + [0.64431860, 0.7166757, 0.26688856], + [0.09283128, 0.9545457, 0.28324000], + [0.63595444, 0.0010000, 0.77172660], +]) convOptions = { - 'hed2rgb' : lambda img_raw: skimage.color.hed2rgb(img_raw), - 'hsv2rgb' : lambda img_raw: skimage.color.hsv2rgb(img_raw), - 'lab2lch' : lambda img_raw: skimage.color.lab2lch(img_raw), - 'lab2rgb' : lambda img_raw: skimage.color.lab2rgb(img_raw), - 'lab2xyz' : lambda img_raw: skimage.color.lab2xyz(img_raw), - 'lch2lab' : lambda img_raw: skimage.color.lch2lab(img_raw), - 'luv2rgb' : lambda img_raw: skimage.color.luv2rgb(img_raw), - 'luv2xyz' : lambda img_raw: skimage.color.luv2xyz(img_raw), - 'rgb2hed' : lambda img_raw: skimage.color.rgb2hed(img_raw), - 'rgb2hsv' : lambda img_raw: skimage.color.rgb2hsv(img_raw), - 'rgb2lab' : lambda img_raw: skimage.color.rgb2lab(img_raw), - 'rgb2luv' : lambda img_raw: skimage.color.rgb2luv(img_raw), - 'rgb2rgbcie' : lambda img_raw: skimage.color.rgb2rgbcie(img_raw), - 'rgb2xyz' : lambda img_raw: skimage.color.rgb2xyz(img_raw), - #'rgb2ycbcr' : lambda img_raw: skimage.color.rgb2ycbcr(img_raw), - #'rgb2yiq' : lambda img_raw: skimage.color.rgb2yiq(img_raw), - #'rgb2ypbpr' : lambda img_raw: skimage.color.rgb2ypbpr(img_raw), - #'rgb2yuv' : lambda img_raw: skimage.color.rgb2yuv(img_raw), - #'rgba2rgb' : lambda img_raw: skimage.color.rgba2rgb(img_raw), - 'rgbcie2rgb' : lambda img_raw: skimage.color.rgbcie2rgb(img_raw), - 'xyz2lab' : lambda img_raw: skimage.color.xyz2lab(img_raw), - 'xyz2luv' : lambda img_raw: skimage.color.xyz2luv(img_raw), - 'xyz2rgb' : lambda img_raw: skimage.color.xyz2rgb(img_raw), - #'ycbcr2rgb' : lambda img_raw: skimage.color.ycbcr2rgb(img_raw), - #'yiq2rgb' : lambda img_raw: skimage.color.yiq2rgb(img_raw), - #'ypbpr2rgb' : lambda img_raw: skimage.color.ypbpr2rgb(img_raw), - #'yuv2rgb' : lambda img_raw: skimage.color.yuv2rgb(img_raw), - - 'rgb_from_hed' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hed), - 'rgb_from_hdx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hdx), - 'rgb_from_fgx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_fgx), - 'rgb_from_bex' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bex), - 'rgb_from_rbd' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_rbd), - 'rgb_from_gdx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_gdx), - 'rgb_from_hax' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hax), - 'rgb_from_bro' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bro), - 'rgb_from_bpx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bpx), - 'rgb_from_ahx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_ahx), - 'rgb_from_hpx' : lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hpx), - - 'hed_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hed_from_rgb), - 'hdx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hdx_from_rgb), - 'fgx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.fgx_from_rgb), - 'bex_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bex_from_rgb), - 'rbd_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.rbd_from_rgb), - 'gdx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.gdx_from_rgb), - 'hax_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hax_from_rgb), - 'bro_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bro_from_rgb), - 'bpx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bpx_from_rgb), - 'ahx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.ahx_from_rgb), - 'hpx_from_rgb' : lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hpx_from_rgb), - - 'pca' : lambda img_raw: np.reshape(PCA(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), - [img_raw.shape[0],img_raw.shape[1],-1]), - 'nmf' : lambda img_raw: np.reshape(NMF(n_components=3, init='nndsvda').fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), - [img_raw.shape[0],img_raw.shape[1],-1]), - 'ica' : lambda img_raw: np.reshape(FastICA(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), - [img_raw.shape[0],img_raw.shape[1],-1]), - 'fa' : lambda img_raw: np.reshape(FactorAnalysis(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), - [img_raw.shape[0],img_raw.shape[1],-1]) + # General color space conversion operations + 'hed2rgb': lambda img_raw: skimage.color.hed2rgb(img_raw), + 'hsv2rgb': lambda img_raw: skimage.color.hsv2rgb(img_raw), + 'lab2lch': lambda img_raw: skimage.color.lab2lch(img_raw), + 'lab2rgb': lambda img_raw: skimage.color.lab2rgb(img_raw), + 'lab2xyz': lambda img_raw: skimage.color.lab2xyz(img_raw), + 'lch2lab': lambda img_raw: skimage.color.lch2lab(img_raw), + 'luv2rgb': lambda img_raw: skimage.color.luv2rgb(img_raw), + 'luv2xyz': lambda img_raw: skimage.color.luv2xyz(img_raw), + 'rgb2hed': lambda img_raw: skimage.color.rgb2hed(img_raw), + 'rgb2hsv': lambda img_raw: skimage.color.rgb2hsv(img_raw), + 'rgb2lab': lambda img_raw: skimage.color.rgb2lab(img_raw), + 'rgb2luv': lambda img_raw: skimage.color.rgb2luv(img_raw), + 'rgb2rgbcie': lambda img_raw: skimage.color.rgb2rgbcie(img_raw), + 'rgb2xyz': lambda img_raw: skimage.color.rgb2xyz(img_raw), + 'rgbcie2rgb': lambda img_raw: skimage.color.rgbcie2rgb(img_raw), + 'xyz2lab': lambda img_raw: skimage.color.xyz2lab(img_raw), + 'xyz2luv': lambda img_raw: skimage.color.xyz2luv(img_raw), + 'xyz2rgb': lambda img_raw: skimage.color.xyz2rgb(img_raw), + + # Color deconvolution operations + 'hed_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hed_from_rgb), + 'hdx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hdx_from_rgb), + 'fgx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.fgx_from_rgb), + 'bex_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bex_from_rgb), + 'rbd_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.rbd_from_rgb), + 'gdx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.gdx_from_rgb), + 'hax_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hax_from_rgb), + 'bro_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bro_from_rgb), + 'bpx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.bpx_from_rgb), + 'ahx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.ahx_from_rgb), + 'hpx_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, skimage.color.hpx_from_rgb), + + # Recomposition operations (reverse color deconvolution) + 'rgb_from_hed': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hed), + 'rgb_from_hdx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hdx), + 'rgb_from_fgx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_fgx), + 'rgb_from_bex': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bex), + 'rgb_from_rbd': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_rbd), + 'rgb_from_gdx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_gdx), + 'rgb_from_hax': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hax), + 'rgb_from_bro': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bro), + 'rgb_from_bpx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_bpx), + 'rgb_from_ahx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_ahx), + 'rgb_from_hpx': lambda img_raw: skimage.color.combine_stains(img_raw, skimage.color.rgb_from_hpx), + + # Custom color deconvolution and recomposition operations + 'rgb_from_he': lambda img_raw: skimage.color.combine_stains(img_raw, rgb_from_he), + 'he_from_rgb': lambda img_raw: skimage.color.separate_stains(img_raw, np.linalg.inv(rgb_from_he)), + + # Unsupervised machine learning-based operations + 'pca': lambda img_raw: np.reshape(PCA(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), + [img_raw.shape[0], img_raw.shape[1], -1]), + 'nmf': lambda img_raw: np.reshape(NMF(n_components=3, init='nndsvda').fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), + [img_raw.shape[0], img_raw.shape[1], -1]), + 'ica': lambda img_raw: np.reshape(FastICA(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), + [img_raw.shape[0], img_raw.shape[1], -1]), + 'fa': lambda img_raw: np.reshape(FactorAnalysis(n_components=3).fit_transform(np.reshape(img_raw, [-1, img_raw.shape[2]])), + [img_raw.shape[0], img_raw.shape[1], -1]) } parser = argparse.ArgumentParser() parser.add_argument('input_file', type=argparse.FileType('r'), default=sys.stdin, help='input file') parser.add_argument('out_file', type=argparse.FileType('w'), default=sys.stdin, help='out file (TIFF)') parser.add_argument('conv_type', choices=convOptions.keys(), help='conversion type') -args = parser.parse_args() +parser.add_argument('--isolate_channel', type=int, help='set all other channels to zero (1-3)', default=0) +args = parser.parse_args() + +# Read and normalize the input image as TZYXC +img_in = giatools.io.imread(args.input_file.name) + +# Verify input image +assert img_in.shape[0] == 1, f'Image must have 1 frame (it has {img_in.shape[0]} frames)' +assert img_in.shape[1] == 1, f'Image must have 1 slice (it has {img_in.shape[1]} slices)' +assert img_in.shape[4] == 3, f'Image must have 3 channels (it has {img_in.shape[4]} channels)' -img_in = skimage.io.imread(args.input_file.name)[:,:,0:3] -res = convOptions[args.conv_type](img_in) -res[res<-1]=-1 -res[res>1]=1 +# Normalize the image from TZYXC to YXC +img_in = img_in.squeeze() +assert img_in.ndim == 3 + +# Apply channel isolation +if args.isolate_channel: + for ch in range(3): + if ch + 1 != args.isolate_channel: + img_in[:, :, ch] = 0 + +result = convOptions[args.conv_type](img_in) + +# It is sufficient to store 32bit floating point data, the precision loss is tolerable +if result.dtype == np.float64: + result = result.astype(np.float32) with warnings.catch_warnings(): - warnings.simplefilter("ignore") - res = skimage.util.img_as_uint(res) #Attention: precision loss - skimage.io.imsave(args.out_file.name, res, plugin='tifffile') + warnings.simplefilter('ignore') + tifffile.imwrite(args.out_file.name, result) diff -r 29110ca1b63a -r 387414aa6496 color_deconvolution.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/color_deconvolution.xml Thu Mar 06 18:12:13 2025 +0000 @@ -0,0 +1,212 @@ + + + + creators.xml + tests.xml + 0.9 + 0 + + + + + + operation_3443 + + + galaxy_image_analysis + + + scikit-image + pillow + scikit-learn + numpy + tifffile + giatools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10.7717/peerj.453 + @inproceedings{sklearn_api, + author = {Lars Buitinck and Gilles Louppe and Mathieu Blondel and + Fabian Pedregosa and Andreas Mueller and Olivier Grisel and + Vlad Niculae and Peter Prettenhofer and Alexandre Gramfort + and Jaques Grobler and Robert Layton and Jake VanderPlas and + Arnaud Joly and Brian Holt and Ga{\"{e}}l Varoquaux}, + title = {{API} design for machine learning software: experiences from the scikit-learn project}, + booktitle = {ECML PKDD Workshop: Languages for Data Mining and Machine Learning}, + year = {2013}, + pages = {108--122}, + } + + diff -r 29110ca1b63a -r 387414aa6496 creators.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/creators.xml Thu Mar 06 18:12:13 2025 +0000 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 29110ca1b63a -r 387414aa6496 static/images/he.png Binary file static/images/he.png has changed diff -r 29110ca1b63a -r 387414aa6496 static/images/he_deconv.png Binary file static/images/he_deconv.png has changed diff -r 29110ca1b63a -r 387414aa6496 static/images/he_recomposed.png Binary file static/images/he_recomposed.png has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/galaxyIcon_noText.png Binary file test-data/galaxyIcon_noText.png has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/galaxyIcon_noText.tiff Binary file test-data/galaxyIcon_noText.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/hdab1.tiff Binary file test-data/hdab1.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/hdab1_deconv_hdab.tiff Binary file test-data/hdab1_deconv_hdab.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1.tiff Binary file test-data/he1.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_axes_cyx.tiff Binary file test-data/he1_axes_cyx.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_axes_yxz.tiff Binary file test-data/he1_axes_yxz.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_deconv_he.tiff Binary file test-data/he1_deconv_he.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_deconv_hed.tiff Binary file test-data/he1_deconv_hed.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_deconv_hed_recomposed.tiff Binary file test-data/he1_deconv_hed_recomposed.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_deconv_hed_recomposed1.tiff Binary file test-data/he1_deconv_hed_recomposed1.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/he1_hsv.tiff Binary file test-data/he1_hsv.tiff has changed diff -r 29110ca1b63a -r 387414aa6496 test-data/im_axes_yx.tif Binary file test-data/im_axes_yx.tif has changed diff -r 29110ca1b63a -r 387414aa6496 tests.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests.xml Thu Mar 06 18:12:13 2025 +0000 @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +