裂缝二维检测:裂缝类型判断

news2025/1/9 7:11:09

裂缝类型选择

裂缝类型有很多种,这里我们判断类型的目的是要搞明白是否有必要检测裂缝的长度。在本文中,需要判断的裂缝类型共有四种:横向裂缝、纵向裂缝、斜裂缝、网状裂缝。

环境搭建

上一节骨架图提取部分,我们已经安装了skimage,并且在这里最好还是要有torch环境。接下来,如果你已经有的就没必要下了。

opencv-python:

​pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

opencv-contrib-python:

​pip install opencv-contrib-python -i https://pypi.tuna.tsinghua.edu.cn/simple

matplotlib:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple matplotlib==3.5.2

pyzjr:

pip install pyzjr -i https://pypi.tuna.tsinghua.edu.cn/simple

最好按照顺序来,因为pyzjr需要的依赖比较的多。

裂缝类型判断

获取图片文件

首先,我们肯定要有目标文件夹和结果保存文件夹。从文件夹中读取下面的图片路径,这里有一个bug,具体可以看这里。

masks_dir = r"E:\pythonconda2\dimension2_data\num" 
results_save_dir = "A_results"
os.makedirs(results_save_dir, exist_ok=True)
imgfile = pz.getPhotopath(masks_dir)

在我们的imgfile里面就储存了该文件夹下图片的路径。

获取最小外接矩形信息

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

def inference_minAreaRect(minAreaRect):
    w, h = minAreaRect[1]
    if w > h:
        angle = int(minAreaRect[2])
    else:
        angle = -(90 - int(minAreaRect[2]))
    return w, h, angle

def get_minAreaRect_information(mask):
    mask = pz.BinaryImg(mask)
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contour_merge = np.vstack(contours)
    minAreaRect = cv2.minAreaRect(contour_merge)
    return minAreaRect

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

初始化分类裂缝的参数

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

class ClassificationCrack():
    def __init__(self,threshold=3,HWratio=10,Histratio=0.5):
        self.threshold=threshold
        self.HWratio=HWratio
        self.Histratio=Histratio
        self.types = {0: 'Horizontal',
                      1: 'Vertical',
                      2: 'Oblique',
                      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)来比较是否超过了直方图比例阈值。

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

裂缝分类

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

    def classify(self, minAreaRect, skeleton_pts, HW):
        H, W = HW
        w, h, angle = inference_minAreaRect(minAreaRect)
        if w / h < self.HWratio or h / w < self.HWratio:
            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)

    @staticmethod
    def angle2cls(angle):
        angle = abs(angle)
        assert 0 <= angle <= 90, "ERROR: The angle value exceeds the limit and should be between 0 and 90 degrees!"
        if angle < 35:
            return 0
        elif 35 <= angle <= 55:
            return 2
        elif angle > 55:
            return 1
        else:
            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 。

主文件

if __name__ == '__main__':
    masks_dir = r"E:\pythonconda2\dimension2_data\num"  # 这里改为存放上面图片的路径
    results_save_dir = "A_results"
    os.makedirs(results_save_dir, exist_ok=True)
    classifier = ClassificationCrack()
    imgfile = pz.getPhotopath(masks_dir)
    for path in imgfile:
        mask = cv2.imread(path)
        H, W = mask.shape[:2]
        mask_copy = mask.copy()
        skeimage, skepoints = pz.ske_information(mask_copy)

        minAreaRect=get_minAreaRect_information(mask)
        timer=pz.Timer()
        w, h, angle = inference_minAreaRect(minAreaRect)
        print(w, h, angle)
        pts_y, pts_x = skepoints[:, 0], skepoints[:, 1]
        hist_x = np.histogram(pts_x, W)
        hist_y = np.histogram(pts_y, H)
        timer.stop()
        print("time cost: ", timer.total())

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

        T = classifier.threshold

        plt.figure()
        plt.subplot(221)
        plt.imshow(mask_copy)
        plt.title("crack type: {}".format(crack_type))
        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(223)
        plt.imshow(skeimage)
        # plt.savefig(os.path.join(results_save_dir, mask_file))
        plt.show()

检测的效果图:

图1 横向裂缝

图2 纵向裂缝

图3 斜裂缝

图4 网状裂缝

检测效果均不错,threshold,HWratio,Histratio这三个初始值均为经验所得,大家在用的时候还是要依照自己的数据来设定,可以看见左下的图,此图为没有经过消除毛刺的骨架图,但并不影响,我们采用的是骨骼点投影直方图的方法,些许毛刺影响不了我们的判断。

全文代码如下:

"""
裂缝分类如何判断
横向、纵向、网状、斜裂缝
"""
import os
import time
import matplotlib.pyplot as plt
import numpy as np
import cv2
import pyzjr as pz

def inference_minAreaRect(minAreaRect):
    w, h = minAreaRect[1]
    if w > h:
        angle = int(minAreaRect[2])
    else:
        angle = -(90 - int(minAreaRect[2]))
    return w, h, angle


