图像预处理是工业缺陷检测系统中的关键环节,直接影响后续检测的准确性。下面将详细介绍一些工业缺陷检测图像预处理流程,包含多种优化技术和实用方法。
import cv2
import numpy as np
from skimage import exposure
import matplotlib.pyplot as plt
def basic_preprocessing(image_path):
"""
基础图像预处理流程
包含灰度化、去噪、增强和边缘保留等基本操作
"""
# 读取图像
img = cv2.imread(image_path)
if img is None:
raise ValueError("无法加载图像,请检查路径")
# 1. 颜色空间转换
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 直方图均衡化
equalized = cv2.equalizeHist(gray)
# 3. 去噪 (非局部均值去噪保留边缘)
denoised = cv2.fastNlMeansDenoising(equalized, None, h=15,
templateWindowSize=7,
searchWindowSize=21)
# 4. 高斯模糊
blurred = cv2.GaussianBlur(denoised, (5,5), 0)
return img, gray, equalized, denoised, blurred
def visualize_preprocessing(images, titles):
"""可视化预处理各阶段结果"""
plt.figure(figsize=(15,10))
for i in range(len(images)):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.axis('off')
plt.tight_layout()
plt.show()
# 使用示例
if __name__ == "__main__":
image_path = "defective_part.jpg"
original, gray, equalized, denoised, blurred = basic_preprocessing(image_path)
images = [original, gray, equalized, denoised, blurred]
titles = ['Original', 'Grayscale', 'Equalized', 'Denoised', 'Blurred']
visualize_preprocessing(images, titles)
def adaptive_illumination_correction(gray_image):
"""
自适应光照校正
解决不均匀光照问题
"""
# 1. 估计光照背景 (使用大核高斯模糊)
background = cv2.GaussianBlur(gray_image, (101,101), 0)
# 2. 从原图中减去背景 (转换为浮点避免截断)
corrected = cv2.addWeighted(gray_image.astype(np.float32), 1,
background.astype(np.float32), -1,
128)
# 3. 重新缩放至0-255范围
corrected = np.clip(corrected, 0, 255).astype(np.uint8)
# 4. 自适应直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
final = clahe.apply(corrected)
return final, background
def multi_scale_enhancement(image, sigma_list=[1, 3, 5]):
"""
多尺度图像增强
通过不同尺度的高斯核提取细节
"""
# 转换为浮点型
image = image.astype(np.float32) / 255.0
# 初始化结果矩阵
enhanced = np.zeros_like(image)
for sigma in sigma_list:
# 高斯模糊
blurred = cv2.GaussianBlur(image, (0,0), sigma)
# 细节层 = 原图 - 模糊图
detail = image - blurred
# 加权叠加 (权重与尺度成反比)
enhanced += detail / sigma
# 合并原始低频信息
enhanced = enhanced + image
# 归一化到0-255
enhanced = np.clip(enhanced * 255, 0, 255).astype(np.uint8)
return enhanced
def texture_enhancement(image, method='lbp', radius=3, neighbors=24):
"""
纹理增强方法
支持LBP(局部二值模式)和Gabor滤波
"""
if method == 'lbp':
# LBP纹理增强
lbp = local_binary_pattern(image, neighbors, radius, method='uniform')
enhanced = (lbp * 255 / lbp.max()).astype(np.uint8)
elif method == 'gabor':
# Gabor滤波增强
kernel = cv2.getGaborKernel((21,21), 5, np.pi/4, 10, 0.5, 0, ktype=cv2.CV_32F)
enhanced = cv2.filter2D(image, cv2.CV_8UC3, kernel)
else:
raise ValueError("不支持的纹理增强方法")
return enhanced
def defect_enhancement(image, background_estimation='morph'):
"""
缺陷增强技术
通过背景减除突出缺陷区域
"""
if background_estimation == 'morph':
# 形态学背景估计
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (51,51))
background = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
else:
# 高斯背景估计
background = cv2.GaussianBlur(image, (101,101), 0)
# 背景减除
diff = cv2.absdiff(image, background)
# 增强对比度
enhanced = cv2.normalize(diff, None, 0, 255, cv2.NORM_MINMAX)
return enhanced, background
def frequency_domain_enhancement(image, low_cutoff=0.1, high_cutoff=0.3):
"""
频域缺陷增强
通过频域滤波增强缺陷特征
"""
# 傅里叶变换
dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 创建带通滤波器
rows, cols = image.shape
crow, ccol = rows//2, cols//2
mask = np.zeros((rows, cols, 2), np.uint8)
r1 = int(low_cutoff * rows/2)
r2 = int(high_cutoff * rows/2)
cv2.circle(mask, (ccol, crow), r2, (1,1), -1)
cv2.circle(mask, (ccol, crow), r1, (0,0), -1)
# 应用滤波器
fshift = dft_shift * mask
# 逆傅里叶变换
ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(ishift)
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
# 归一化
enhanced = cv2.normalize(img_back, None, 0, 255, cv2.NORM_MINMAX)
return enhanced.astype(np.uint8)
def complete_preprocessing_pipeline(image_path,
illumination_corr=True,
texture_enhance=True,
defect_enhance=True):
"""
完整的工业缺陷检测预处理流水线
"""
# 1. 基础预处理
original = cv2.imread(image_path)
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
# 2. 光照校正
if illumination_corr:
gray, _ = adaptive_illumination_correction(gray)
# 3. 多尺度增强
enhanced = multi_scale_enhancement(gray)
# 4. 纹理增强
if texture_enhance:
enhanced = texture_enhancement(enhanced, method='lbp')
# 5. 缺陷增强
if defect_enhance:
enhanced, _ = defect_enhancement(enhanced)
# 6. 最终去噪
final = cv2.fastNlMeansDenoising(enhanced, None, h=7,
templateWindowSize=5,
searchWindowSize=15)
# 可视化
plt.figure(figsize=(15,10))
images = [original, gray, enhanced, final]
titles = ['Original', 'Grayscale', 'Enhanced', 'Final Preprocessed']
for i in range(len(images)):
plt.subplot(2, 2, i+1)
if i == 0:
plt.imshow(cv2.cvtColor(images[i], cv2.COLOR_BGR2RGB))
else:
plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.axis('off')
plt.tight_layout()
plt.show()
return final
# 使用示例
if __name__ == "__main__":
processed = complete_preprocessing_pipeline("defective_product.jpg")
针对不同类型的工业缺陷,推荐以下预处理组合:
缺陷类型 | 推荐预处理组合 |
---|---|
表面划痕 | 光照校正 + 多尺度增强 + 频域滤波 |
孔洞缺陷 | 纹理增强(LBP) + 形态学背景减除 |
边缘缺陷 | 多尺度增强 + Sobel边缘增强 + 非局部均值去噪 |
纹理异常 | Gabor滤波 + 局部对比度增强 |
微小颗粒 | 高频增强 + 锐化 + 阈值分割 |
ROI(Region of Interest,感兴趣区域)提取是工业缺陷检测中的关键步骤,能够有效缩小检测范围、提高处理效率。
import cv2
import numpy as np
def adaptive_threshold_roi(image):
"""
基于自适应阈值的ROI提取
适用于光照不均匀的场景
"""
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 自适应阈值
binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2)
# 形态学操作
kernel = np.ones((3,3), np.uint8)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rois = []
for cnt in contours:
if cv2.contourArea(cnt) > 1000: # 面积阈值
x,y,w,h = cv2.boundingRect(cnt)
rois.append(image[y:y+h, x:x+w])
return rois, cleaned
def otsu_threshold_roi(image):
"""
基于Otsu阈值的ROI提取
适用于双峰直方图的图像
"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 高斯模糊
blurred = cv2.GaussianBlur(gray, (5,5), 0)
# Otsu阈值
_, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return [cv2.boundingRect(cnt) for cnt in contours if cv2.contourArea(cnt) > 500], binary
def canny_edge_roi(image, low_threshold=30, high_threshold=100):
"""
基于Canny边缘检测的ROI提取
"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, low_threshold, high_threshold)
# 膨胀连接边缘
dilated = cv2.dilate(edges, None, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rois = []
for cnt in contours:
if cv2.contourArea(cnt) > 800:
x,y,w,h = cv2.boundingRect(cnt)
rois.append((x,y,w,h))
return rois, edges
def sobel_edge_roi(image, ksize=3): """ 基于Sobel算子的ROI提取 """ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Sobel边缘检测 sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=ksize) sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=ksize) # 计算梯度幅值 gradient = np.sqrt(sobelx**2 + sobely**2) gradient = np.uint8(gradient / gradient.max() * 255) # 阈值处理 _, binary = cv2.threshold(gradient, 50, 255, cv2.THRESH_BINARY) # 查找轮廓 contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) return [cv2.boundingRect(cnt) for cnt in contours if cv2.contourArea(cnt) > 500], gradient
def watershed_roi(image):
"""
基于分水岭算法的ROI提取
适用于重叠对象的分离
"""
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
# 去除噪声
kernel = np.ones((3,3), np.uint8)
opening = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)
# 确定背景区域
sure_bg = cv2.dilate(opening, kernel, iterations=3)
# 确定前景区域
dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
_, sure_fg = cv2.threshold(dist_transform, 0.7*dist_transform.max(), 255, 0)
sure_fg = np.uint8(sure_fg)
# 未知区域
unknown = cv2.subtract(sure_bg, sure_fg)
# 标记连通区域
_, markers = cv2.connectedComponents(sure_fg)
markers += 1
markers[unknown==255] = 0
# 分水岭算法
markers = cv2.watershed(image, markers)
image[markers == -1] = [255,0,0] # 标记边界
# 提取ROI
rois = []
for mark in np.unique(markers):
if mark > 1: # 忽略背景和边界
mask = np.zeros(gray.shape, dtype="uint8")
mask[markers == mark] = 255
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
if cv2.contourArea(cnt) > 500:
x,y,w,h = cv2.boundingRect(cnt)
rois.append((x,y,w,h))
return rois, markers
def robust_roi_extraction(binary_image,
min_area=1000,
max_area_ratio=0.9,
solidity_thresh=0.8,
convexity_thresh=0.9,
aspect_ratio_range=(0.2, 5)):
"""
鲁棒性ROI提取算法
包含多级过滤条件和几何特征分析
"""
# 查找轮廓(使用RETR_TREE获取层级关系)
contours, hierarchy = cv2.findContours(binary_image,
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
# 计算图像总面积
img_area = binary_image.shape[0] * binary_image.shape[1]
rois = []
for i, cnt in enumerate(contours):
# 1. 面积过滤
area = cv2.contourArea(cnt)
if area < min_area or area > (img_area * max_area_ratio):
continue
# 2. 几何特征计算
# 凸包
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
# 凸性检测
if hull_area == 0:
continue
solidity = float(area)/hull_area
if solidity < solidity_thresh:
continue
# 凸度检测
perimeter = cv2.arcLength(cnt, True)
hull_perimeter = cv2.arcLength(hull, True)
if hull_perimeter == 0:
continue
convexity = perimeter / hull_perimeter
if convexity > convexity_thresh:
continue
# 3. 形状特征过滤
# 最小外接矩形
rect = cv2.minAreaRect(cnt)
(_, (w, h), _) = rect
# 长宽比过滤
aspect_ratio = max(w, h) / (min(w, h) + 1e-6)
if not (aspect_ratio_range[0] <= aspect_ratio <= aspect_ratio_range[1]):
continue
# 4. 层级关系过滤(排除嵌套轮廓)
if hierarchy[0][i][3] != -1: # 如果有父轮廓则跳过
continue
# 计算所有特征
box = cv2.boxPoints(rect)
box = np.int0(box)
# 计算等效椭圆
ellipse = cv2.fitEllipse(cnt)
rois.append({
'contour': cnt,
'bounding_rect': cv2.boundingRect(cnt),
'min_area_rect': rect,
'min_area_box': box,
'convex_hull': hull,
'ellipse': ellipse,
'area': area,
'solidity': solidity,
'convexity': convexity,
'aspect_ratio': aspect_ratio
})
# 按面积排序
rois.sort(key=lambda x: x['area'], reverse=True)
return rois
def multi_scale_roi_detection(image,
min_scale=0.5,
max_scale=1.5,
scale_steps=3,
overlap_threshold=0.5):
"""
多尺度ROI检测算法
在不同尺度下检测ROI并合并结果
"""
all_rois = []
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
for scale in np.linspace(min_scale, max_scale, scale_steps):
# 缩放图像
width = int(image.shape[1] * scale)
height = int(image.shape[0] * scale)
resized = cv2.resize(gray, (width, height), interpolation=cv2.INTER_AREA)
# 预处理和ROI检测
_, _, binary = enhanced_preprocess(resized)
rois = robust_roi_extraction(binary)
# 将ROI坐标转换回原始尺寸
for roi in rois:
# 转换边界矩形
x, y, w, h = roi['bounding_rect']
orig_rect = (
int(x / scale), int(y / scale),
int(w / scale), int(h / scale)
# 转换最小外接矩形
(cx, cy), (rw, rh), angle = roi['min_area_rect']
orig_min_rect = (
(cx / scale, cy / scale),
(rw / scale, rh / scale),
angle)
# 转换轮廓点
orig_contour = (roi['contour'] / scale).astype(np.int32)
all_rois.append({
'bounding_rect': orig_rect,
'min_area_rect': orig_min_rect,
'contour': orig_contour
})
# 非极大值抑制 (NMS) 去除重叠ROI
final_rois = []
while len(all_rois) > 0:
# 选择面积最大的ROI
all_rois.sort(key=lambda x: x['bounding_rect'][2] * x['bounding_rect'][3], reverse=True)
selected = all_rois.pop(0)
final_rois.append(selected)
# 计算与剩余ROI的重叠
to_remove = []
x1, y1, w1, h1 = selected['bounding_rect']
area1 = w1 * h1
for i, roi in enumerate(all_rois):
x2, y2, w2, h2 = roi['bounding_rect']
# 计算交集
x_left = max(x1, x2)
y_top = max(y1, y2)
x_right = min(x1 + w1, x2 + w2)
y_bottom = min(y1 + h1, y2 + h2)
if x_right < x_left or y_bottom < y_top:
continue
intersection_area = (x_right - x_left) * (y_bottom - y_top)
area2 = w2 * h2
overlap = intersection_area / min(area1, area2)
if overlap > overlap_threshold:
to_remove.append(i)
# 从后往前删除避免索引问题
for i in sorted(to_remove, reverse=True):
all_rois.pop(i)
return final_rois