Search code examples
pythonimageopencvnoise

Remove image noise based on spatial dimensions


I would like to remove the noise "confetti" from around the main feature of a RGB image using Python. Ideally, this process would leave the large feature (blob) in the center of the example image untouched. i.e. Is it possible to only remove noise when its area is below a given value?

I have tried using OpenCV's fastNlMeansDenoisingColored function on an example image (see below) but this removes significant signal from the rest of the image.

Here is the example image:

example.png

Which can also be downloaded here.

import cv2
import matplotlib.pyplot as plt
import numpy as np

img = cv2.imread('example.png')

dst = cv2.fastNlMeansDenoisingColored(img,None,10,7,21)

# Original
plt.imshow(img)
plt.show()
print(np.nanmin(img),np.nanmax(img))
# denoised
plt.imshow(dst)
print(np.nanmin(dst),np.nanmax(dst))
plt.show()
# difference 
plt.imshow(img-dst)
plt.show()

Result from code


Solution

  • If you only want the central blob you could opted for finding contours and selecting the one with the maximum area.

    Code:

    #--- convert image to grayscale ---
    imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
    
    #--- Perform Otsu threshold ---
    ret2, th2 = cv2.threshold(imgray,0,255,cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    cv2.imshow('Threshold', th2)
    

    It results in a binary image:

    enter image description here

    #--- Finding contours using the binary image ---
     _, contours, hierarchy =    cv2.findContours(th2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    #--- finding the contour with the maximum area ---
    big_contour = 0
    max_area = 0
    
    for cnt in contours:
        if (cv2.contourArea(cnt) > max_area):
            max_area = cv2.contourArea(cnt)
            big_contour = cnt
    
    #--- creating a mask containing only the biggest contour ---
    mask = np.zeros(imgray.shape)
    cv2.drawContours(mask, [big_contour], 0, (255,255,255), -1)
    cv2.imshow('Mask', mask)
    

    enter image description here

    #--- masking the image above with a copy of the original image ---
    im2 = im.copy()
    fin = cv2.bitwise_and(im2, im2, mask = mask.astype(np.uint8))
    cv2.imshow('Final result', fin)
    

    enter image description here