如何去除二值图像中的噪声?
How to remove noise in binary image?
这是我的代码,我正在尝试从二进制图像中删除掩码(噪声)。我得到的是噪音周围留下的白线。我知道该噪声周围有一个轮廓,在结果中形成了最终的白线。有帮助吗?
原图
面具和结果
代码
import numpy as np
import cv2
from skimage import util
img = cv2.imread('11_otsu.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(img, contours, -1, (0,255,0), 2)
# create an empty mask
mask = np.zeros(img.shape[:2], dtype=np.uint8)
# loop through the contours
for i, cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1:
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) <70:
cv2.drawContours(mask, [cnt], 0, (255), -1)
# display result
cv2.imshow("Mask", mask)
cv2.imshow("Img", img)
image = cv2.bitwise_not(img, img, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", image)
cv2.waitKey()
cv2.destroyAllWindows()
我认为使用 cv2.GaussianBlur()
方法可能会对您有所帮助。将图像转换为灰度后,使用此方法对其进行模糊处理(顾名思义,这是一种高斯滤波器)。这是文档:
https://docs.opencv.org/4.3.0/d4/d86/group__imgproc__filter.html
我可以建议使用 cv2.floodFill
,而不是试图找到内部轮廓并填充它们吗?洪水填充操作通常用于填充封闭对象内部的孔洞。具体来说,如果您将种子像素设置为图像的左上角,然后对图像进行泛光填充,则将填充的是背景,而封闭的对象则保持不变。如果反转此图像,您会发现所有位于具有“孔”的封闭对象内部的像素。如果你把这个倒置的图像,用非零位置直接设置原图,你就会填补空洞。
因此:
im = cv2.imread('8AdUp.png', 0)
h, w = im.shape[:2]
mask = np.zeros((h+2, w+2), dtype=np.uint8)
holes = cv2.floodFill(im.copy(), mask, (0, 0), 255)[1]
holes = ~holes
im[holes == 255] = 255
cv2.imshow('Holes Filled', im)
cv2.waitKey(0)
cv2.destroyAllWindows()
首先我们读入您提供的经过阈值处理且“噪声过滤”之前的图像,然后获取它的高度和宽度。我们还使用输入掩码来告诉我们哪些像素要对洪水填充进行操作。使用全零蒙版意味着您将对整个图像进行操作。同样重要的是要注意,在使用遮罩之前,遮罩周围需要有一个 1 像素的边框。我们使用左上角作为初始点对图像进行洪水填充,将其反转,将任何“孔”像素设置为 255 并显示它。请注意,一旦该方法完成,输入图像就会发生变化,因此您需要传入一个副本以保持输入图像不变。此外,cv2.floodFill
(使用 OpenCV 4)returns 一个包含四个元素的元组。我会让您查看文档,但您需要此元组的第二个元素,即填充的图像。
我们因此得到:
您的代码完全没问题,只要进行这些调整,它就可以工作了:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) # Use cv2.CCOMP for two level hierarchy
if hierarchy[0][i][3] != -1: # basically look for holes
# if the size of the contour is less than a threshold (noise)
if cv2.contourArea(cnt) < 70:
# Fill the holes in the original image
cv2.drawContours(img, [cnt], 0, (255), -1)
这是我的代码,我正在尝试从二进制图像中删除掩码(噪声)。我得到的是噪音周围留下的白线。我知道该噪声周围有一个轮廓,在结果中形成了最终的白线。有帮助吗?
原图
面具和结果
代码
import numpy as np
import cv2
from skimage import util
img = cv2.imread('11_otsu.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(img, contours, -1, (0,255,0), 2)
# create an empty mask
mask = np.zeros(img.shape[:2], dtype=np.uint8)
# loop through the contours
for i, cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1:
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) <70:
cv2.drawContours(mask, [cnt], 0, (255), -1)
# display result
cv2.imshow("Mask", mask)
cv2.imshow("Img", img)
image = cv2.bitwise_not(img, img, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", image)
cv2.waitKey()
cv2.destroyAllWindows()
我认为使用 cv2.GaussianBlur()
方法可能会对您有所帮助。将图像转换为灰度后,使用此方法对其进行模糊处理(顾名思义,这是一种高斯滤波器)。这是文档:
https://docs.opencv.org/4.3.0/d4/d86/group__imgproc__filter.html
我可以建议使用 cv2.floodFill
,而不是试图找到内部轮廓并填充它们吗?洪水填充操作通常用于填充封闭对象内部的孔洞。具体来说,如果您将种子像素设置为图像的左上角,然后对图像进行泛光填充,则将填充的是背景,而封闭的对象则保持不变。如果反转此图像,您会发现所有位于具有“孔”的封闭对象内部的像素。如果你把这个倒置的图像,用非零位置直接设置原图,你就会填补空洞。
因此:
im = cv2.imread('8AdUp.png', 0)
h, w = im.shape[:2]
mask = np.zeros((h+2, w+2), dtype=np.uint8)
holes = cv2.floodFill(im.copy(), mask, (0, 0), 255)[1]
holes = ~holes
im[holes == 255] = 255
cv2.imshow('Holes Filled', im)
cv2.waitKey(0)
cv2.destroyAllWindows()
首先我们读入您提供的经过阈值处理且“噪声过滤”之前的图像,然后获取它的高度和宽度。我们还使用输入掩码来告诉我们哪些像素要对洪水填充进行操作。使用全零蒙版意味着您将对整个图像进行操作。同样重要的是要注意,在使用遮罩之前,遮罩周围需要有一个 1 像素的边框。我们使用左上角作为初始点对图像进行洪水填充,将其反转,将任何“孔”像素设置为 255 并显示它。请注意,一旦该方法完成,输入图像就会发生变化,因此您需要传入一个副本以保持输入图像不变。此外,cv2.floodFill
(使用 OpenCV 4)returns 一个包含四个元素的元组。我会让您查看文档,但您需要此元组的第二个元素,即填充的图像。
我们因此得到:
您的代码完全没问题,只要进行这些调整,它就可以工作了:
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) # Use cv2.CCOMP for two level hierarchy
if hierarchy[0][i][3] != -1: # basically look for holes
# if the size of the contour is less than a threshold (noise)
if cv2.contourArea(cnt) < 70:
# Fill the holes in the original image
cv2.drawContours(img, [cnt], 0, (255), -1)