Thresholding in Lab space

I’m trying to do color thresholding of DAB stained image to measure the blue and brown areas. I am able to use the color threshold in Fiji, but because I have other pre and post scripts I’m trying to replicate in opencv how color threshold works.

I have code that comes close to what the plugin does, but am probably some key step that would make it simpler. Here is the sequence I’ve tried:

  1. convert to float32 before converting to lab space (without this the roundtrip bgr–>lab–>bgr gets lots of differences)
  2. transform to lab space
  3. normalize to 0-255 l/a/b, like imagej UI shows
  4. apply thresholds in lab space. This is where it gets tricky. The opencv methods for threshold set values to 0/255. However we need value to be say 127 since 0 and 255 are blue/yellow or green/red extremes. Also for pixels outside threshold I would like them to be white, like imagej has an option, so L is set to 255. So all this requires some convoluted code.
  5. renormalize back to -127 +126 range and convert to bgr and then to greyscale
  6. use thresholding in grayscale to get back to BW image

At this point I expect a roughly matching output, but its either fully white or black or has strange colors which tells me something didnt go right!

In step 4, if I iterate over each pixel in code and set them appropriately, and some additional masking, I get matching output. But there must be a better way?

Is you image only stained with DAB or is there another dye?
Maybe you are aware of this, but if there is another dye, you might want to look at colour deconvolution and segment the dyes independently rather than the mixture (histological stains mix subtractively and generate ‘new colours’ when colocalised and that will be tricky to segment in Lab space).

Alternatively instead of rewriting an ImageJ method in OpenCV you can mix and match both ImageJ and OpenCV together.
Either with pyimagej that should allow you to call most imageJ functions from python.

Or the opposite, use IJ-OpenCV to do the other opencv tasks within ImageJ/Fiji, if the lab space thresholding can also be scripted (normally yes if you can record it with the macro-recorder).

That was another issue - the Color Threshold cmd doesnt seem to support macro recording (no params are passed) so I cant apply in batch.
I will try out the pyimagej, if the color threshold method can be called.

Its only DAB afaik. This is an example image

e, and I couldnt get qupath’s thresholder to get the H-stain right by default. So for each image a new threshold might be needed and I’m hoping to do something more generic. I have some code that works, but calling imagej’s color threshold function or its equivalent would be much better.

What is the blue stain then?

Yes, it does, that is what the button called “Macro” does.

I’m on Fiji. Am pretty sure - it does record the call but no parameters. So inside the dialog doesnt seem to be captured. Am I doing something wrong?

You do not need to record the call to the colour threshold itself, but if you wish to reproduce what the procedure does, you must press the button that says “Macro” while in Macro Recorded mode to generate a macro sequence that will reproduce the current settings of the dialogue.

1 Like

Thanks! very helpful. I missed knowing about the macro button on the dialog itself.

I’m having some trouble getting the macro to produce the same results on each run. I recorded the steps using the Macro button on the dialog.
When I run the macro, if the “Color Threshold” dialog is already up when I run the macro, it produces the right result. Otherwise it seems to do a slightly different output. It appears like a race condition between macro running and the dialog being up?
I saw a related post and will try with inserting wait() between the calls.


run("Color Threshold...");
// Color Thresholder 2.0.0-rc-69/1.52p
// Autogenerated macro, single images only!
// Color Thresholder 2.0.0-rc-69/1.52p
// Autogenerated macro, single images only!
run("RGB Stack");
run("Convert Stack to Images");
for (i=0;i<3;i++){
  setThreshold(min[i], max[i]);
  run("Convert to Mask");
  if (filter[i]=="stop")  run("Invert");
imageCalculator("AND create", "0","1");
imageCalculator("AND create", "Result of 0","2");
for (i=0;i<3;i++){
selectWindow("Result of 0");
selectWindow("Result of Result of 0");
// Colour Thresholding-------------


Adding a wait(500), and removing the run(“Color Threshold…”) call inserted by regular macro recording fixed the issue.