基于卷积自编码器和图像金字塔的布料缺陷无监督学习与检测方法
这篇博客是在我在浙江大学计算机学院做实习时接触到的一个课题,参考了论文《An Unsupervised-Learning-Based Approach for Automated Defect Inspection on Textured Surfaces》的基础上进行了复现,并加入了自己的理解和改进。
一、布料纹理缺陷检测与纹理学习
因为对缺陷进行标记或像素级分割很困难,缺陷的类型也十分复杂,所以基于学习的纹理缺陷检测大体思路是无监督的。即利用无监督学习算法学习正常纹理的数据分布特征,而不学习缺陷的数据分布特征。在待测图上以滑动区域为重构对象,与原图像做残差。由于正常纹理学习充分,重构残差应当很小,而缺陷区域的残差较大,故被凸现出来,随后再利用残差图做进一步处理。
在空域上进行无监督学习主要用卷积自编码器,其有两部分组成,编码器和解码器。编码器由卷积、激活、池化操作做成,对原始数据域分层做特征提取和降维,最后将数据映射到一个欠完备的隐层特征空间上。解码器由上采样、卷积、激活操作完成,将特征空间上的数据映射回空域。自编码器的训练一般由重构残差做为损失函数驱动,其中也可以加入稀疏正则化,提高模型泛化能力。
L ( x , x ′ ) = 1 2 N ∑ i = 1 N ∣ ∣ x − x ′ ∣ ∣ 2 + λ ⋅ ∑ w ∈ W , W ′ ∣ ∣ w ∣ ∣ F L(\bm{x},\bm{x}')=\frac{1}{2N}\sum_{i=1}^{N} || \bm{x}-\bm{x}' ||^2+\lambda \cdot \sum_{w \in W,W'}|| w ||_F L(x,x′)=2N1i=1∑N∣∣x−x′∣∣2+λ⋅w∈W,W′∑∣∣w∣∣F
式中, W W W和 W ′ W' W′是编码器和解码器的卷积核参数, ∣ ∣ ⋅ ∣ ∣ F ||\cdot||_F ∣∣⋅∣∣F表示矩阵的Frobenius范数,类似于向量的L2范数。
二、算法总述
下图是算法总体框图。左侧是训练阶段,右侧是测试阶段。
训练阶段包括:图像预处理和patch提取,以及最后的分层训练。
测试阶段包括:图像预处理(但不包括噪声腐蚀)和patch提取,分层构建残差图和层间结果融合。
三、数据集准备
布料数据集是我用手机拍摄的布料视频抽取图像帧构成的。拍摄过程涉及到镜头远近(不同尺度纹理)、镜头旋转和光照不均(开闪光灯)。每张图片被裁剪成 512 × 512 512\times 512 512×512像素的彩图。缺陷主要是块状污渍和模拟缺陷。
四、训练阶段
4.1 图像预处理
- 光照归一化
- 构建高斯金字塔
- 噪声腐蚀
1)光照归一化是为了消除图像因为光照不均而引起的畸变。原文中用的是韦伯局部算子,但我没有复现出合理的归一化效果。故采用经典的直方图均衡算法。
W L D ( I ) = a r c t a n ( ∑ Δ x ∈ A ∑ Δ y ∈ A I ( x , y ) − I ( x − Δ x , y − Δ y ) I ( x , y ) ) WLD(\bm{I}) = arctan(\sum_{\Delta x \in A} \sum_{\Delta y \in A} \dfrac{ \bm{I}(x,y) - \bm{I}(x-\Delta x,y-\Delta y)}{\bm{I}(x,y)}) WLD(I)=arctan(Δx∈A∑Δy∈A∑I(x,y)I(x,y)−I(x−Δx,y−Δy))
def Illumination_Normalization(img,method = 'hist'):
'''
the function to carry out the illumination normalization using Weber Local Descriptor or Histogram Equalization
'''
height,width,channel = np.shape(img)
if method == 'hist':
# 分通道做直方图均衡
equ0 = cv2.equalizeHist(img[:,:,0])
equ1 = cv2.equalizeHist(img[:,:,1])
equ2 = cv2.equalizeHist(img[:,:,2])
img_hist_equ = np.zeros((height,width,channel),np.uint8)
img_hist_equ[:,:,0] = equ0
img_hist_equ[:,:,1] = equ1
img_hist_equ[:,:,2] = equ2
return img_hist_equ
if method == 'wld':
# 外部补零
img_padding = np.zeros((height+2,width+2,channel),np.float32)
img_padding[1:height+1,1:width+1,:] = img.astype('float32')
temp = np.zeros((h