damioune123
damioune123

Reputation: 53

How can I remove these parallel lines noise on my image using opencv

I'm new to opencv and I m trying to remove all these diagonal parallel lines that are noise in my image. enter image description here

I have tried using HoughLinesP after some erosion/dilatation but the result is poo (and keeping only the one with a near 135 degree angle).

    img = cv2.imread('images/dungeon.jpg')
    ret,img = cv2.threshold(img,180,255,0)

    element = cv2.getStructuringElement(cv2.MORPH_CROSS,(5,5))
    eroded = cv2.erode(img,element)
    dilate = cv2.dilate(eroded, element)
    skeleton = cv2.subtract(img, dilate)
    gray = cv2.cvtColor(skeleton,cv2.COLOR_BGR2GRAY)

    minLineLength = 10

    lines = cv2.HoughLinesP(gray, 1, np.pi/180, 1, 10, 0.5)

    for line in lines:
        for x1,y1,x2,y2 in line:
            angle = math.atan2(y2-y1,x2-x1)
            if (angle > -0.1 and angle < 0.1):
                cv2.line(img,(x1,y1),(x2,y2),(0,255,0),1)


    cv2.imshow("result", img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

enter image description here

My thinking here was to detect these lines in order to remove them afterwards but I m not even sure that's the good way to do this.

Upvotes: 0

Views: 577

Answers (1)

stateMachine
stateMachine

Reputation: 5805

I guess you are trying to get the contours of the walls, right? Here’s a possible path to the solution using mainly spatial filtering. You will still need to clean the results to get where you want. The idea is to try and compute a mask of the parallel lines (high-frequency noise) of the image and calculate the difference between the (binary) input and this mask. These are the steps:

  1. Convert the input image to grayscale
  2. Apply Gaussian Blur to get rid of the high-frequency noise you are trying to eliminate
  3. Get a binary image of the blurred image
  4. Apply area filters to get rid of everything that is not noise, to get a noise mask
  5. Compute the difference between the original binary mask and the noise mask
  6. Clean up the difference image
  7. Compute contours on this image

Let’s see the code:

import cv2
import numpy as np

# Set image path
path = "C://opencvImages//"
fileName = "map.png"

# Read Input image
inputImage = cv2.imread(path+fileName)

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

# Apply Gaussian Blur:
blurredImage = cv2.GaussianBlur(grayscaleImage, (3, 3), cv2.BORDER_DEFAULT)

# Threshold via Otsu:
_, binaryImage = cv2.threshold(blurredImage, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# Save a copy of the binary mask
binaryCopy = cv2.cvtColor(binaryImage, cv2.COLOR_GRAY2BGR)

This is the output:

Up until now you get this binary mask. The process so far has smoothed the noise and is creating thick black blobs where the noise is located. Again, the idea is to generate a noise mask that can be subtracted to this image.

Let’s apply an area filter and try to remove the big white blobs, which are NOT the noise we are interested to preserve. I’ll define the function towards the end, for now I just want to present the general idea:

# Set the minimum pixels for the area filter:
minArea = 50000

# Perform an area filter on the binary blobs:
filteredImage = areaFilter(minArea, binaryImage)

The filter will suppress every white blob that is above the minimum threshold. The value is big because in this particular case we are interested in preserving only the black blobs. This is the result:

We have a pretty solid mask. Let’s subtract this from the original binary mask we created earlier:

# Get the difference between the binary image and the mask:
imgDifference = binaryImage - filteredImage

This is what we get:

The difference image has some small noise. Let’s apply the area filter again to get rid of it. This time with a more traditional threshold value:

# Set the minimum pixels for the area filter:
minArea = 20

# Perform an area filter on the binary blobs:
filteredImage = areaFilter(minArea, imgDifference)

Cool. This is the final mask:

Just for completeness. Let’s compute contours on this input, which is very straightforward:

# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(filteredImage, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)

# Draw the contours on the mask image:
cv2.drawContours(binaryCopy, contours, -1, (0, 255, 0), 3)

Let’s see the result:

As you see it is not perfect. However, there’s still some room for improvement, perhaps you can polish a little bit more this idea to get a potential solution. Here's the definition and implementation of the areaFilter function:

def areaFilter(minArea, inputImage):

    # Perform an area filter on the binary blobs:
    componentsNumber, labeledImage, componentStats, componentCentroids = \
    cv2.connectedComponentsWithStats(inputImage, connectivity=4)

    # Get the indices/labels of the remaining components based on the area stat
    # (skip the background component at index 0)
    remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]

    # Filter the labeled pixels based on the remaining labels,
    # assign pixel intensity to 255 (uint8) for the remaining pixels
    filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')

    return filteredImage

Upvotes: 2

Related Questions