数据集下载:https://download.csdn.net/download/qq_38784454/10349531
《Micro-crack detection of multicrystalline solar cells featuring an improved anisotropic diffusion filter and image segmentation technique》请在google学术上搜所这篇文章。
个人认为裂纹检测或是缺陷检测的方法主要分为两类:(1)图像增强类。(2)特征提取+分类。目前特征提取+分类是主流。上面这篇文章属于图像增强类,主要的研究工作就是凸显出裂纹缺陷。上文的研究重点放在了对各向异性扩散滤波的改进上,主要创新点是改变了扩散系数和导热系数(用于控制平滑)的求法。
首先我是这样理解各向异性扩散的,我们用泄洪来做比喻,导热系数K就是就是洪水的大小,导热系数越大,洪水就越大。图像里面的边缘就是堤坝,用来阻挡洪水。首先,堤坝是一定的,即图像的边缘是不可更改的。我们要做的就是找到一个合适的导热系数,即洪水的大小一定要控制好,既要保证泄洪量最大,又要保证堤坝不会被冲垮。这就需要我们选择一个合适的K值。这个值是我们通过试错法找到的。上面这篇文章就是提出了一种可以自适应的调节K值的方法,不需要人为调节。
俗话说创新点不足,实验来凑。 这篇文章做了大量的对比试验,文章所提的改进各向异性扩散滤波分别与Ostu阈值分割,Sobel边缘检测,Canny边缘检测,拉普拉斯滤波,FIR边缘检测作对比。
提取的特征选择的是裂纹的形状特征,所用的方法是ART(角度径向转换)提取形状特征,再用PCA做降维,对降为后的特征用SVM做分类。特征提取的方法ART分别与FD(傅里叶形状描述器)、GFD(通用傅里叶形状描述器)、RCF(基于投影的特征提取)作对比。
分类方法用的是SVM,分别与LDA(线性判别分析)、QDA(双线性判别分析),以及经典的KNN作对比。
总的来说,这种写论文的投机方式可以借鉴。没时间了,先写到这里,有时间再补充。。。。。。
我在这里用的是傅里叶形状描述子(ART太难实现),分类器用的SVM。傅里叶描述子选用 36*1 维的复数特征描述子,这个36*1 维的特征向量是这样得到的,首先筛选边缘点数大于50的,再对其进行傅里叶描述,从所得的描述子里面选择36个,提取出36*1 维复数描述子的实部与虚部,组成72*1的特征向量作为SVM分类器的输入。对于SVM分类器来说,先训练,在测试。
下面直接说算法实现:
一、对原图做傅里叶变换,进行低通滤波,去除噪声,得到图1。
二、再接着对图1,进行各向异性扩散滤波,平滑图像,实现图像增强,得到图2。
三、对图2进行二值化处理,去噪,得到图3。
四、提取图3(二值图像)的边缘得到图4。首先得到其边缘坐标,然后用傅里叶形状描述器描述。
五、SVM训练与测试。
图1:傅里叶低通滤波后的图像
图2:各向异性扩散后的图像
图3:二值化后的图像
图4:提取二值图像的边缘
这里定义 1 代表裂纹,2代表背景,筛选后只留下一个裂纹和一个背景,如图:
分类结果:
classification =
1
2
下面的matlab是我集成后的程序:
matalb程序:
close all; clear; clc; [filename,pathname] = uigetfile('*.*','选择图片','E:\裂纹\测试图片\1.png'); if isequal(filename,0)||isequal(pathname,0) return; else filefullpath = [pathname,filename]; end %% 傅里叶变换,低通滤波,傅里叶反变换。 d = 30; bw = imread(filefullpath); bw = rgb2gray(bw); figure(1); subplot(2,2,1);imshow(bw); F = fftshift(fft2(double(bw))); F1 = abs(F); T = log(F1+1); subplot(2,2,2); imshow(T,[]) [m,n] = size(bw); m_mid = fix(m/2); n_mid = fix(n/2); F_lpf = zeros(m,n); h = zeros(m,n); for i = 1:m for j = 1:n distance = sqrt((i-m_mid)^2+(j-n_mid)^2); if distance >= d h(i,j) = 0; else h(i,j) = 1; end F_lpf(i,j) = h(i,j)*F(i,j); end end bw = ifftshift(F_lpf); bw = uint8(real(ifft2(bw))); subplot(2,2,3);imshow(bw);title('低通滤波图像'); % %% 直方图均衡 % bw = histeq(bw,128); % subplot(2,2,4);imshow(bw);title('直方图均衡化后的图像'); %% 各向异性扩散 diff_im=bw; num_iter =5; delta_t = 1/4; kappa =4; dx = 1; dy = 1; dd = sqrt(2); %这是4个方向 hN = [0 1 0; 0 -1 0; 0 0 0]; hS = [0 0 0; 0 -1 0; 0 1 0]; hE = [0 0 0; 0 -1 1; 0 0 0]; hW = [0 0 0; 1 -1 0; 0 0 0]; %非正方向 % hNE = [0 0 1; 0 -1 0; 0 0 0]; % hSE = [0 0 0; 0 -1 0; 0 0 1]; % hSW = [0 0 0; 0 -1 0; 1 0 0]; % hNW = [1 0 0; 0 -1 0; 0 0 0]; f=diff_im/255; %将图像归一化 % Anisotropic diffusion. for t = 1:num_iter nablaN = imfilter(diff_im,hN,'conv'); nablaS = imfilter(diff_im,hS,'conv'); nablaW = imfilter(diff_im,hW,'conv'); nablaE = imfilter(diff_im,hE,'conv'); % nablaNE = imfilter(diff_im,hNE,'conv'); % nablaSE = imfilter(diff_im,hSE,'conv'); % nablaSW = imfilter(diff_im,hSW,'conv'); % nablaNW = imfilter(diff_im,hNW,'conv'); cN = 1./(1 + f.*(kappa./nablaN).^2); cS = 1./(1 + f.*(kappa./nablaS).^2); cW = 1./(1 + f.*(kappa./nablaW).^2); cE = 1./(1 + f.*(kappa./nablaE).^2); % cNE = 1./(1 + f.*(kappa./nablaN).^2); % cSE = 1./(1 +f.*(kappa./nablaN).^2); % cSW = 1./(1 +f.*(kappa./nablaN).^2); % cNW = 1./(1 + f.*(kappa./nablaN).^2); % diff_im = diff_im + ... % delta_t*(... % (1/(dy^2))*cN.*nablaN + (1/(dy^2))*cS.*nablaS + ... % (1/(dx^2))*cW.*nablaW + (1/(dx^2))*cE.*nablaE + ... % (1/(dd^2))*cNE.*nablaNE + (1/(dd^2))*cSE.*nablaSE + ... % (1/(dd^2))*cSW.*nablaSW + (1/(dd^2))*cNW.*nablaNW ); diff_im = diff_im + ... delta_t*(... cN.*nablaN + cS.*nablaS + ... cW.*nablaW + cE.*nablaE ); fprintf('\rIteration %d\n',t); end figure(2),imshow(diff_im,[]); I=abs(diff_im-bw); figure(3),imshow(I,[]); mean_I=mean(mean(I)); std_I=std2(I); C=1.5; threshold=mean_I+C*std_I; % threshold=5; A=size(I); for i=1:A(1) for j=1:A(2) if I(i,j)<threshold I(i,j)=255; else I(i,j)=0; end end end figure(4); imshow(I); [m,n]=size(I); B=I; c=zeros(m,n); for x=2:m-1 for y=2:n-1 if (B(x-1,y)+B(x,y)+B(x+1,y)==0 || B(x,y-1)+B(x,y)+B(x,y+1)==0 || B(x-1,y-1)+B(x,y)+B(x+1,y+1)==0 || ...
B(x-1,y+1)+B(x,y)+B(x+1,y-1)==0) %去除噪声 c(x,y)=255; else c(x,y)=0; end end end c = ~c; figure(5),imshow(c); %%%%显示为红色的区域 I_color = bw; I_color1 = I_color; I_color2 = I_color; I_color3 = I_color; [rows,cols] = find(c==0); for i =1:size(rows) % I_red(rows(i),cols(i)) = [255,0,0]; % I_color(rows(i),cols(i)) = 255; I_color1(rows(i),cols(i)) = 255; I_color2(rows(i),cols(i)) = 0; I_color3(rows(i),cols(i)) = 0; end I_red(:,:,1) = I_color1; I_red(:,:,2) = I_color2; I_red(:,:,3) = I_color3; figure(6),imshow(I_red(2:m-2,2:n-2,:)); d=c; for i1=-1:1 for j1=-1:1 for x=2:m-1 for y=2:n-1 if( d(x,y)==0 && B(x+i1,y+j1)==0) d(x+i1,y+j1)=0; end end end end end figure(7); imshow(d); %% 分水岭分割图片,对于裂纹穿过的图片可以把裂纹分开。 g2 = imclose(imopen(d,ones(3,3)),ones(3,3)); L2 = watershed(g2); wr2 = L2 == 0; d2 = d; d2(wr2) = 255; figure;imshow(d2);title('d2') d = d2; d = ~d; figure;imshow(d); %% e=c-d; figure(8); imshow(e); % se = strel('square',2); % d = imopen(d,se); % figure(9);imshow(d); thresh = graythresh(uint8(d)); bw = im2bw(d,thresh); figure(10);imshow(bw); s = bwmorph(bw,'skel',Inf); %骨骼图 figure(11);imshow(s); figure(12);imshow(bw);hold on [L,num] = bwlabel(bw); g = bwperim(bw); %提取边界 % imwrite(g,'C:\Users\thinkpad\Desktop\裂纹图片\a.jpg'); figure(13);imshow(g); [B,L,NR,A] = bwboundaries(d,8,'noholes'); b = cat(1,B{:}); [M,N] = size(d); image = bound2im(b,M,N); figure;imshow(image); b = B{2}; % bim = bound2im(b,m,n); % figure;imshow(bim); d = []; for j = 1:length(B) b = B{j}; if length(B{j})>50 bim = bound2im(b,m,n); figure;imshow(bim); z = frdescp(b); %傅里叶描述子,复数 l = length(z); a = round((l-36)/2); z(1:a) = 0; z(l-a+1:l) = 0; c = z(a+1:a+36); d = [d,c]; %d存放 维数>50 的裂纹和背景的傅里叶描述子,复数 a = j end end data = [real(d);imag(d)]'; %data 存放的是傅里叶描述子的实部和虚部 %% SVM对data进行分类,用14个裂纹,14个背景,做训练。 D = dir('E:\裂纹\标记后裂纹\*.mat'); data1 =[]; for i = 1:40 load(['E:\裂纹\标记后裂纹\',D(i).name]); a = f; data1 = [data1,a]; end data1 = data1'; save('data1.mat'); D = dir('E:\裂纹\标记后背景\*.mat'); length(D) data2 = []; for i = 1:40 load(['E:\裂纹\标记后背景\',D(i).name]); a = f; data2 = [data2,a]; end data2 = data2'; save('data2.mat'); train = [data1;data2]; % group = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2]; group = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,...
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]; % load('E:\裂纹\标记后背景\18.mat','f'); test = data; %测试数据 %训练分类模型 svmModel = svmtrain(train,group,'kernel_function','rbf','showplot',true); classification = svmclassify(svmModel,test,'Showplot',true) % 分类结果。 1 代表裂纹 ,2 代表背景。 %% 找裂纹 % creal = real(c); % cimga = imag(c); % figure; % plot(creal*1000,cimga*1000,'bo') % hold on; % figure; % plot(creal*1000,cimga*1000,'rx')% b = B{36}; %??? % bim = bound2im(b,m,n); % figure;imshow(bim);title('裂纹'); % imwrite(bim,'E:\裂纹\标记后背景\18.png') %??? % z = frdescp(b); %傅里叶描述子,复数 % l = length(z); % a = round((l-36)/2); % z(1:a) = 0; % z(l-a+1:l) = 0; % c = z(a+1:a+36); % creal = real(c); % cimag = imag(c); % f = [creal;cimag]; % save('E:\裂纹\标记后背景\18.mat','f'); %??? %% % s36 = ifrdescp(c); %傅里叶描述的逆变换 % s36im = bound2im(s36,m,n); % figure;imshow(s36im);title('s36'); % s = diameter(L); % max(L(:)); %区域和孔洞的边界数 % numel(B)-1; %孔洞数 % boundaryEnclosed = find(A(:,k)) % STATS = regionprops(L,'all') % size(STATS, 1) % % for i = 1 : size(STATS, 1) % boundary = STATS(i).BoundingBox; % rectangle('Position',boundary,'edgecolor','r' ); % end这个程序是我用来做测试用的,已经汇集到一起了。想要运行这个程序的话,需要自定义的工具箱,留下邮箱即可。