Colour Deconvolution

The colour deconvolution plugin (java and class files) for ImageJ and Fiji implements stain separation using Ruifrok and Johnston’s method described in [1]. The code is based on a NIH Image macro kindly provided by A. C. Ruifrok.
The plugin assumes images generated by colour subtraction (i.e. light-absorbing dyes such as those used in bright field histology or ink on printed paper). However, the dyes should not be neutral grey (most histological stains are not so). The procedure takes an RGB image as input and returns three 8-bit images with colour look up tables that correspond to the respective vector colours, i.e. it unmixes the dyes.
If you intend to work with this plugin, it is important to read the original paper to understand how new vectors are determined and how the procedure works.
The plugin works correctly when the background is neutral (white to grey), so background subtraction with colour correction must be applied to the images before processing.
The plugin provides a number of “built in” stain vectors some of which were determined experimentally in our lab (marked in the source code with ‘GL’), but you should determine your own vectors to achieve an accurate stain separation, depending on the stains and methods you use. See the note below.
The built-in vectors are :

      • Haematoxylin and Eosin (H&E)  (two different ones)
      • Haematoxylin and DAB (H DAB)
      • Feulgen Light Green
      • Giemsa
      • Fast Red, Fast Blue and DAB
      • Methyl green and DAB
      • Haematoxylin, Eosin and DAB (H&E DAB)
      • Haematoxylin and AEC (H AEC)
      • Azan-Mallory
      • Masson Trichrome
      • Alcian blue & Haematoxylin
      • Haematoxylin and Periodic Acid – Schiff (PAS)
      • RGB subtractive
      • CMY subtractive
      • User values entered by hand
      • Values interactively determined from rectangular ROIs

Note: A very frequently asked question relates to quantification of immunostain intensity (for example DAB intensity) to evaluate antigen expression. However, one needs to consider some important issues that prevent doing this in a quantitative manner:

    1. Antigen-antibody reactions are not stoichiometric, therefore “darkness of stain”  does not equate to “amount of reaction products” or  to “expression” of a given antigen. In fact most histological stains are non-stoichiometric (some exceptions are Feulgen stain which is commonly used for DNA cytometry and phalloidin for visualising actin).
    2. Immunohistochemistry uses a series of amplification steps to visualise the results making it difficult to control what the final intensity of the amplified signal actually represents in terms of amount of antigen.
    3. The chromogen DAB does not follow Beer-Lambert law either. See, e.g., the paper by CM van der Loos:
      “The brown DAB reaction product is not a true absorber of light, but a scatterer of light, and has a very broad, featureless spectrum. This means that DAB does not follow the Beer-Lambert law, which describes the linear relationship between the concentration of a compound and its absorbance, or optical density. As a consequence, darkly stained DAB has a different spectral shape than lightly stained DAB.”
  1. Further details are mentioned in  this informative message posted by Al Floyd to the ImageJ mailing list.

Therefore, while colour deconvolution might be useful to segment immunostained structures (e.g. count cells, nuclei, identify structures) or for image enhancement for colour blind observers, attempting to quantify DAB intensity as a measure of a reliable measure of antigen expression using this plugin is not a good idea.

New vector determination should be done on slides stained with only one dye at a time, using the From ROI option. Using images with combined dyes, does not guarantee that the vectors are correct because there is always some degree of dye co-localisation between the individual dyes (even when the results look “acceptable”, the contribution of each dye is likely to be erroneous).

When the specimen is stained with a 2 colour scheme (such as H&E) the 3rd image represents a residual channel, with the complementary of the first two colours (i.e. green). Ideally the 3rd image should be completely white. If not, then either the colour vectors or the background colour correction have not been correctly determined.
Read the paper!

