裂缝二维检测:裂缝面积与裂缝个数

本文介绍

本章节的目标是检测裂缝的个数,并计算出每条裂缝相应的面积。

实现思路

这里还是要写成函数的形式,毕竟我们要检测的二维数据有很多,这里我命名detect_crack_areas表示检测裂缝面积,但同时又能返回裂缝的数量。参数可以给一个图片img,除此外,我想要判断是否要对裂缝画出一个边界框,所以要给一个bool值名为Bbox,然后我想要再给一个裂缝合并的距离阈值,毕竟检测的方式是看裂缝是否连通,如果只不过是断开了一个像素点大小,却要再添加一个裂缝个数似乎也没有这个必要。另外有些虽然断开了,但是确实是太小了,咱们也没必要去计算,所以还要有一个裂缝面积过滤的阈值,这个参数用于过滤掉面积小于阈值的裂缝区域。

代码实现

这里我们一步一步的去实现我们上面的思路。

第一步:将BGR图片转化为二值图,并进行闭运算

裂缝合并的距离阈值,这里我们给了3,这个值为我根据CrackForest的图片进行测试得到的。morphology.square()、morphology.rectangle() 或者 morphology.disk()为闭运算中使用的结构元素的一种形式,也是需要大家自己去尝试的,我这里采用了morphology.disk()是在我的数据上表现的比较好。

binary_image = pz.BinaryImg(img)
connected_image = morphology.closing(binary_image, morphology.disk(3))

第二步:标记连通区域和获取区域属性

label这个函数可以用于标记整数数组的连接区域,所以我们需要对图片进行二值化,我并不希望有其他的颜色混入,connectivity指考虑像素之间的连接性,这个可以看API里面是怎么解释的:

1-connectivity     2-connectivity     diagonal connection close-up

     [ ]           [ ]  [ ]  [ ]             [ ]
      |               \  |  /                 |  <- hop 2
[ ]--[x]--[ ]      [ ]--[x]--[ ]        [x]--[ ]
      |               /  |  \             hop 1
     [ ]           [ ]  [ ]  [ ]

很显然,我希望的是八连通的区域。

regionprops返回的是一个列表,其中的每个元素表示一个连通区域的属性。每个元素是一个对象,包含了与该连通区域相关的各种属性信息,例如面积、质心坐标、外接矩形框、周长等。

labeled_image = measure.label(connected_image, connectivity=2)
region_props = measure.regionprops(labeled_image)

第三步:对裂缝进行标记

这里的标记很好理解,简单来说就是我需要知道每天裂缝的信息,那么就需要给它命名,这里我采用了大写的英文字母来表示,我认为一般情况下,横向、纵向、斜裂缝的个数都不会太多,用英文字母来进行命名完全绰绰有余。

area = {}
crack_label = ord('A')

第四步:过滤裂缝面积

for region in region_props:
    area_value = region.area
    if area_value >= 50:

即使裂缝确实是间隔的比较的远,但却又太小了,这样我们也没有必要再认为它是一条裂缝了。

第五步:是否对其进行画框

if Bbox:
    minr, minc, maxr, maxc = region.bbox
    pz.Boxcenter_text(img, [minc, minr, maxc, maxr], Z.green, chr(crack_label), Z.red, 0.7, 2)

在经过面积过滤后,我们就可以通过布尔值Bbox来判断是否要进行标记和话框了。

全文代码

import cv2
import pyzjr as pz
import pyzjr.Z as Z
from skimage import morphology
from skimage import measure

def detect_crack_areas(img, Bbox=True, merge_threshold=3, area_threshold=50):
    """
    检测裂缝区域并计算裂缝面积。
    morphology.square()、morphology.rectangle() 或者 morphology.disk()
    :param img: 输入图像。
    :param Bbox: 是否绘制边界框,默认为 True。
    :param merge_threshold: 裂缝合并的距离阈值,默认为 3。
    :param area_threshold: 裂缝面积过滤的阈值,默认为 50。
    :return: 裂缝面积的字典和裂缝数量。
    """
    binary_image = pz.BinaryImg(img)
    connected_image = morphology.closing(binary_image, morphology.disk(merge_threshold))
    labeled_image = measure.label(connected_image, connectivity=2)
    region_props = measure.regionprops(labeled_image)
    area = {}
    crack_label = ord('A')
    for region in region_props:
        area_value = region.area
        if area_value >= area_threshold:
            if Bbox:
                minr, minc, maxr, maxc = region.bbox
                pz.Boxcenter_text(img, [minc, minr, maxc, maxr], Z.green, chr(crack_label), Z.red, 0.7, 2)
            if crack_label <= ord('Z'):
                area[chr(crack_label)] = area_value
                crack_label += 1
    if Bbox:
        cv2.imshow("Image with Bounding Boxes", img)
        cv2.waitKey(0)

    CrackNum = len(area)
    return area, CrackNum

if __name__=="__main__":
    file_path = r"E:\pythonconda2\dimension2_data\num"
    img_paths = pz.getPhotopath(file_path)
    for img_path in img_paths:
        img = cv2.imread(img_path)
        area, CrackNum = detect_crack_areas(img, False)
        print(f"图片{img_path[-7:]}裂缝个数为{CrackNum},裂缝的面积为:{area}")

这里我们来查看一张图片的标记情况,在上面我们的Bbox是设置为False。

你可以这样修改:

if __name__=="__main__":
    img_path = r"E:\pythonconda2\dimension2_data\num\046.png"
    img = cv2.imread(img_path)
    area, CrackNum = detect_crack_areas(img)
    print(f"裂缝个数为{CrackNum},裂缝的面积为:{area}")

我来说明一下标记A的情况,中间有一点小间隔,但实际经过闭运算后,这条裂缝是连接在一起的,而C裂缝虽然是分开的,但分开部分的面积小于了设定的50,所以也没有算进去。 

猜你喜欢

转载自blog.csdn.net/m0_62919535/article/details/131919958