使用传统方法进行裂缝分类

0、前言

在进行构件破损检测时,我们通过语义分割得到很多各裂缝的实例。那么,如何判断每个裂缝是属于横向裂缝、纵向裂缝、斜向裂缝还是网状裂缝呢?本文提供了一个解决方案。

1、方法

主要利用分割得到的mask的骨架线,对其进行X、Y两个方向的投影,得到两个投影直方图,然后根据投影情况进行判断,即可判断其类型;代码如下:

"""
传统方法进行裂缝分类
"""
import numpy as np


def parse_minAreaRect(rect):
    """
    旋转矩形框长边与x轴的夹角,在第一象限时,角度为-90°~0°,在第二象限时,角度为0°~90°;
    """
    w, h = rect[1]
    if w > h:
        angle = int(rect[2])
    else:
        angle = -(90 - int(rect[2]))
    return w, h, angle


class CrackClassifier(object):
    def __init__(self, Thres_num=3, Thres_hist_ratio=0.5, Thres_hw_ratio=10):
        self.classes = {0: 'horizontal', 1: 'vertical', 2: 'oblique', 3: 'mesh'}
        self.Thres_num = Thres_num
        self.Thres_hist_ratio = Thres_hist_ratio
        self.Thres_hw_ratio = Thres_hw_ratio

    def classify(self, minAreaRect, skeleton_pts, HW):
        """ 针对当前crack instance,对其进行分类;主要利用了骨骼点双向投影直方图、旋转矩形框宽高比/角度;
        minAreaRect: 最小外接矩形框,[(cx, cy), (w, h), angle];
        skeleton_pts: 骨骼点坐标;
        HW: 当前patch的高、宽;
        """
        H, W = HW
        w, h, angle = parse_minAreaRect(minAreaRect)
        if w / h < self.Thres_hw_ratio or h / w < self.Thres_hw_ratio:
            pts_y, pts_x = skeleton_pts[:, 0], skeleton_pts[:, 1]
            hist_x = np.histogram(pts_x, W)
            hist_y = np.histogram(pts_y, H)
            if self.hist_judge(hist_x[0]) and self.hist_judge(hist_y[0]):
                return 3

        return self.angle2cls(angle)

    def hist_judge(self, hist_v):
        less_than_T = list((hist_v > 0) & (hist_v <= self.Thres_num)).count(True)
        more_than_T = list(hist_v > self.Thres_num).count(True)
        return more_than_T / (less_than_T + 1e-5) > self.Thres_hist_ratio

    @staticmethod
    def angle2cls(angle):
        angle = abs(angle)
        assert 0 <= angle <= 90, "ERROR: angle should be in 0~90!"
        if angle < 35:
            return 0
        elif 35 <= angle <= 55:
            return 2
        elif angle > 55:
            return 1
        else:
            return None

2、实验验证

将上面的代码保存为一个文件:crack_classify.py;然后,再写一个测试代码,利用几张图进行测试。

用到的图片如下(保存到一个文件夹中):

使用传统方法进行裂缝分类_第1张图片使用传统方法进行裂缝分类_第2张图片使用传统方法进行裂缝分类_第3张图片

 测试代码如下:

import os
import time

import matplotlib.pyplot as plt
import numpy as np
import cv2

from crack_classify import CrackClassifier


def get_skeleton(blobs):
    """
    骨骼点提取
    """
    skeleton = skeletonize(blobs)  # ndarray, 为TRUE的元素代表骨骼线的位置
    skeleton_pts = np.argwhere(skeleton)
    return skeleton, skeleton_pts



def parse_minAreaRect(rect):
    """
    旋转矩形框长边与x轴的夹角,在第一象限时,角度为-90°~0°,在第二象限时,角度为0°~90°;
    """
    w, h = rect[1]
    if w > h:
        angle = int(rect[2])
    else:
        angle = -(90 - int(rect[2]))
    return w, h, angle


if __name__ == '__main__':
    masks_dir = "path_to_imgs"  # 这里改为存放上面图片的路径
    results_save_dir = "results"
    os.makedirs(results_save_dir, exist_ok=True)

    classifier = CrackClassifier()

    for mask_file in os.listdir(masks_dir):
        mask_path = os.path.join(masks_dir, mask_file)
        mask = cv2.imread(mask_path)
        H, W, C = mask.shape
        mask_copy = mask.copy()
        mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
        ret, mask = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
        blob = mask.copy()
        blob[blob > 0] = 1

        # 获取裂缝骨架坐标点
        skeleton, skeleton_pts = get_skeleton(blob)

        # 获取裂缝的最小外接矩形框的宽、高、倾斜角
        contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contour_merge = np.vstack(contours)
        minAreaRect = cv2.minAreaRect(contour_merge)
        t0 = time.time()
        w, h, angle = parse_minAreaRect(minAreaRect)
        print(w, h, angle)

        # 根据mask外接矩形框以及骨架线判断裂缝类型
        pts_y, pts_x = skeleton_pts[:, 0], skeleton_pts[:, 1]
        hist_x = np.histogram(pts_x, W)
        hist_y = np.histogram(pts_y, H)
        print("time cost: ", time.time()-t0)

        result = classifier.classify(minAreaRect, skeleton_pts, HW=(H, W))
        crack_type = classifier.classes[result]
        print(crack_type)

        # plot
        T = classifier.Thres_num
        plt.figure()
        plt.subplot(222)
        plt.plot(hist_x[1][:-1], [T]*len(hist_x[0]), 'r')
        plt.bar(hist_x[1][:-1], hist_x[0])
        plt.subplot(224)
        plt.plot(hist_y[1][:-1], [T] * len(hist_y[0]), 'r')
        plt.bar(hist_y[1][:-1], hist_y[0])
        plt.subplot(121)
        plt.imshow(mask_copy)
        plt.title("crack type: {}".format(crack_type))
        plt.savefig(os.path.join(results_save_dir, mask_file))
        plt.show()


运行上述代码,即可得到下图的效果:

 使用传统方法进行裂缝分类_第4张图片使用传统方法进行裂缝分类_第5张图片

 使用传统方法进行裂缝分类_第6张图片

 使用传统方法进行裂缝分类_第7张图片

你可能感兴趣的:(CV数据处理,Python,opencv,python,传统方法,裂缝分类,双向投影,投影直方图)