You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在同一张图像中分别计算大小裂缝面积?解决Canny算子裂缝检测的精度问题

解决材料表面大小裂缝共存的检测精度问题

针对你遇到的「单一形态学核无法兼顾大小裂缝检测」的核心矛盾,我之前在类似的工业材料缺陷检测任务中用过分尺度处理+轮廓智能筛选的方案,能有效区分并准确计算大小裂缝的各项指标,下面是具体的思路和代码实现:

一、核心解决思路

不要用单一尺寸的膨胀/闭运算核,而是拆分处理流程:

  1. 先提取大裂缝:用较大的闭运算核填充大裂缝内部空隙,同时通过轮廓面积阈值过滤噪点;
  2. 再提取小裂缝:从原始边缘图中扣除大裂缝区域,用小核修复小裂缝的断裂部分,避免被大裂缝干扰;
  3. 分维度计算指标:对大小裂缝的轮廓分别统计数量、面积、长度等,同时合并整体指标,满足你对单条裂缝、分类统计的需求。

二、完整改进代码实现

1. 基础预处理与参数定义

先完成图像读取、灰度化模糊,同时定义像素-毫米转换的标定因子(需根据你的实际标定数据调整):

import cv2
import numpy as np

# 读取图像与基础参数
image = cv2.imread('./crack_img.jpg')
image_height, image_width = image.shape[:2]
total_image_pixel_area = image_height * image_width
# 示例:假设1毫米对应5个像素,即1像素=0.2毫米,替换为你的实际标定值
calibration_factor = 1 / 5  

# 灰度化与高斯模糊(降噪)
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (11, 11), 5)
# 保留原始Canny边缘图,用于后续小裂缝提取
edged_raw = cv2.Canny(img_blur, 50, 200, 1)

2. 大裂缝提取与区域标记

用大核闭运算填充大裂缝,提取外部轮廓并生成掩码,避免后续小裂缝检测被干扰:

# 大裂缝处理:椭圆核闭运算填充内部空隙
kernel_large = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (16, 16))
edged_large = cv2.morphologyEx(edged_raw, cv2.MORPH_CLOSE, kernel_large)

