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:
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()
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:
#--- 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)
#--- 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)