如何连接边缘的末端以封闭它们之间的孔?

Posted

技术标签:

【中文标题】如何连接边缘的末端以封闭它们之间的孔?【英文标题】:How to connect the ends of edges in order to close the holes between them? 【发布时间】:2019-11-07 07:35:08 【问题描述】:

我的任务是检测土壤表面的裂缝并计算裂缝的总面积。为此,我使用了 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 Transformations连接线路 由于裂缝比土壤颜色深,您也可以查看Thresholding 或color based separation,尽管它可能无法很好地推广到其他图像或其他类型的土壤。 感谢您的回复。我使用了基于颜色的分离,它会产生很多不易处理的噪点。关于形态变换,你的意思是扩张吗? 【参考方案1】:

您可以使用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()  

【讨论】:

【参考方案2】:

从您提供的第二张图片开始,这是我解决此问题的方法:

高斯模糊图像并转换为灰度 从盆中分离土壤 仅创建土壤的圆形蒙版 提取土壤 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。

土壤面膜

土壤投资回报率

你的问题是

如何连接边缘的末端以封闭它们之间的孔?

为此,您可以使用cv2.morphologyEx() 执行morphological transformation 来关闭导致此问题的孔

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)

【讨论】:

以上是关于如何连接边缘的末端以封闭它们之间的孔?的主要内容,如果未能解决你的问题,请参考以下文章

使用OpenCV(Python)找到具有最大封闭区域的轮廓

膨胀腐蚀开闭

连接节点以最大化总边权重

仅连接一个三角形的边缘的轮廓检测(几何着色器)

如何提高使用ODBC连接MYSQL数据库的效率?

如何在 MATLAB 中连接元胞数组中的字符串,它们之间有空格?