运用改进的各向异性扩散 + 傅里叶形状描述器 + SVM分类器 对微裂纹进行检测

数据集下载: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
这个程序是我用来做测试用的,已经汇集到一起了。想要运行这个程序的话,需要自定义的工具箱,留下邮箱即可。

猜你喜欢

转载自blog.csdn.net/qq_38784454/article/details/79782375