pythonimagepngnoise

Remove central noise from image


I'm working with biologists, who are imaging DNA strands under a microscope, giving greyscale PNGs. Depending on the experiment, there can be a lot of background noise. On the ones where there is quite little, a simple threshold for pixel intensity is generally enough to remove it. However for some, it's not so simple.

DNA lines on noisy background

(not sure why it's uploading as jpg - this should let you download it as PNG)

When I do apply a threshold for pixel intensity, I get this result:

Same DNA lines, but with a less noisy background

If I raise the threshold any more, I'll start to lose the pixels at the edges of the image that aren't very bright.

The noise seems to follow a gaussian distribution based on its location within the image, probably due to the light source of the microscope. What's the best way to compare pixels to the local background noise? Can I integrate the noise distribution?

For the moment I've been using imageio.v3 in python3 for basic pixel manipulation, but I'm open to suggestions.

EDIT: I tried to use histogram equalisation, but it doesn't quite seem to be what I'm looking for...

DNA lines after histogram equalisation


Solution

  • I am not sure what result you want so I am working somewhat in the dark. I suspect you want a "Local Area Threshold" - see Wikipedia. Also referred to as "Adaptive Threshold".

    It is available, or can be achieved in many image processing packages, but for brevity and until I (or others) better understand what you want as a result, I will just demonstrate with ImageMagick which is free and can be installed on macOS, Linux and Windows. The command is -lat which means "Local Area Threshold".

    So, the syntax to highlight in white any pixel that is more than 2% brighter than the surrounding local area of 80x80 is as follows:

    magick image.png -alpha off -lat 80x80+2% result.png
    

    enter image description here

    If you want any pixel more than 2% brighter than its surrounding 10x10 neighbourhood, use:

    magick image.png -alpha off -lat 10x10+2% result.png
    

    enter image description here


    Note that I used -alpha off because your image has a superfluous alpha channel I wanted to discard.


    Using OpenCV you'll get something vaguely similar with:

    #!/usr/bin/env python3
    
    import cv2 as cv
    
    # Load image as greyscale
    im = cv.imread('image.png', cv.IMREAD_GRAYSCALE)
    
    # Local area is 49x49, and pixel must be 15 above threshold to show in output
    thMean = cv.adaptiveThreshold(im,255,cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY,49,-15)
    cv.imwrite('result-mean.png', thMean)
    
    # Local area is 29x29, and pixel must be 10 above threshold to show in output
    thGauss = cv.adaptiveThreshold(im,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY,29,-10)
    cv.imwrite('result-Gauss.png', thGauss)