class ClassificationCrack():
    def __init__(self,threshold=3,HWratio=10,Histratio=0.5):
        self.threshold=threshold
        self.HWratio=HWratio
        self.Histratio=Histratio
        self.types = {0: 'Horizontal',
                      1: 'Vertical',
                      2: 'Oblique',
                      3: 'Mesh'}

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

    def classify(self, minAreaRect, skeleton_pts, HW):
        H, W = HW
        w, h, angle = inference_minAreaRect(minAreaRect)
        if w / h < self.HWratio or h / w < self.HWratio:
            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)

    @staticmethod
    def angle2cls(angle):
        angle = abs(angle)
        assert 0 <= angle <= 90, "ERROR: The angle value exceeds the limit and should be between 0 and 90 degrees!"
        if angle < 35:
            return 0
        elif 35 <= angle <= 55:
            return 2
        elif angle > 55:
            return 1
        else:
            return None

def get_minAreaRect_information(mask):
    mask = pz.BinaryImg(mask)
    contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contour_merge = np.vstack(contours)
    minAreaRect = cv2.minAreaRect(contour_merge)
    return minAreaRect


if __name__ == '__main__':
    masks_dir = r"E:\pythonconda2\dimension2_data\num"  
    results_save_dir = "A_results"
    os.makedirs(results_save_dir, exist_ok=True)
    classifier = ClassificationCrack()
    imgfile = pz.getPhotopath(masks_dir)
    for path in imgfile:
        mask = cv2.imread(path)
        H, W = mask.shape[:2]
        mask_copy = mask.copy()
        skeimage, skepoints = pz.ske_information(mask_copy)

        minAreaRect=get_minAreaRect_information(mask)
        timer=pz.Timer()
        w, h, angle = inference_minAreaRect(minAreaRect)
        print(w, h, angle)
        pts_y, pts_x = skepoints[:, 0], skepoints[:, 1]
        hist_x = np.histogram(pts_x, W)
        hist_y = np.histogram(pts_y, H)
        timer.stop()
        print("time cost: ", timer.total())

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

        T = classifier.threshold

        plt.figure()
        plt.subplot(221)
        plt.imshow(mask_copy)
        plt.title("crack type: {}".format(crack_type))
        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(223)
        plt.imshow(skeimage)
        # plt.savefig(os.path.join(results_save_dir, mask_file))
        plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/787556.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux centos安装openoffice在线预览

前言&#xff1a;由于项目里需要用到word、excel等文件的在线预览&#xff0c;所有选择了openoffice 1、下载openoffice Apache OpenOffice - Official Download 大家自行选择需要安装的版本&#xff0c;楼主由于之前在其他服务器安装过&#xff0c;选择了之前用过的版本&am…

4.Docker数据管理和容器互联

文章目录 Docker数据管理数据卷&#xff08;容器与宿主机之间数据共享&#xff09;数据卷容器&#xff08;容器与容器之间数据共享&#xff09;容器互联 Docker数据管理 数据卷&#xff08;容器与宿主机之间数据共享&#xff09; 数据卷是一个供容器使用的特殊目录&#xff0…

【UE5 多人联机教程】03-创建游戏

效果 步骤 打开“UMG_MainMenu”&#xff0c;增加创建房间按钮的点击事件 添加如下节点 其中&#xff0c;“FUNL Fast Create Widget”是插件自带的函数节点&#xff0c;内容如下&#xff1a; “创建会话”节点指游戏成功创建一个会话后&#xff0c;游戏的其他实例即可发现&am…

mysql -速成

目录 1.概述 1.3SQL的优点 1.4 SQL 语言的分类 2. 软件的安装与启动 2.1 安装 2.2 MySQL服务的启动和停止 2.3 MySQL服务的登录和退出 ​编辑 2.4 mysql常用命令 2.5 图形化用户结构Sqlyong 3.DQL 语言 3.1 基础查询 3.1.1、语法 3.1.2 特点 3.2 条件查询 3.2.1 …

数据库的聚合函数和窗口函数

1. 聚合函数 数据库的聚合函数是用于对数据集执行聚合计算的函数。它们将一组值作为输入&#xff0c;并生成单个聚合值作为输出。聚合函数通常与GROUP BY子句结合使用&#xff0c;以便在数据分组的基础上执行聚合操作。 1.1. 常用的聚合函数 COUNT()&#xff1a;计算指定列或…

(五)springboot实战——springboot自定义事件的发布和订阅

前言 本节内容我们主要介绍一下springboot自定义事件的发布与订阅功能&#xff0c;一些特定应用场景下使用自定义事件发布功能&#xff0c;能大大降低我们代码的耦合性&#xff0c;使得我们应用程序的扩展更加方便。就本身而言&#xff0c;springboot的事件机制是通过观察者设…

Python(三十九)for-in循环

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

