# 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 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+