machine-learningimage-processingcomputer-visionconv-neural-network

Identify pixels occupied by red particles in image


I have some images from an experiment with plastics particles and water waves. The goal is to identify the plastics particles automatically. They sometimes overlap, and I don't need to find individual particles, it is enough to find those pixels that contain plastics.

Since the particles are red, and the background is mostly white or black, I thought I could go for a simple thresholding, saying that pixels are plastics if R > 5*B and R > 0.25, where R and B are the red and blue channels. However the exposure varies quite a bit between experiments, and sometimes within an experiment when part of the surface is covered by water, so my approach doesn't work very consistently, and sometimes mis-identifies the dark cracks along the sides.

I'm wondering what could be other options. I have limited experience with neural networks, so I'm not sure if that would work (with a reasonable amount of effort). In particular, I don't think shape is going to be much help, since the particles are close together and partially overlapping with poor contrast between them, but maybe colour is enough?

Example images:

enter image description here

enter image description here

enter image description here


Solution

  • If you convert to Lab colourspace, separate the 3 channels, contrast-stretch them and lay them out side-by-side across the page with L on the left, a in the centre and b on the right using ImageMagick:

    magick image.png -alpha off -colorspace lab -separate -normalize +append lab.png
    

    enter image description here

    You can see in the centre (a) channel that your plastics are well differentiated from the crack at the left of the image. That suggests an OTSU threshold of the a channel will be a good discriminant for what you seek.

    With OpenCV, that might look like this:

    import cv2 as cv
    
    # Load image
    im = cv.imread('image.png')
    
    # Convert to Lab colourspace and take "a" channel
    Lab = cv.cvtColor(im,cv.COLOR_BGR2LAB)
    a = Lab[..., 1]
    
    # discriminate plastics using "a" channel
    plastics = cv.threshold(a, 0, 255, cv.THRESH_OTSU+cv.THRESH_BINARY)[1]
    

    First image

    enter image description here

    Second image

    enter image description here


    The Otsu threshold doesn't work so well for your third image because there is more "crack" on the left side than there is plastics, so the two categories you end up with are "background" and "crack", rather than the desired "background" and "plastics". However, the a channel does still discriminate if you find a (non-Otsu) threshold. For example, continuing from code above:

    norm = cv.normalize(a, None, alpha=0, beta=255, norm_type= cv2.NORM_MINMAX)
    

    enter image description here