# 提取大裂缝外部轮廓(排除嵌套轮廓)
cnts_large, _ = cv2.findContours(edged_large.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 过滤过小的误检区域(阈值根据你的图像调整)
cnts_large = [c for c in cnts_large if cv2.contourArea(c) > 100]

# 生成大裂缝掩码,用于后续扣除大裂缝区域
large_crack_mask = np.zeros_like(edged_raw)
cv2.drawContours(large_crack_mask, cnts_large, -1, 255, thickness=cv2.FILLED)

3. 小裂缝提取(扣除大裂缝区域)

从原始边缘图中移除大裂缝区域,用小核修复小裂缝的断裂部分:

# 扣除大裂缝区域,仅保留小裂缝边缘
edged_small = cv2.subtract(edged_raw, large_crack_mask)
# 小核闭运算修复小裂缝的细微断裂
kernel_small = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
edged_small = cv2.morphologyEx(edged_small, cv2.MORPH_CLOSE, kernel_small)

# 提取小裂缝轮廓并过滤噪点
cnts_small, _ = cv2.findContours(edged_small.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts_small = [c for c in cnts_small if cv2.contourArea(c) > 10]

4. 全维度指标计算

分别计算大小裂缝的单条/整体指标,同时输出毫米单位的转换结果:

# ---------------------- 大裂缝指标 ----------------------
large_crack_count = len(cnts_large)
large_total_pixel_area = sum(cv2.contourArea(c) for c in cnts_large)
large_total_mm_area = large_total_pixel_area * (calibration_factor ** 2)
# 最大大裂缝:面积+长度(用弧长计算,裂缝为线状,closed=False更准确)
large_max_pixel_area = max([cv2.contourArea(c) for c in cnts_large], default=0)
large_max_mm_area = large_max_pixel_area * (calibration_factor ** 2)
large_max_pixel_length = max([cv2.arcLength(c, closed=False) for c in cnts_large], default=0)
large_max_mm_length = large_max_pixel_length * calibration_factor

# ---------------------- 小裂缝指标 ----------------------
small_crack_count = len(cnts_small)
small_total_pixel_area = sum(cv2.contourArea(c) for c in cnts_small)
small_total_mm_area = small_total_pixel_area * (calibration_factor ** 2)
small_max_pixel_area = max([cv2.contourArea(c) for c in cnts_small], default=0)
small_max_mm_area = small_max_pixel_area * (calibration_factor ** 2)
small_max_pixel_length = max([cv2.arcLength(c, closed=False) for c in cnts_small], default=0)
small_max_mm_length = small_max_pixel_length * calibration_factor

# ---------------------- 整体指标 ----------------------
total_crack_count = large_crack_count + small_crack_count
total_pixel_area = large_total_pixel_area + small_total_pixel_area
total_mm_area = total_pixel_area * (calibration_factor ** 2)
crack_area_percent = round((total_pixel_area / total_image_pixel_area) * 100, 3)

# 打印单条裂缝面积
print("=== 单条大裂缝面积 ===")
for idx, c in enumerate(cnts_large):
    area_pixel = cv2.contourArea(c)
    area_mm = area_pixel * (calibration_factor ** 2)
    print(f"大裂缝{idx+1}: {area_pixel:.2f}像素 | {area_mm:.2f}平方毫米")

print("\n=== 单条小裂缝面积 ===")
for idx, c in enumerate(cnts_small):
    area_pixel = cv2.contourArea(c)
    area_mm = area_pixel * (calibration_factor ** 2)
    print(f"小裂缝{idx+1}: {area_pixel:.2f}像素 | {area_mm:.2f}平方毫米")

# 打印汇总指标
print("\n=== 汇总指标 ===")
print(f"整体裂缝数量: {total_crack_count} (大裂缝{large_crack_count}条,小裂缝{small_crack_count}条)")
print(f"整体裂缝面积: {total_pixel_area:.2f}像素 | {total_mm_area:.2f}平方毫米")
print(f"裂缝面积占比: {crack_area_percent:.2f}%")
print(f"最大大裂缝: {large_max_pixel_area:.2f}像素 | {large_max_mm_area:.2f}平方毫米;长度: {large_max_pixel_length:.2f}像素 | {large_max_mm_length:.2f}毫米")
print(f"最大小裂缝: {small_max_pixel_area:.2f}像素 | {small_max_mm_area:.2f}平方毫米;长度: {small_max_pixel_length:.2f}像素 | {small_max_mm_length:.2f}毫米")

5. 可视化验证

用不同颜色标记大小裂缝,直观验证检测结果:

result_img = image.copy()
# 大裂缝用绿色标记,小裂缝用红色标记
cv2.drawContours(result_img, cnts_large, -1, (0, 255, 0), 2)
cv2.drawContours(result_img, cnts_small, -1, (0, 0, 255), 1)

# 添加文本标注
cv2.putText(result_img, f"Total Cracks: {total_crack_count}", (50, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
cv2.putText(result_img, f"Crack Area%: {crack_area_percent:.2f}%", (50, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
cv2.putText(result_img, f"Large: {large_crack_count} | Small: {small_crack_count}", (50, 90), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)

cv2.imshow('Raw Edges', edged_raw)
cv2.imshow('Large Cracks Mask', large_crack_mask)
cv2.imshow('Small Cracks Edges', edged_small)
cv2.imshow('Final Detection Result', result_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

三、额外优化建议

  • 动态阈值调整:可以用Otsu方法自动计算Canny的高低阈值,适配不同光照的图像;
  • 小裂缝合并:如果多个小裂缝距离极近(外接矩形重叠),可通过距离聚类判断为同一条裂缝的断裂部分,再合并轮廓;
  • 精准标定:一定要用标准标定板获取calibration_factor,避免毫米单位的计算误差。

内容的提问来源于stack exchange,提问作者dpsm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 15:19:11