如何用OpenCV或传统方法提取亮度不均的钢材灰度图轮廓?
钢材灰度图像轮廓提取优化(适配高亮度/亮度不均场景)
问题背景
手头有大量钢材灰度图像,部分亮度偏高,还有些存在亮度不均的问题,目前的轮廓提取效果有缺陷,想找更靠谱的处理方案。
现有尝试
先通过确定左右边界裁剪来减少背景噪声,裁剪后的处理代码如下:
m=img.shape[0]//2 count=mayregion if clip_top: last_row = img_three[-1, :] mid_row= img_three[m, :] mean_pixel = (np.mean(last_row)+np.mean(mid_row))//2 count=(np.sum(last_row > 240)+np.sum(mid_row>240)) else: last_row = img_three[0, :] mid_row = img_three[m, :] mean_pixel = (np.mean(last_row) + np.mean(mid_row)) // 2 count = (np.sum(last_row > 240) + np.sum(mid_row > 240)) if mean_pixel>240: k=100 elif mean_pixel>210: k=80 else: k=30 img_three_color = cv2.cvtColor(img_three, cv2.COLOR_GRAY2BGR) img_three = cv2.GaussianBlur(img, (7, 7), 1) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) img_open = cv2.morphologyEx(img_three, cv2.MORPH_OPEN, kernel) img_close = cv2.morphologyEx(img_open, cv2.MORPH_CLOSE, kernel) _, binary = cv2.threshold(img_close, k, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) >= 100] cv2.drawContours(img_three_color, filtered_contours, -1, (0, 255, 0), 10)
但用这个方法提取的轮廓还是有不少问题。
优化方向
针对高亮度和亮度不均的核心问题,可以从这几个点调整:
- 用自适应阈值替代全局阈值:全局阈值在亮度不均的图里要么丢细节要么留噪声,
cv2.adaptiveThreshold能根据局部区域的像素分布算阈值,适配不同区域的亮度变化,替换原全局阈值代码:
其中binary = cv2.adaptiveThreshold(img_close, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2)11是局部块大小(需为奇数),2是阈值偏移量,可根据你的图像实际情况微调。 - 换用双边滤波替代高斯模糊:高斯模糊容易把边缘糊掉,双边滤波能在滤除噪声的同时保留边缘细节,适合钢材这种需要清晰边缘的场景:
img_three = cv2.bilateralFilter(img, d=9, sigmaColor=75, sigmaSpace=75) - 优化形态学操作参数:当前的(3,3)椭圆核可能太细,试试调大到(5,5),或者增加形态学梯度操作强化边缘:
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) img_grad = cv2.morphologyEx(img_open, cv2.MORPH_GRADIENT, kernel) - 改进阈值计算逻辑:原逻辑只取两行像素的均值来定阈值,样本量太小容易误判。可以改用Otsu自动阈值法,它能根据图像直方图自动找到最优分割阈值:
_, binary = cv2.threshold(img_close, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) - 增强轮廓后处理:除了面积过滤,还可以加长宽比过滤,剔除不符合钢材形态的噪声轮廓:
filtered_contours = [] for cnt in contours: area = cv2.contourArea(cnt) if area < 100: continue x, y, w, h = cv2.boundingRect(cnt) aspect_ratio = w / h # 根据你的钢材实际形态设定合理范围,这里只是示例 if 0.2 < aspect_ratio < 5: filtered_contours.append(cnt)
实操建议
优先试自适应阈值+双边滤波的组合,先解决亮度不均的核心问题;再根据轮廓的具体缺陷(比如断边、噪声轮廓多)调整形态学参数和过滤规则,逐步优化。
内容的提问来源于stack exchange,提问作者Mumu
相关产品推荐
相关产品推荐

