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

如何从二值图像提取裂纹轮廓?Python代码遇维度不匹配错误

解决二值图像裂纹轮廓提取中的形状不匹配问题

问题场景

要从二值图像中提取裂纹轮廓并可视化宽度分布,但运行代码时出现形状不匹配的报错,无法完成计算。

原代码:

import matplotlib.pyplot as plt
import matplotlib
matplotlib.rc("font", family='SimHei')
import numpy as np
import cv2
from matplotlib.colors import ListedColormap
im = cv2.imread(r'Desktop\\file\\lunwen\\test\\image1\\1381_8.jpg', cv2.IMREAD_GRAYSCALE)
im = cv2.threshold(im, 128, 1, cv2.THRESH_BINARY)[1]
result = np.zeros_like(im, dtype='uint32')
diff = np.diff(im.astype(np.int8), axis=1)
y, x_min = np.nonzero(diff == 1) 
_, x_max = np.nonzero(diff == -1)
width_list = x_max - x_min
print('max_width:', np.max(width_list))
print('min_width:', np.min(width_list))
print('mean_width:', np.mean(width_list))
for i in range(x_min.size):
    result[y[i], x_min[i]:x_max[i]] = width_list[i]
viridis = plt.cm.viridis
newcolors = viridis(np.linspace(0, 1, 256))
newcolors[0, :] = np.array([1, 1, 1, 1])
newcmp = ListedColormap(newcolors)
plt.figure(figsize=(8, 6))
plt.title('Crack Contour')
cax = plt.imshow(result, cmap=newcmp)
plt.axis('off')
cbar = plt.colorbar(cax, fraction=0.03, pad=0.06,label='Width', orientation='vertical')
plt.savefig('result_with_colorbar.png', bbox_inches='tight', pad_inches=0.1)
plt.show()

报错信息:

Traceback (most recent call last):
  File "E:\\Test.py", line 30, in <module>
    width_list = x_max - x_min  
ValueError: operands could not be broadcast together with shapes (797,) (800,) 

问题根源

直接通过np.nonzero提取的左右边界点,无法保证每行的左边界(diff==1)和右边界(diff==-1)数量完全对应。比如图像首列是裂纹时,没有左边界;末列是裂纹时,没有右边界;或者裂纹区域有断点,都会导致两边数量不一致。

修复代码

按行分组处理边界点,确保每个左边界都能找到对应的右边界:

import matplotlib.pyplot as plt
import matplotlib
matplotlib.rc("font", family='SimHei')
import numpy as np
import cv2
from matplotlib.colors import ListedColormap

# 读取并二值化图像
im = cv2.imread(r'Desktop\\file\\lunwen\\test\\image1\\1381_8.jpg', cv2.IMREAD_GRAYSCALE)
im = cv2.threshold(im, 128, 1, cv2.THRESH_BINARY)[1]
result = np.zeros_like(im, dtype='uint32')
height, width = im.shape

# 存储所有宽度数据
width_list = []

# 按行遍历处理边界
for y in range(height):
    row = im[y]
    # 找到当前行的所有前景起始和结束位置
    starts = []
    ends = []
    # 处理行首就是前景的情况
    if row[0] == 1:
        starts.append(0)
    # 检测行内的边界变化
    diff = np.diff(row.astype(np.int8))
    x_starts = np.where(diff == 1)[0] + 1  # diff=1表示从0变1,起始位置是当前索引+1
    x_ends = np.where(diff == -1)[0]       # diff=-1表示从1变0,结束位置是当前索引
    
    starts.extend(x_starts.tolist())
    ends.extend(x_ends.tolist())
    
    # 处理行末是前景的情况
    if row[-1] == 1:
        ends.append(width - 1)
    
    # 配对起始和结束点,计算宽度
    for s, e in zip(starts, ends):
        crack_width = e - s + 1
        width_list.append(crack_width)
        result[y, s:e+1] = crack_width

# 输出宽度统计信息
if width_list:
    print('max_width:', np.max(width_list))
    print('min_width:', np.min(width_list))
    print('mean_width:', np.mean(width_list))
else:
    print('未检测到裂纹区域')

# 可视化结果
viridis = plt.cm.viridis
newcolors = viridis(np.linspace(0, 1, 256))
newcolors[0, :] = np.array([1, 1, 1, 1])
newcmp = ListedColormap(newcolors)

plt.figure(figsize=(8, 6))
plt.title('Crack Contour')
cax = plt.imshow(result, cmap=newcmp)
plt.axis('off')
cbar = plt.colorbar(cax, fraction=0.03, pad=0.06, label='Width', orientation='vertical')
plt.savefig('result_with_colorbar.png', bbox_inches='tight', pad_inches=0.1)
plt.show()

关键修改说明

  • 改为按行遍历,针对每行单独处理边界点,避免跨行的边界点配对错误
  • 主动处理行首/行末为前景的特殊情况,补充缺失的边界点
  • 对每行的起始、结束点一一配对,确保数量一致,解决形状不匹配问题
  • 宽度计算修正为e - s + 1,符合像素数量的统计逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 00:20:10