101 lines
4.4 KiB
Python
101 lines
4.4 KiB
Python
|
|
import cv2
|
|||
|
|
import numpy as np
|
|||
|
|
|
|||
|
|
# 读取图像(以灰度模式加载)
|
|||
|
|
# 作用:将 '1.jpg' 图像读入内存,转换为单通道灰度图,便于后续图像处理
|
|||
|
|
# 注意:如果路径错误或文件不存在,image 将为 None,导致后续报错
|
|||
|
|
image = cv2.imread('1.jpg', cv2.IMREAD_GRAYSCALE)
|
|||
|
|
|
|||
|
|
# 检查图像是否成功加载
|
|||
|
|
if image is None:
|
|||
|
|
raise FileNotFoundError("无法读取图像,请检查文件路径是否正确:'1.jpg'")
|
|||
|
|
|
|||
|
|
# 均值滤波,滤波核大小为7x7
|
|||
|
|
# 作用:对图像进行平滑处理,去除高频噪声(如随机噪点)
|
|||
|
|
# 原理:用每个像素周围7x7邻域的平均值替换该像素值,使图像更“柔和”
|
|||
|
|
blurred = cv2.blur(image, (7, 7))
|
|||
|
|
|
|||
|
|
# 使用局部自适应阈值进行图像分割
|
|||
|
|
# 作用:将图像二值化,突出比局部背景更暗的区域(如划痕、凹坑等缺陷)
|
|||
|
|
# 参数说明:
|
|||
|
|
# - cv2.ADAPTIVE_THRESH_MEAN_C:阈值基于局部邻域的平均值
|
|||
|
|
# - 11:邻域大小(奇数),决定局部范围
|
|||
|
|
# - 2:从均值中减去的常数,用于微调灵敏度
|
|||
|
|
# - cv2.THRESH_BINARY_INV:反转结果,缺陷区域为白色(255),背景为黑色(0)
|
|||
|
|
thresholded = cv2.adaptiveThreshold(
|
|||
|
|
blurred, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
|
|||
|
|
cv2.THRESH_BINARY_INV, 11, 2
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 查找图像中的轮廓
|
|||
|
|
# 作用:检测二值图像中所有连通的白色区域(即潜在缺陷)
|
|||
|
|
# 返回值:
|
|||
|
|
# - contours: 所有轮廓的坐标点列表
|
|||
|
|
# - _ : 轮廓的层级关系(此处不需要)
|
|||
|
|
# 方法选择:
|
|||
|
|
# - cv2.RETR_LIST:提取所有轮廓,不建立层级
|
|||
|
|
# - cv2.CHAIN_APPROX_SIMPLE:压缩水平/垂直/对角线方向的轮廓点,节省内存
|
|||
|
|
contours, _ = cv2.findContours(thresholded, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
|
|||
|
|
|
|||
|
|
# 创建一个彩色图像用于结果显示
|
|||
|
|
# 作用:将原始灰度图转换为BGR三通道图像,以便用不同颜色绘制检测结果
|
|||
|
|
output_image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
|
|||
|
|
|
|||
|
|
# 对每个检测到的轮廓进行面积筛选
|
|||
|
|
# 作用:过滤掉太小(可能是噪声)或太大(可能是背景干扰)的区域
|
|||
|
|
# 设置面积阈值范围:250 ~ 1000 像素
|
|||
|
|
min_area, max_area = 250, 1000
|
|||
|
|
for contour in contours:
|
|||
|
|
area = cv2.contourArea(contour) # 计算轮廓包围的面积
|
|||
|
|
if min_area < area < max_area:
|
|||
|
|
# 绘制符合条件的轮廓
|
|||
|
|
# 颜色:绿色 (0, 255, 0),线宽:2
|
|||
|
|
cv2.drawContours(output_image, [contour], -1, (0, 255, 0), 2)
|
|||
|
|
|
|||
|
|
# 形态学膨胀操作
|
|||
|
|
# 作用:扩大二值图像中的白色区域,连接邻近的断裂缺陷(如断开的划痕)
|
|||
|
|
# 结构元素:椭圆形(模拟圆形),大小为7x7
|
|||
|
|
# iterations=1:执行一次膨胀
|
|||
|
|
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
|
|||
|
|
dilated = cv2.dilate(thresholded, kernel, iterations=1)
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 骨架化处理(细化为单像素宽的中心线)
|
|||
|
|
# 作用:将膨胀后的缺陷区域“细线化”,便于分析划痕的路径和长度
|
|||
|
|
# 注意:OpenCV 无内置骨架化函数,这里使用经典的Zhang-Suen细化算法简化版
|
|||
|
|
def thinning(img):
|
|||
|
|
skel = np.zeros(img.shape, np.uint8) # 存储骨架结果
|
|||
|
|
size = np.size(img)
|
|||
|
|
element = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3)) # 3x3十字结构元
|
|||
|
|
done = False
|
|||
|
|
|
|||
|
|
while not done:
|
|||
|
|
# 重复执行:腐蚀 → 膨胀 → 差分 → 累加到骨架
|
|||
|
|
eroded = cv2.erode(img, element) # 腐蚀:缩小区域
|
|||
|
|
temp = cv2.dilate(eroded, element) # 膨胀回来
|
|||
|
|
temp = cv2.subtract(img, temp) # 得到最外层轮廓(即“剥皮”一层)
|
|||
|
|
skel = cv2.bitwise_or(skel, temp) # 累加到骨架图像
|
|||
|
|
img = eroded.copy() # 更新图像为腐蚀后的结果
|
|||
|
|
|
|||
|
|
# 当图像完全变黑(无非零像素)时停止
|
|||
|
|
zeros = size - cv2.countNonZero(img)
|
|||
|
|
if zeros == size:
|
|||
|
|
done = True
|
|||
|
|
|
|||
|
|
return skel
|
|||
|
|
|
|||
|
|
|
|||
|
|
# 执行骨架化
|
|||
|
|
skeleton = thinning(dilated)
|
|||
|
|
|
|||
|
|
# 显示所有中间结果和最终检测图
|
|||
|
|
# 作用:可视化每一步的处理效果,便于调试和分析
|
|||
|
|
cv2.imshow("Original Image", image) # 原始灰度图
|
|||
|
|
cv2.imshow("Thresholded Image", thresholded) # 自适应阈值分割结果
|
|||
|
|
cv2.imshow("Dilated Image", dilated) # 膨胀后结果
|
|||
|
|
cv2.imshow("Skeletonized Image", skeleton) # 骨架化结果(细线)
|
|||
|
|
cv2.imshow("Detected Regions", output_image) # 最终检测结果(绿色轮廓)
|
|||
|
|
|
|||
|
|
# 等待按键后关闭所有窗口
|
|||
|
|
cv2.waitKey(0)
|
|||
|
|
cv2.destroyAllWindows()
|