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

Note: A very frequently asked question in the ImageJ mailing list relates to quantification of immunostain intensity (for example DAB intensity) to evaluate antigen expression. However, one needs to consider 2 main issues that prevent doing this in a quantitative manner:
  1. Antigen-antibody reactions are not stoichiometric, so "darkness of stain" does not mean "amount of reaction products". In fact most histological stains are not stoichiometric (one exception is Feulgen stain which is commonly used for DNA cytometry).

  2. In particular DAB does not follow Beer-Lambert law. See, for example, CM van der Loos paper:
    "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."
  3. 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 or for image enhancement for colour blind observers, attempting to quantify DAB intensity 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" interactive option. If one uses images with combined dyes, there is no guarantee that the vectors are correct as there is always some degree of dye colocalisation between the individual dyes (so even when the results look acceptable, the contribution of each dye is likely to be erroneous).
The plugin takes an RGB image and returns three 8-bit images with a colour look up table that corresponds to the respective vector colours.
When the specimen is stained with a 2 colour scheme (such as H&E) the 3rd image represents 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!

New in version 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])".

New in version 1.3: disable popup menu when using ROIs.

New in version 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 for new vectors.

New in version 1.5
: Added vector for Masson Trichrome stain.


Examples

Haematoxylin and Eosin separation (using the built-in vectors).
H and E stained Haematoxylin Eosin 3rd pigment
From left to right: original, Haematoxylin, Eosin, virtually empty 3rd (complementary) component (showing that the vectors match the image quite well).

Haematoxylin and DAB separation (using the built-in vectors).
Haematoxylin and DAB stained Haematoxylin DAB 3rd pigment
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).
Feulgen Methyl Green Feulgen Methyl Green 3rd pigment
From left to right: original Feulgen, Methyl Green, 3rd component.

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

Masson Trichrome (using the built-in vectors).
Masson Trichrome Methyl Blue Ponceau-Fuchsin 3rd pigment
From left to right: original (Masson Trichrome), Methyl Blue, Ponceau-Fuchsin, 3rd component.
Note that this is a particularly difficult stain to separate because the red component is not unique but the result of two dyes: Fuchsin and Xylidine Ponceau (with slightly different hues). Likewise, the blue component is Methyl Blue, but commonly Iron Haematoxylin is also used for nuclear stain (thus adding another 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 within the same image. (Image of MS408, folio 2 recto courtesy of the Beinecke Rare Book and Manuscript Library, Yale University).
Original image green
ink 3rd component
Top row: original, green pigment.
Bottom row: brown ink, 3rd component.

Vector values for this example:
 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 ratio (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).
original image blue pigment
Left: original, right: ratio of the original and the blue pigment image.
Here is an ImageJ macro to perform this task (requires Colour_deconvolution plugin and assumes that the 1st. colour in the deconvolved images is the one to filter out ).



Determining new vectors

  • Grab images with single stains to make sure that the colour vectors are pure and contain no contribution of other dyes. For example, to create the vectors of H&E, grab one image stained only with Haematoxylin and one stained only with Eosin. Apply 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.

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

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

  • Now the plugin will ask you to make selections. 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 2 colours instead of 3, for the 3rd selection just right-click and the vector will be defined automatically as the '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 and one can evaluate the stain separation.
    The Log window will show something like this (the values will be different, of course):

    From ROI Vector Matrix ---
    Colour[1]:
    R1: 52.347073
    G1: 54.48523
    B1: 20.798874

    Colour[2]:
    R2: 2.6333768
    G2: 44.480793
    B2: 12.087534

    Colour[3]:
    R3: 0.0
    G3: 0.0
    B3: 0.0

    From ROI Java code ---
    if (myStain.equals("New_Stain")){
    // This is the New_Stain
    MODx[0]=0.66797;
    MODy[0]=0.6952538;
    MODz[0]=0.26540214;

    MODx[1]=0.05703767;
    MODy[1]=0.9634325;
    MODz[1]=0.26181015;

    MODx[2]=0.62452626;
    MODy[2]=0.0;
    MODz[2]=0.78100383;
    }

    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: vectors are now stored in a separate file. Thanks to Benjamin Pavie, the Fiji version of this plugin stores the vectors in the /plugins/colourdeconvolution.txt file, which should be easier to use and maintain.

    That is a comma-delimited file, containing one stain method (with 3 vectors containing 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 indicates a comment.

    #Stain_Name,R0,G0,B0,R1,G1,B1,R2,G2,B2
    H&E,0.644211000,0.716556000,0.266844000,0.09278900,0.95411100,0.28311100,0.00000000,0.00000000,0.0000000
    H&E 2,0.490157340,0.768970850,0.410401730,0.04615336,0.84206840,0.53739250,0.00000000,0.00000000,0.0000000
    H DAB,0.650000000,0.704000000,0.286000000,0.26800000,0.57000000,0.77600000,0.00000000,0.00000000,0.0000000
    
    ...etc.

    The first time you run the plugin, 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 and experimental conditions vary and is likely that they will not correspond to the stains you are 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 old version of the plugin (still downloadable from this site) follow the rest of this explanation if you want to add new vectors into the source code:

  • Now look for the text under the title "From ROI Java Code --- ". Those are the Java statements that need to be pasted in the plugin source code, after the line that reads "// stains are defined after this line:"
    The plugin code looks like this:
    // stains are defined after this line:
    if (myStain.equals("New_Stain")){
    // This is the New_Stain
    MODx[0]=0.66797;
    MODy[0]=0.6952538;
    MODz[0]=0.26540214;

    MODx[1]=0.05703767;
    MODy[1]=0.9634325;
    MODz[1]=0.26181015;

    MODx[2]=0.62452626;
    MODy[2]=0.0;
    MODz[2]=0.78100383;
    }
  • Change the default name of the stain "New_Stain" to the true staining method name.

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

  • 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 that you gave it above. For example (the new stain name was appended at the end):
     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"};
  • Recompile the plugin from the menu entry Plugins>Compile and Run...

  • Restart IJ or run the Help>Update Menus command so the new class becomes active. That is all.


  • I would be grateful for any new stain vectors with images used to determine them, so they can be tested.

    References

    [1] Ruifrok AC, Johnston DA. Quantification of histochemical staining by color deconvolution. Anal Quant Cytol Histol 23: 291-299, 2001.
    [2] A free plugin (no source code) for Photoshop-compatible hosts to do colour separation is available from www.4n6site.com.
    [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-2010 (except where indicated).
    Last updated on Dec/2015.

    Back