DaniSSimo
DaniSSimo

Reputation: 31

Python filter to remove outliers in image

Writing CNN to classify pictures. I encountered a problem with garbage pixels. image The resulting network gives ~90% quality, it seems that it can be improved by averaging these pixels. Is there a ready algorithm in numpy, opencv, etc. that allows to do this? Not normally smoothing, but specifically for these pixels. Or do I have to do it manually?

Upvotes: 2

Views: 3767

Answers (1)

stateMachine
stateMachine

Reputation: 5815

I agree that if you are using a CNN for some kind of classification you should train the network to handle this kind of noisy images. Maybe augment your dataset with some salt and pepper noise. Anyway, here's a possible solution for filtering out the outliers. It builds on the idea proposed by fmw42. These are the steps:

  1. Apply a median blur with a large kernel
  2. Convert the original (unprocessed) image to grayscale
  3. (Invert) Threshold the grayscale image with a low threshold value (e.g, 5) to create a mask for the outliers close to 0.
  4. Threshold the grayscale image with a high low threshold value (e.g., 250) to create a mask for the outliers close to 255.
  5. Combine both mask to create the outlier mask
  6. Use the outlier mask to adaptive-filter the original input image substituting the median values where necessary.

Let's see the code:

# Imports:
import cv2
import numpy as np

# image path
path = "D://opencvImages//noisyNumbers//"
fileName = "noisy01.png"

# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)

# Apply median filter:
filteredImage = cv2.medianBlur(inputImage, ksize=11)

# Convert input image to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)

The median filtering with a kernel size of 11 looks like this:

The outliers are practically gone. Now, let's put this aside for the moment and compute a pair of binary masks for both outliers:

# Get low mask:
_, lowMask = cv2.threshold(grayscaleImage, 5, 255, cv2.THRESH_BINARY_INV)

# Get high mask:
_, highMask = cv2.threshold(grayscaleImage, 250, 255, cv2.THRESH_BINARY)

# Create outliers mask:
outliersMask = cv2.add(lowMask, highMask)

The outliers mask is this:

Now, you really don't provide your original data. You provide an image most likely plotted using matplotlib. That's a problem, because the image you posted is processed and compressed. This results in some sharp edges around the outliers on the original image. One straightforward solution is to dilate the outliers mask a little bit to cover this compression artifacts:

# Set kernel (structuring element) size:
kernelSize = 3
# Set operation iterations:
opIterations = 1
# Get the structuring element:
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))

# Apply dilation:
outliersMask = cv2.dilate(outliersMask, maxKernel)

The outliers mask now looks like this:

Ok, let's adaptive-filter the original input using the median blurred image and the outliers mask. Just make sure to reshape all the numpy arrays to their proper size for broadcasting:

# Re-shape the binary mask to a 3-channeled image:
augmentedBinary = cv2.merge([outliersMask, outliersMask, outliersMask])
# Apply the adaptive filter:
cleanedImage = np.where(augmentedBinary == (255, 255, 255), filteredImage, inputImage)

# Show the result
cv2.imshow("Adaptive Filtering", cleanedImage)
cv2.waitKey(0)

For the first image, this is the result:

More results:

Upvotes: 4

Related Questions