• 直方图投影法判断裂缝走势(裂缝类型)


    裂缝类型

    裂缝类型有很多种,这里我们仅仅判断线性裂缝与网状裂缝,线性裂缝按照其走势有可分为横向裂缝、纵向裂缝和斜向裂缝。

    我觉得大家应当有这样的意识,面对网状裂缝,它的二维参数是否有意义?答案是没有!如果检测到网状裂缝,我想大家的第一反应是比较严重了,需要修补了。如果是一条线性裂缝呢?我是不是还有考虑一下它的受损程度是否达到需要修补的地步。

    所以按照我的想法,可以求网状裂缝的面积,评估其受损程度,求线性裂缝的面积、长度和宽度,评估其受损程度。

    直方图投影法

    网状裂缝的直方图投影 

    横向裂缝的直方图投影 

    纵向裂缝的直方图投影 

    斜向裂缝的直方图投影 

    上面四张图是四种裂缝对应的直方图,结合上面的一些特点,我们可以依照自己的数据集进行类型分类。

     获取最小外接矩形信息

    接下来,get_minAreaRect_information函数会从二值化掩膜图像中提取最小外接矩形的相关信息,包括中心点坐标、宽高和旋转角度。inference_minAreaRect函数用于计算最小外接矩形框的宽、高和角度信息,并将角度转换为相对于图像水平方向的夹角。

    1. def inference_minAreaRect(minAreaRect):
    2. w, h = minAreaRect[1]
    3. if w > h:
    4. angle = int(minAreaRect[2])
    5. else:
    6. angle = -(90 - int(minAreaRect[2]))
    7. return w, h, angle
    8. def _get_minAreaRect_information(mask):
    9. mask = pz.BinaryImg(mask)
    10. contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    11. contour_merge = np.vstack(contours)
    12. minAreaRect = cv2.minAreaRect(contour_merge)
    13. return minAreaRect

    pz.BinaryImg获取图像二值图,请确保读取时候为BGR的图片。 

    初始化分类裂缝的参数

    创建了一个ClassificationCrack类,并且对裂缝的分类参数进行初始化,有分类裂缝的阈值threshold,分类裂缝的高宽比阈值HWration,用于分类裂缝的直方图比例阈值Histration。

    1. class CrackType():
    2. """直方图投影法推断裂缝类型"""
    3. def __init__(self, threshold=3, HWratio=10, Histratio=0.5):
    4. """
    5. 初始化分类裂缝的参数
    6. :param threshold: 阈值,用于分类裂缝的阈值
    7. :param HWratio: 高宽比,用于分类裂缝的高宽比阈值
    8. :param Histratio: 直方图比例,用于分类裂缝的直方图比例阈值
    9. """
    10. self.threshold = threshold
    11. self.HWratio = HWratio
    12. self.Histratio = Histratio
    13. self.types = {0: 'Horizontal',
    14. 1: 'Vertical',
    15. 2: 'Oblique',
    16. 3: 'Mesh'}

    这里我们使用字典self.types,这样就可以通过键值对判断裂缝的类型了。

    骨骼点投影直方图

    在ClassificationCrack类下,我们再定义了一个hist_judge的方法,less_than_T统计直方图中大于 0 且小于等于阈值 self.threshold 的像素数量,more_than_T统计直方图中大于阈值 self.threshold 的像素数量。通过more_than_T / (less_than_T + 1e-5)来比较是否超过了直方图比例阈值。

    1. def hist_judge(self, hist_v):
    2. less_than_T = np.count_nonzero((hist_v > 0) & (hist_v <= self.threshold))
    3. more_than_T = np.count_nonzero(hist_v > self.threshold)
    4. return more_than_T / (less_than_T + 1e-5) > self.Histratio

    裂缝分类

    classify 方法是 ClassificationCrack 类中的另一个成员方法,它接收三个值,minAreaRect 是一个元组,表示最小外接矩形框的信息,包括中心点坐标、宽高和旋转角度;skeleton_pts是一个数组,表示骨骼点的坐标;HW是当前 patch 的高和宽。

    1. def classify(self, minAreaRect, skeleton_pts, HW):
    2. H, W = HW
    3. w, h, angle = inference_minAreaRect(minAreaRect)
    4. if w / h < self.HWratio or h / w < self.HWratio:
    5. pts_y, pts_x = skeleton_pts[:, 0], skeleton_pts[:, 1]
    6. hist_x = np.histogram(pts_x, W)
    7. hist_y = np.histogram(pts_y, H)
    8. if self.hist_judge(hist_x[0]) and self.hist_judge(hist_y[0]):
    9. return 3
    10. return self.angle2cls(angle)
    11. @staticmethod
    12. def angle2cls(angle):
    13. angle = abs(angle)
    14. assert 0 <= angle <= 90, "ERROR: The angle value exceeds the limit and should be between 0 and 90 degrees!"
    15. if angle < 35:
    16. return 0
    17. elif 35 <= angle <= 55:
    18. return 2
    19. elif angle > 55:
    20. return 1
    21. else:
    22. return None

    利用 inference_minAreaRect 函数从 minAreaRect 中获取旋转矩形框的宽度 w、高度 h 和角度 angle。接下来,通过判断 w / h 和 h / w 是否小于 self.HWratio 来判断旋转矩形框的长宽比是否满足分类条件。

    如果长宽比满足条件,则将 skeleton_pts 按照 x 和 y 方向投影到直方图 hist_x 和 hist_y,然后通过 self.hist_judge 方法判断这两个直方图是否满足分类条件。

    以上条件均满足,则会认为是网状裂缝,否则就使用angle2cls来进行角度分类。

    根据角度的大小将裂缝分为以下三类:

    • 如果角度小于 35 度,则返回 0,表示水平裂缝。
    • 如果角度在 35 到 55 度之间,则返回 2,表示倾斜裂缝。
    • 如果角度大于 55 度,则返回 1,表示垂直裂缝。
    • 如果角度不在上述范围内,则返回 None 。

    测试文件main

    1. """
    2. 裂缝分类如何判断
    3. 横向、纵向、网状、斜裂缝
    4. """
    5. import os
    6. import matplotlib.pyplot as plt
    7. import numpy as np
    8. import cv2
    9. import pyzjr as pz
    10. from skimage.morphology import skeletonize
    11. from skimage.filters import threshold_otsu
    12. from skimage.color import rgb2gray
    13. class CrackType():
    14. """直方图投影法推断裂缝类型"""
    15. def __init__(self, threshold=3, HWratio=10, Histratio=0.5):
    16. """
    17. 初始化分类裂缝的参数
    18. :param threshold: 阈值,用于分类裂缝的阈值
    19. :param HWratio: 高宽比,用于分类裂缝的高宽比阈值
    20. :param Histratio: 直方图比例,用于分类裂缝的直方图比例阈值
    21. """
    22. self.threshold = threshold
    23. self.HWratio = HWratio
    24. self.Histratio = Histratio
    25. self.types = {0: 'Horizontal',
    26. 1: 'Vertical',
    27. 2: 'Oblique',
    28. 3: 'Mesh'}
    29. def inference_minAreaRect(self, minAreaRect):
    30. """
    31. 旋转矩形框长边与x轴的夹角.
    32. 旋转角度 angle 是相对于图像水平方向的夹角,范围是 -90 到 +90 度.
    33. 然而,一般情况下,我们习惯将角度定义为相对于 x 轴正方向的夹角,范围是 -180 到 +180 度.
    34. """
    35. w, h = minAreaRect[1]
    36. if w > h:
    37. angle = int(minAreaRect[2])
    38. else:
    39. angle = -(90 - int(minAreaRect[2]))
    40. return w, h, angle
    41. def classify(self, minAreaRect, skeleton_pts, HW):
    42. """
    43. 针对当前crack instance,对其进行分类;
    44. 主要利用了骨骼点双向投影直方图、旋转矩形框宽高比/角度;
    45. :param minAreaRect: 最小外接矩形框,[(cx, cy), (w, h), angle];
    46. :param skeleton_pts: 骨骼点坐标;
    47. :param HW: 当前patch的高、宽;
    48. """
    49. H, W = HW
    50. w, h, angle = self.inference_minAreaRect(minAreaRect)
    51. if w / h < self.HWratio or h / w < self.HWratio:
    52. pts_y, pts_x = skeleton_pts[:, 0], skeleton_pts[:, 1]
    53. hist_x = np.histogram(pts_x, W)
    54. hist_y = np.histogram(pts_y, H)
    55. if self.hist_judge(hist_x[0]) and self.hist_judge(hist_y[0]):
    56. return 3
    57. return self.angle2cls(angle)
    58. def hist_judge(self, hist_v):
    59. less_than_T = np.count_nonzero((hist_v > 0) & (hist_v <= self.threshold))
    60. more_than_T = np.count_nonzero(hist_v > self.threshold)
    61. return more_than_T / (less_than_T + 1e-5) > self.Histratio
    62. @staticmethod
    63. def angle2cls(angle):
    64. angle = abs(angle)
    65. assert 0 <= angle <= 90, "ERROR: The angle value exceeds the limit and should be between 0 and 90 degrees!"
    66. if angle < 35:
    67. return 0
    68. elif 35 <= angle <= 55:
    69. return 2
    70. elif angle > 55:
    71. return 1
    72. else:
    73. return None
    74. def _get_minAreaRect_information(mask):
    75. """
    76. 从二值化掩膜图像中获取最小外接矩形的相关信息
    77. :param mask:二值化掩膜图像,包含目标区域的白色区域
    78. :return:最小外接矩形的信息,包括中心点坐标、宽高和旋转角度
    79. """
    80. mask = pz.BinaryImg(mask)
    81. contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    82. contour_merge = np.vstack(contours)
    83. minAreaRect = cv2.minAreaRect(contour_merge)
    84. return minAreaRect
    85. def SkeletonMap(target):
    86. """
    87. 获取骨架图的信息
    88. :param target: 目标图
    89. :return: 骨架图与一个数组,其中每一行表示一个非零元素的索引(y,x),包括行索引和列索引
    90. """
    91. gray = rgb2gray(target)
    92. thresh = threshold_otsu(gray)
    93. binary = gray > thresh
    94. skimage = skeletonize(binary)
    95. skepoints = np.argwhere(skimage)
    96. skimage = skimage.astype(np.uint8)
    97. return skimage, skepoints
    98. if __name__ == '__main__':
    99. plt.switch_backend('TkAgg')
    100. masks_dir = r"D:\PythonProject\RoadCrack\dimension2_data\num" # 这里改为存放上面图片的路径
    101. results_save_dir = "A_results"
    102. os.makedirs(results_save_dir, exist_ok=True)
    103. classifier = CrackType()
    104. imgfile,_ = pz.getPhotopath(masks_dir, debug=False)
    105. for path in imgfile:
    106. mask = cv2.imread(path)
    107. H, W = mask.shape[:2]
    108. mask_copy = mask.copy()
    109. skeimage, skepoints = SkeletonMap(mask_copy)
    110. minAreaRect=_get_minAreaRect_information(mask)
    111. pts_y, pts_x = skepoints[:, 0], skepoints[:, 1]
    112. hist_x = np.histogram(pts_x, W)
    113. hist_y = np.histogram(pts_y, H)
    114. result = classifier.classify(minAreaRect, skepoints, HW=(H, W))
    115. crack_type = classifier.types[result]
    116. print(crack_type)
    117. T = classifier.threshold
    118. plt.figure(figsize=(10, 5))
    119. plt.subplot(121)
    120. plt.plot(hist_x[1][:-1], [T] * len(hist_x[0]), 'r')
    121. plt.bar(hist_x[1][:-1], hist_x[0])
    122. plt.title("Histogram X")
    123. plt.subplot(122)
    124. plt.plot(hist_y[1][:-1], [T] * len(hist_y[0]), 'r')
    125. plt.bar(hist_y[1][:-1], hist_y[0])
    126. plt.title("Histogram Y")
    127. plt.tight_layout() # 自动调整子图布局,防止重叠
    128. plt.show()

    与我们实际图片进行对比,其检测效果均还不错,threshold,HWratio,Histratio这三个初始值均为经验所得,还是要依照自己的数据来设定。这里的SkeletionMap函数将会获得骨架图中的索引点,它并没有进行去消除毛刺的,实际并不影响,因为我们采用的这个方法,些许毛刺影响不了判断。

    现在我们只需要写一个推动裂缝类型的函数,可以用于直接去判断我们设定的裂缝类型:

    1. def infertype(mask):
    2. """推导裂缝类型"""
    3. crack = CrackType()
    4. H, W = mask.shape[:2]
    5. mask_copy = mask.copy()
    6. skeimage, skepoints = SkeletonMap(mask_copy)
    7. minAreaRect = _get_minAreaRect_information(mask)
    8. result = crack.classify(minAreaRect, skepoints, HW=(H, W))
    9. crack_type = crack.types[result]
    10. return result, crack_type
  • 相关阅读:
    VScode如何在敲代码时自动导入包
    django 项目基本配置
    【图卷积神经网络】1-入门篇:为什么使用图神经网络(下)
    (附源码)springboorCRM客户关系管理系统 毕业设计 316631
    2024在职考研|MBA/MPA/MEM管理类硕士报考流程及基础问题扫盲
    服务状态巡检:
    【韩国】UE5的MetaHuman确实可以导入Blender进行编辑。
    C++动态内存分配
    二进制中1的个数 C++实现
    2023年天津仁爱学院高职升本科专业考试报考须知
  • 原文地址:https://blog.csdn.net/m0_62919535/article/details/133365918