1.2: Dennis Chao has kindly provided the following modifications to the code, 1) the plugin works with stacks, 2) the user-defined vectors can now also be recorded by the macro recorder, 3) the result images keep the original image name plus ” (Colour[n])” in the name (note the leading space). Example: “test.tif” will produce images named “test.tif (Colour[1])”, “test.tif (Colour[2])” and “test.tif (Colour[3])”.
1.3: disable popup menu when using ROIs.
1.4: Added vectors for Feulgen-Light Green and Giemsa (Methylene Blue & Eosin) stains.
Images are now named with the suffixes “-(Colour_1)”, “-(Colour_2)”, and “-(Colour_3)” to minimise problems arising during the parsing image names with other macros and plugins.
Added “Hide legend” option.
The “Show matrices” option now prints to the Log window the Java code necessary for adding new vectors.
1.5: Added vector for Masson Trichrome stain.


Haematoxylin and Eosin separation (using the built-in  vectors).

From left to right: original, Haematoxylin, Eosin, virtually empty 3rd (complementary) component (showing that the vectors match the image quite well, except a column of corrupted pixels at the right border of the image).

Haematoxylin and DAB separation (using the built-in vectors).
From left to right: original, Haematoxylin, DAB, 3rd component (the vectors did not perfectly matched the stains in this image, so they should be determined again from single-stained samples).

Feulgen and Methyl Green separation (using the built-in vectors).
From left to right: original, Feulgen, Methyl Green, 3rd component.

Giemsa stain (using the built-in vectors).
From left to right: original (Giemsa),  Methylene Blue, Eosin, 3rd component.

Masson Trichrome (using the built-in vectors).

From left to right: original (Masson Trichrome), Methyl Blue, Ponceau-Fuchsin, 3rd component.
Note that this is a particularly difficult stain to separate. This is so because the red component is the result of two dyes: Fuchsin and Xylidine Ponceau (with slightly different hues). Likewise, the blue component is Methyl Blue, but Iron Haematoxylin is commonly used for nuclear stain (thus adding a further hue). Note the strong 3rd component. A similarly difficult stain to separate is Azan-Mallory.

Ink/pigment separation (using ROI defined vectors).
This example shows stain separation in an old manuscript using regions of interest (defined by the user) where stains were applied uniquely. The top part of the image was pasted from the same manuscript page to allow sampling the ink alone. (Image of MS408, folio 2 recto courtesy of the Beinecke Rare Book and Manuscript Library, Yale University).


Top row: original, green pigment.  Bottom row: brown ink (revealing some difficult to read writing), 3rd component. The vector values for this example were:

Colour[1] (green pigment):
   R1: 0.592132
   G1: 0.4580393
   B1: 0.6630081
 Colour[2] (brown ink):
   R2: 0.39982012
   G2: 0.55231196
   B2: 0.7315021
 Colour[3] (3rd component):
   R3: 0.69965965
   G3: 0.6965282
   B3: 0.15913807

Ink/pigment separation using ROI defined vectors and image ratios.
Sometimes, the pigments are not easily separated. In such case the pigment that is best separated can be filtered out using image ratios (a division of the original RGB planes by the pigment RGB planes followed by a multiplication of each plane by 255). The example below uses ratio of the original image to the blue pigment image. (Image of MS408, folio 9 verso courtesy of the Beinecke Rare Book and Manuscript Library, Yale University).

Left: original, right: ratio of the original and the blue pigment image.

Determining new vectors

1. Grab images with single stains to make sure that the colour vectors are pure and contain no contribution from other dyes. For example, to create the vectors of H&E, grab one image stained only with Haematoxylin and one stained only with Eosin. Use background correction, so empty areas appear white or bright neutral grey. See this document for step by step instructions on background correction. If no background correction is applied, the colours in the image will depend on the colour/temperature of the light source and the uneven background illumination of the microscope.

2. Stitch/paste these two images (or the if the stain consists of three dyes) together into a test image, so singly-stained areas appear in the same image.

3. Run the Colour Deconvolution plugin and select the From ROI option to define stained areas. Check the Show matrices box to display the vectors in the log window (these will need to be added to the source code later). Press OK.

