如何连接边缘的末端以关闭它们之间的孔?
How to connect the ends of edges in order to close the holes between them?
我的任务是检测土壤表面的裂缝并计算裂缝的总面积。为此,我使用了 Canny 边缘检测。
输入图片
结果
我的下一步是将精巧的边缘转换为轮廓,因为我想使用 cv2.mean
过滤裂缝并使用 cv2.contourArea
函数计算它们的面积。在这一步,我遇到了问题。当我使用时:
canny_cracks = cv2.Canny(gray, 100, 200)
contours, _ = cv2.findContours(canny_cracks, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
由于边缘的末端有孔,因此无法正确转换。在这里查看问题
我的问题是如何连接边缘的末端以闭合它们之间的孔?
注意:我使用了轮廓检测,但没有应用 Canny 边缘。问题在于轮廓检测会产生大量噪音并且无法很好地检测到所有裂缝。或者我不知道如何像精明的边缘那样找到轮廓。
您可以使用 Morphological Close。这会缩小白色像素之间的间隙。如果您在下面的脚本中输入了您的 Canny 图片,您可以自己尝试一下。
结果:
代码:
import cv2
import numpy as np
# function that handles trackbar changes
def doClose(val):
# create a kernel based on trackbar input
kernel = np.ones((val,val))
# do a morphologic close
res = cv2.morphologyEx(img,cv2.MORPH_CLOSE, kernel)
# display result
cv2.imshow("Result", res)
#load image as grayscale
img = cv2.imread("KbMHp.png",0)
# create window and add trackbar
cv2.namedWindow('Result')
cv2.createTrackbar('KernelSize','Result',0,15,doClose)
# display image
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
从您提供的第二张图片开始,这是我解决此问题的方法:
- 高斯模糊图像并转换为灰度
- 从花盆中分离土壤
- 只创建土壤的圆形蒙版
- 提取土壤 ROI
- 执行形态变换以关闭孔洞
- 查找轮廓并按轮廓区域过滤
- 面积求和得到结果
我们首先进行高斯模糊并将图像转换为灰度。
image = cv2.imread('5.png')
original = image.copy()
blur = cv2.GaussianBlur(image, (3,3), 0)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
目标是将土壤边缘与花盆边缘隔离开来。为此,我们使用 cv2.HoughCircles()
找到花盆的外圈,缩小圆圈以抓住土壤区域,并使用原始图像的形状创建蒙版。
circle_mask = np.zeros(original.shape, dtype=np.uint8)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 200)
# Convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
circle_ratio = 0.85
# Loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# Draw the circle, create mask, and obtain soil ROI
cv2.circle(image, (x, y), int(r * circle_ratio), (0, 255, 0), 2)
cv2.circle(circle_mask, (x, y), int(r * circle_ratio), (255, 255, 255), -1)
soil_ROI = cv2.bitwise_and(original, circle_mask)
我们遍历坐标以找到圆的半径。从这里我们画出最大的外圈。
现在为了分离土壤和花盆,我们应用比例因子来获得这个
接下来,我们填充圆圈以获得掩码,然后将其应用于原始图像以获得土壤ROI。
土壤面膜
土壤投资回报率
你的问题是
How can I connect the ends of edges in order to close the hole between them?
为此,您可以执行 morphological transformation
使用 cv2.morphologyEx()
来关闭孔,从而导致此
gray_soil_ROI = cv2.cvtColor(soil_ROI, cv2.COLOR_BGR2GRAY)
close = cv2.morphologyEx(gray_soil_ROI, cv2.MORPH_CLOSE, kernel)
现在我们使用 cv2.findContours()
找到轮廓,并使用 cv2.contourArea()
以最小阈值区域进行过滤,以去除岩石等小噪声。您可以调整最小区域以控制过滤强度。
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
crack_area = 0
minumum_area = 25
for c in cnts:
area = cv2.contourArea(c)
if area > minumum_area:
cv2.drawContours(original,[c], 0, (36,255,12), 2)
crack_area += area
最后,我们对面积求和,得到裂缝的总面积
3483.5
import cv2
import numpy as np
image = cv2.imread('5.png')
original = image.copy()
blur = cv2.GaussianBlur(image, (3,3), 0)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
circle_mask = np.zeros(original.shape, dtype=np.uint8)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 200)
# Convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
circle_ratio = 0.85
# Loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# Draw the circle, create mask, and obtain soil ROI
cv2.circle(image, (x, y), int(r * circle_ratio), (0, 255, 0), 2)
cv2.circle(circle_mask, (x, y), int(r * circle_ratio), (255, 255, 255), -1)
soil_ROI = cv2.bitwise_and(original, circle_mask)
gray_soil_ROI = cv2.cvtColor(soil_ROI, cv2.COLOR_BGR2GRAY)
close = cv2.morphologyEx(gray_soil_ROI, cv2.MORPH_CLOSE, kernel)
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
crack_area = 0
minumum_area = 25
for c in cnts:
area = cv2.contourArea(c)
if area > minumum_area:
cv2.drawContours(original,[c], 0, (36,255,12), 2)
crack_area += area
print(crack_area)
cv2.imshow('close', close)
cv2.imshow('circle_mask', circle_mask)
cv2.imshow('soil_ROI', soil_ROI)
cv2.imshow('original', original)
cv2.waitKey(0)
我的任务是检测土壤表面的裂缝并计算裂缝的总面积。为此,我使用了 Canny 边缘检测。
输入图片
结果
我的下一步是将精巧的边缘转换为轮廓,因为我想使用 cv2.mean
过滤裂缝并使用 cv2.contourArea
函数计算它们的面积。在这一步,我遇到了问题。当我使用时:
canny_cracks = cv2.Canny(gray, 100, 200)
contours, _ = cv2.findContours(canny_cracks, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
由于边缘的末端有孔,因此无法正确转换。在这里查看问题
我的问题是如何连接边缘的末端以闭合它们之间的孔?
注意:我使用了轮廓检测,但没有应用 Canny 边缘。问题在于轮廓检测会产生大量噪音并且无法很好地检测到所有裂缝。或者我不知道如何像精明的边缘那样找到轮廓。
您可以使用 Morphological Close。这会缩小白色像素之间的间隙。如果您在下面的脚本中输入了您的 Canny 图片,您可以自己尝试一下。
结果:
代码:
import cv2
import numpy as np
# function that handles trackbar changes
def doClose(val):
# create a kernel based on trackbar input
kernel = np.ones((val,val))
# do a morphologic close
res = cv2.morphologyEx(img,cv2.MORPH_CLOSE, kernel)
# display result
cv2.imshow("Result", res)
#load image as grayscale
img = cv2.imread("KbMHp.png",0)
# create window and add trackbar
cv2.namedWindow('Result')
cv2.createTrackbar('KernelSize','Result',0,15,doClose)
# display image
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
从您提供的第二张图片开始,这是我解决此问题的方法:
- 高斯模糊图像并转换为灰度
- 从花盆中分离土壤
- 只创建土壤的圆形蒙版
- 提取土壤 ROI
- 执行形态变换以关闭孔洞
- 查找轮廓并按轮廓区域过滤
- 面积求和得到结果
我们首先进行高斯模糊并将图像转换为灰度。
image = cv2.imread('5.png')
original = image.copy()
blur = cv2.GaussianBlur(image, (3,3), 0)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
目标是将土壤边缘与花盆边缘隔离开来。为此,我们使用 cv2.HoughCircles()
找到花盆的外圈,缩小圆圈以抓住土壤区域,并使用原始图像的形状创建蒙版。
circle_mask = np.zeros(original.shape, dtype=np.uint8)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 200)
# Convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
circle_ratio = 0.85
# Loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# Draw the circle, create mask, and obtain soil ROI
cv2.circle(image, (x, y), int(r * circle_ratio), (0, 255, 0), 2)
cv2.circle(circle_mask, (x, y), int(r * circle_ratio), (255, 255, 255), -1)
soil_ROI = cv2.bitwise_and(original, circle_mask)
我们遍历坐标以找到圆的半径。从这里我们画出最大的外圈。
现在为了分离土壤和花盆,我们应用比例因子来获得这个
接下来,我们填充圆圈以获得掩码,然后将其应用于原始图像以获得土壤ROI。
土壤面膜
土壤投资回报率
你的问题是
How can I connect the ends of edges in order to close the hole between them?
为此,您可以执行 morphological transformation
使用 cv2.morphologyEx()
来关闭孔,从而导致此
gray_soil_ROI = cv2.cvtColor(soil_ROI, cv2.COLOR_BGR2GRAY)
close = cv2.morphologyEx(gray_soil_ROI, cv2.MORPH_CLOSE, kernel)
现在我们使用 cv2.findContours()
找到轮廓,并使用 cv2.contourArea()
以最小阈值区域进行过滤,以去除岩石等小噪声。您可以调整最小区域以控制过滤强度。
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
crack_area = 0
minumum_area = 25
for c in cnts:
area = cv2.contourArea(c)
if area > minumum_area:
cv2.drawContours(original,[c], 0, (36,255,12), 2)
crack_area += area
最后,我们对面积求和,得到裂缝的总面积
3483.5
import cv2
import numpy as np
image = cv2.imread('5.png')
original = image.copy()
blur = cv2.GaussianBlur(image, (3,3), 0)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
circle_mask = np.zeros(original.shape, dtype=np.uint8)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.5, 200)
# Convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
circle_ratio = 0.85
# Loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# Draw the circle, create mask, and obtain soil ROI
cv2.circle(image, (x, y), int(r * circle_ratio), (0, 255, 0), 2)
cv2.circle(circle_mask, (x, y), int(r * circle_ratio), (255, 255, 255), -1)
soil_ROI = cv2.bitwise_and(original, circle_mask)
gray_soil_ROI = cv2.cvtColor(soil_ROI, cv2.COLOR_BGR2GRAY)
close = cv2.morphologyEx(gray_soil_ROI, cv2.MORPH_CLOSE, kernel)
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
crack_area = 0
minumum_area = 25
for c in cnts:
area = cv2.contourArea(c)
if area > minumum_area:
cv2.drawContours(original,[c], 0, (36,255,12), 2)
crack_area += area
print(crack_area)
cv2.imshow('close', close)
cv2.imshow('circle_mask', circle_mask)
cv2.imshow('soil_ROI', soil_ROI)
cv2.imshow('original', original)
cv2.waitKey(0)