网上有很多博客都写了理论,但是很少有代码 就算有也是opencv c++的代码
所以贴一个自己写的matlab版本
otsu理论的话, 看冈萨雷斯的《数字图像处理》 第10.3.6节 多阈值处理
看完书,按照书上的理论来写的
otsu 双阈值函数:
function [t1,t2]=DoubleOtsuThresh(img)
%
% Otsu 双阈值求解
% 输入 图像img,输出 最优阈值t1和t2(归一化,范围在[0,1])
%
%
BinsNum = 256;
hist = imhist(img,BinsNum);
p = hist / sum(hist); % 直方图的概率密度函数
mG= sum(p .* (1:BinsNum)'); % 全局均值
P1 = cumsum(p); % 概率分布
m1 = cumsum(p .* (1:BinsNum)')./P1; % 256*1 每个阈值的前景平均灰度
% 根据算法理论,从k2+1累加到L-1,可以先倒着累加再翻转回来
P3= cumsum(flip(p));
m3 = cumsum(flip(p) .* flip(1:BinsNum)')./P3;
P3=flip(P3);
P3=[P3(2:end) ;0]; %P3的索引用k2,则P3(1)实际上是从p(2)加到p(256),所以要移动一个
%移动后也用不到P3(1)这个值,因为k2>k1>1
m3=flip(m3);
m3=[m3(2:end) ;0];
% m2 P2为 k1*k2的索引矩阵,为 256*256 的上三角矩阵 因为k1<k2
m2=zeros(BinsNum,BinsNum);
P2=zeros(BinsNum,BinsNum);
for k1=1:BinsNum-2
for k2=k1+1:BinsNum-1
P2(k1,k2)=1-P1(k1)-P3(k2);
m2(k1,k2) = sum( (k1+1:k2)' .* p(k1+1:k2) )./ P2(k1,k2) ;
end
end
% 遍历k1,k2各种组合 求方差
variance=zeros(BinsNum,BinsNum);
for k1=1:BinsNum-2
for k2=k1+1:BinsNum-1
% variance 为256*256 的上三角矩阵 因为l1<k2
variance(k1,k2) = P1(k1)*(m1(k1)-mG)^2 + ...
P2(k1,k2)*(m2(k1,k2)-mG)^2 + ...
P3(k2)*(m3(k2)-mG)^2;
end
end
% 最大方差点即为最优阈值
[~,index] = max(variance(:));
[index_row,index_col] = ind2sub(size(variance),index);
%灰度值为[0,255] 因此需要-1
t1= (index_row-1)./(BinsNum-1);
t2= (index_col-1)./(BinsNum-1);
end
根据上面求得的两个阈值,把图分成0 127 255三个灰度值显示的函数:
function out=img2gray(img,t1,t2)
[M,N]=size(img);
out=zeros(M,N);
for m=1:M
for n=1:N
if img(m,n)<t1
out(m,n)=0;
else
if( img(m,n)>t1 && img(m,n)<t2)
out(m,n)=127; %灰度级可调
else
out(m,n)=255; %灰度级可调
end
end
end
end
end
测试函数
%% 图B 彩色图
imgB =imread('window.jpg');
imgB=rgb2gray(imgB);
imgB=im2double(imgB);
%% 先高斯滤波平滑图B,去高频,再用阈值处理
gaussH=fspecial('gaussian',[5 5],10);
smoothB=imfilter(imgB,gaussH);
[t1B,t2B] =DoubleOtsuThresh(smoothB);
% 如果imgB是unit8类型,则灰度范围在[0,255] 要把算法得到的两个阈值乘255
% 如果imgB是[0,1]范围的double类型则不用乘
% T1B=t1B*255;
% T2B=t2B*255;
T1B=t1B;
T2B=t2B;
J9=img2gray(smoothB,T1B,T2B); %转为灰度图
figure
imshow(J9,[]);title('平滑+双阈值分割'); % imshow里面J9后面的[]相当于直方图均衡效果,让灰度值均匀分布,可以去掉对比一下
%%
% —— 图D —— %
imgD=imread('ladder.jpg');
% imgD=rgb2gray(imgD);
[t1D,t2D] =DoubleOtsuThresh(imgD);
T1D=t1D*255;
T2D=t2D*255;
% 分为 0 127 255 显示
J8=img2gray(imgD,T1D,T2D);
figure
subplot 121
imshow(imgD);title('原图D');
subplot 122
imshow(J8,[]);title('双阈值分割');
%% 用书上的图测试算法,输出的两个阈值和书上结果一样 80和177
imgC=imread('iceberg.tif'); % 用书上例子的图片测试,验证算法正确与否
[t1,t2] =DoubleOtsuThresh(imgC);
T1=t1*255;
T2=t2*255;
J7=img2gray(imgC,T1,T2);
figure
subplot 121
imshow(imgC);title('原图C');
subplot 122
imshow(J7,[]);title('双阈值');
disp([' otsu双阈值为: ' ,'t1=',num2str(T1),' t2=',num2str(T2)])