Identifying positive pixels after color deconvolution ignoring boundaries

I am analyzing histology tissue images stained with a specific protein marker which I would like to identify the positive pixels for that marker. My problem is that thresholding on the image gives too much false positives which I’d like to exclude.

I am using color deconvolution (separate_stains from skimage.color) to get the AEC channel (corresponding to the red marker), separating it from the background (Hematoxylin blue color) and applying cv2 Otsu thresholding to identify the positive pixels using cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU), but it is also picking up the tissue boundaries (see white lines in the example picture, sometimes it even has random colors other than white) and sometimes even non positive cells (blue regions in the example picture). It’s also missing some faint positive pixels which I’d like to capture.

Overall: (1) how do I filter the false positive tissue boundaries and blue pixels? and (2) how do I adjust the Otsu thresholding to capture the faint red positives?


One option I like is to measure the properties for all positives (true or false), then develop some criteria by which to filter out the false positives based on the measures.

Below is an example using the AEC jpg as input that prints whether each detected region is true/false based on some criteria I totally made up with no knowledge of your sample :slight_smile:

import numpy as np
from import imread, imshow, show
from skimage.color import separate_stains, hax_from_rgb
from skimage.filters import threshold_otsu
from skimage.measure import label, regionprops

aec_path = 'C:/FILES/Scratch/AEC.jpg'

aec = imread(aec_path, as_gray=True)

otsu = threshold_otsu(aec)

masked = np.where(aec>otsu, 255, 0)

labeled = label(masked)

properties = regionprops(labeled, aec)

for p in properties:
    area = p.area
    mean = p.mean_intensity
    if area < 1000 and area > 5 and mean > 0.4:

You could also try blob detection or extracting coordinates of local maxima.

If you don’t like the output of the blob detectors in #scikit-image, and want the actual contours of the nuclei instead of just their centroid coordinates, you can run a blob filter and manually threshold the output of that.

Otsu (among others) is also included in scikit-image. So if you are only importing opencv for Otsu, you can eliminate that extra dependency:

Hope this helps! Let me know if I misunderstood.

Thank you. Can you clarify what are you doing in the quoted region?
Also. regarding the coordinates - I’m actually looking to quantify the % of positive pixels relative to the tissue regions, so I need the actual positive pixels rather than the coordinates of the cell.

The label function takes the binary image, containing both true and false positives, and gives each individual object in that image its own pixel value. Then the regionprops function computes a series of measurements for each of those individual objects.

Since you’re only interested in % of the total image that contains positive pixels, I’d recommend trying this instead:

  1. Use gaussian_laplace on your deconvolved image. This makes a new gray image where, ideally, only blobs are bright
  2. Threshold the “blob” image
  3. Compute the percentage of the pixels in the image that are thresholded vs. the total size of the array; something like numpy.count_nonzero(thresholded) / thresholded.size