Next the plugin will ask to make selections of singly-stained regions. Select small ROIs areas which are all intensely stained with only one of the dyes. Select fully stained areas, without empty background. Repeat this for each dye.

If the staining method uses only two colours, for the 3rd selection just right-click and the vector will be defined automatically as a ‘possible’ (that is, without negative components) complementary of the other 2 colours.
After selecting the three samples, the deconvolution of the test image will take place. This is where the stain separation can be evaluated.
The Log window will show something like this (the values will be different, of course):

From ROI Vector Matrix ---
  R1: 52.347073
  G1: 54.48523
  B1: 20.798874
  R2: 2.6333768
  G2: 44.480793
  B2: 12.087534
  R3: 0.0
  G3: 0.0
  B3: 0.0
From ROI Java code ---
	if (myStain.equals("New_Stain")){  
		// This is the New_Stain   



Those are the vectors for each of the dyes. It might be necessary to repeat the procedure a few times to make sure the separation is optimal.

New for Fiji users: vectors are now stored in a separate file.
Thanks to Benjamin Pavie, the Fiji version of this plugin stores the vectors in a text file created in /plugins/colourdeconvolution.txt, which should be easier to use and maintain.

That is a comma-delimited file, containing one stain method (with 3 vectors, each containing the 3 components each) per row. This includes the stain_name, followed by the red, green and blue components (in that order) of each of the 3 vectors as explained earlier (MOD arrays in the example above).
The “#” character defines a line comment:

… and so on for other stains.

The first time the plugin is run, a default /plugins/colourdeconvolution.txt file is created, containing a set of example stains.
It is, however, advisable that you determine your own vectors. Do not rely blindly on the examples provided because techniques, compounds and experimental conditions vary and it is likely that the built-in values from one lab correspond exactly to the stains one is working with.
Once new vectors are saved to the file, they will be read from this location each time the plugin runs.

Important: If you delete the file, the plugin will create the default vector set the next time it runs, so to avoid losing your previous work, it is a good idea to store a backup of your determined vectors elsewhere.

For the version of the plugin downloadable from this site, follow the rest of this explanation to add new vectors into the source code:

4. Look in the Log window the text under the title “From ROI Java Code — “.  Those are the Java statements that need to be pasted in the plugin’s source code, after the line that reads “// stains are defined after this line:” which look like this:

// stains are defined after this line: 
if (myStain.equals("New_Stain")){ 
     // This is the New_Stain 



5. Change the default name of the stain from “New_Stain” to the true staining method name.

6. If the 3rd colour was not defined (i.e. staining method consists of only two dyes), just insert 0.0 values for the third component and the complementary 3rd colour will be calculated by the plugin at run time.

7. Add the name of the stain in double quotes to the string array called “stain” (the order of names does not matter) so it matches the name given earlier. In the example below, note that the “New_Stain” name was appended at the end of the array:

String [] stain={"From ROI", "H&E", "H&E 2","H DAB", "Feulgen Light Green", "Giemsa",
"FastRed FastBlue DAB", "Methyl Green DAB", "H&E DAB", "H AEC", "Azan-Mallory", 
"Alcian blue & H", "H PAS", "RGB", "CMY", "User values", "New_Stain"};

8. Save and recompile the plugin from the ImageJ menu entry Plugins>Compile and Run…

9. Restart IJ or run the Help>Update Menus command so the new class becomes active. That is all. The compiled version should now include the newly added stain vectors.


[1] Ruifrok AC, Johnston DA. Quantification of histochemical staining by color deconvolution. Anal Quant Cytol Histol 23: 291-299, 2001.
[2] A free plugin (but no source code) for Photoshop-compatible hosts to do colour separation is available from the site:
[3] Tom Macura ported the code of this plugin to MATLAB MEX c code, and it is available at the Open Microscopy Environment Repository.

Copyright G. Landini, 2004-2019 (except where indicated).