JAVA设计模式——模板设计模式(heima)

JAVA设计模式——模板设计模式&#xff08;heima&#xff09; 文章目录 JAVA设计模式——模板设计模式&#xff08;heima&#xff09;一、模板类二、子类2.1 Tom类2.2 Tony类 三、测试类 一、模板类 package _01模板设计模式;public abstract class TextTemplate{public final…

利用FME实现批量提取图斑特征点、关键界址点提取、图斑拐点抽稀,解决出界址点成果表时点数过多问题的方法

目录 一、实现效果 二、实现过程 1.提取图斑界址点 2.计算各界址点的角度 3.筛选提取关键界址点 三、总结 对于范围较大的图斑&#xff0c;界址点数目较大&#xff0c;在出界址点成果表前&#xff0c;往往需要对界址点进行处理&#xff0c;提取出关键特征点作为出界址点成…

数据库集群方案详解

本期直播我们邀请 KaiwuDB 资深解决方案专家周幸骏&#xff0c;为大家分享数据库集群方案详解。周老师毕业于复旦大学数学系&#xff0c;从业 20 余年&#xff0c;曾在 IBM 公司任资深技术专家&#xff0c;并为多家国有大型商业银行提供技术咨询和数据库业务连续方案设计等服务…

IBM:2023 年数据泄露的平均成本将达到 445 万美元

IBM 发布年度《数据泄露成本报告》&#xff0c;显示 2023 年全球数据泄露平均成本达到 445 万美元&#xff0c;比过去 3 年增加了 15%。创下该报告的历史新高。 报告显示&#xff0c;企业在计划如何应对日益增长的数据泄露频率和成本方面存在分歧。研究发现&#xff0c;虽然 95…

Linux学成之路(基础篇)(二十三)MySQL服务(上)

目录 一、概述 一、什么是MySQL 二、数据库能干什么 三、为什么要用数据库&#xff0c;优势、特性&#xff1f; 二、数据库类型 一、关系型数据库 RDBMS 一、概述 二、特点 三、代表产品 二、非关系型数据库 一、概述 二、特点 三、代表产品 三、数据库模型 一、…

【GEE笔记】主成分分析(PCA)算法的实现和应用

前言 主成分分析&#xff08;PCA&#xff09;是一种常用的降维方法&#xff0c;它可以将多个相关的变量转换为少数几个不相关的变量&#xff0c;称为主成分&#xff08;PC&#xff09;。这些主成分可以反映原始变量的大部分信息&#xff0c;同时减少数据的复杂度和冗余性。在遥…

结构型设计模式之组合模式【设计模式系列】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 设计模式系列 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everythi…

【nginx】nginx中root与alias的区别:

文章目录 root与alias主要区别在于nginx如何解释location后面的uri&#xff0c;这会使两者分别以不同的方式将请求映射到服务器文件上。 root的处理结果是&#xff1a;root路径&#xff0b;location路径 alias的处理结果是&#xff1a;使用alias路径替换location路径 alias是一…

前端配置nginx反向代理

1.下载nginx 2.进入nginx.conf 3.配置 4.将nginx放入前端项目根目录 5.启动后端项目和nginx服务 启动命令 start nginx ; 重新启动命令 nginx -s reload 6.访问localhost就能看到项目了&#xff08;默认80端口&#xff09; 注意&#xff1a;如果nginx配置了域名&#xff0…

Android ConstraintLayout使用攻略

原文链接 Android ConstraintLayout使用攻略 ConstraintLayout是新一代的布局&#xff0c;它汲取了众家之长&#xff0c;把布局的概念进行了大统一&#xff0c;灵活且强大&#xff0c;基本上可以干掉以前所有的常用布局&#xff08;LinearLayout, RelativeLayout和FrameLayout…

94.qt qml-分页Table表格组件

在我们之前学习了87.qt qml-分页组件控件(支持设置任意折叠页数等)_qt分页控件_诺谦的博客-CSDN博客 然后我们又学习了Table实现,所以本章实现一个分页Table表格组件,配合分页控件, 模拟请求服务器数据来实现数据分解效果,因为一般使用分页的时候,一般都是分页请求,避免数…

Flutter的开发环境搭建-图解

前言&#xff1a;Flutter作为一个移动应用开发框架&#xff0c;具有许多优点和一些局限性。最大的优点就是-跨平台开发&#xff1a;Flutter可以在iOS和Android等多个平台上进行跨平台开发&#xff0c;使用一套代码编写应用程序&#xff0c;节省开发时间和成本。 Flutter可以编…

python将大文件拆分为多个小文件

如上图&#xff0c;目前采用单行不停写入的方式&#xff0c;这里是读了两次文件&#xff0c;第一次读取文件是为了获取总行数&#xff0c;第二次读取是取数据内容。 如果只读取一次文件&#xff0c;则会对内存有一定的要求&#xff0c;会需要在第一次读取数据的时候就将文件内…