我猜你正试图获得墙壁的contours,对吧?这是主要使用空间过滤的解决方案的可能路径。您仍然需要清理结果才能到达您想要的位置。这个想法是尝试计算图像的平行线(高频噪声)的掩码,并计算(二进制)输入与此掩码之间的差异。这些是步骤:
- 将输入图像转换为灰度
- 应用高斯模糊来消除您试图消除的高频噪声
- 获取模糊图像的二值图像
- 应用区域过滤器去除所有非噪声,以获得噪声掩码
- 计算原始二进制掩码和噪声掩码之间的差异
- 清理差异图像
- 计算此图像的轮廓
让我们看看代码:
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)
这是输出:
到目前为止,您已经得到了这个二进制掩码。到目前为止,该过程已经平滑了噪声,并在噪声所在的位置产生了厚厚的黑色斑点。同样,这个想法是生成一个可以减去该图像的噪声掩码。
让我们应用area filter 并尝试移除大的白色斑点,它们不是我们有兴趣保留的噪声。我将在最后定义函数,现在我只想介绍一个总体思路:
# Set the minimum pixels for the area filter:
minArea = 50000
# Perform an area filter on the binary blobs:
filteredImage = areaFilter(minArea, binaryImage)
过滤器将抑制高于最小阈值的每个白色斑点。值很大,因为在这种特殊情况下,我们只对保留黑色斑点感兴趣。结果如下:
我们有一个非常坚固的面具。让我们从我们之前创建的原始二进制掩码中减去它:
# Get the difference between the binary image and the mask:
imgDifference = binaryImage - filteredImage
这是我们得到的:
差异图像有一些小噪点。让我们再次应用area filter 来摆脱它。这次使用更传统的阈值:
# Set the minimum pixels for the area filter:
minArea = 20
# Perform an area filter on the binary blobs:
filteredImage = areaFilter(minArea, imgDifference)
酷。这是最终的面具:
只是为了完整性。让我们在这个输入上计算contours,这非常简单:
# 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)
让我们看看结果:
如您所见,它并不完美。但是,还有一些改进的空间,也许您可以对这个想法进行更多的改进以获得潜在的解决方案。下面是areaFilter函数的定义和实现:
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