一、hough变换的原理:
(1)xy空间一个圆对应三维参数空间一个点(a,b,r)
(2)xy空间圆上一个点(x,y)对应参数空间一条曲线
(3)xy空间圆上n个点对应参数空间n条相交于一点的曲线
注:对于原图中每一点,在参数空间确定一条曲线,经过曲线最多的点为原图中圆的参数
(1)假设原图像已经处理为二值边缘图像,扫描图中的每一个像素点:
①背景点,不作任何处理
② 目标点,确定曲线:参数空间上的对应曲线上所有点的值累加1
(2)循环扫描所有点
(3)参数空间上累计值为最大的点(a*,b*,r*)为所求圆参数
(4)按照该参数与原图像同等大小的空白图像上绘制圆
三维参数空间,计算量大,可以采样其他形式,如极坐标式,进行进一步简化。
二、主函数
I=rgb2gray(imread('renyan.png'));
BW = edge(I,'sobel'); %目的检测圆的轮廓,如果直接就是圆图像,不用这个
% 用sobel进行边缘检测
%设置参数:
%检测的圆半径步长为0.5
Step_r = 0.5; %步长过短,运行时间会很长
%角度步长0.1,单位为弧度
Step_angle = 0.1; %步长过短,运行时间会很长
%最小圆半径30(圆半径根据实际检测调整)
minr =30;
%最大圆半径50
maxr=50;
%以thresh*hough_space的最大值为阈值,thresh取0-1之间的数
thresh = 0.8;
circleParaXYR=[];
%---------------------------------------------------------------------------------
%开始检测
[Hough_space,Hough_circle_result,Para] = Hough_circle(BW,Step_r,Step_angle,minr,maxr,thresh);
circleParaXYR=Para;
axis equal
figure(1);
imshow(BW,[]),title('边缘');
axis equal
figure(2);
imshow(Hough_circle_result,[]),title('检测结果');
axis equal
figure(3),imshow(I,[]),title('检测出图中的圆')
hold on
%---------------------------------------------------------------------------------
%以红色线标记出的检测圆心与圆
plot(circleParaXYR(:,2), circleParaXYR(:,1), 'r+');
for k = 1 : size(circleParaXYR, 1)
t=0:0.01*pi:2*pi;
x=cos(t).*circleParaXYR(k,3)+circleParaXYR(k,2);
y=sin(t).*circleParaXYR(k,3)+circleParaXYR(k,1);
plot(x,y,'r-');
end
三、构造Hough变换检测圆函数(主函数调用即可)
function [Hough_space,Hough_circle_result,Para] = Hough_circle(BW,Step_r,Step_angle,r_min,r_max,p)
circleParaXYR=[];
Para=[];
%得到二值图像大小
[m,n] = size(BW);
%计算检测半径和角度的步数、循环次数 并取整,四舍五入
size_r = round((r_max-r_min)/Step_r)+1;
size_angle = round(2*pi/Step_angle);
%建立参数空间
Hough_space = zeros(m,n,size_r);
%查找非零元素的行列坐标
[rows,cols] = find(BW);
%非零坐标的个数
ecount = size(rows);
% Hough变换
% 将图像空间(x,y)对应到参数空间(a,b,r)
% a = x-r*cos(angle)
% b = y-r*sin(angle)
for i=1:ecount
for r=1:size_r %半径步长数按一定弧度把圆几等分
for k=1:size_angle
a = round(rows(i)-(r_min+(r-1)*Step_r)*cos(k*Step_angle));
b = round(cols(i)-(r_min+(r-1)*Step_r)*sin(k*Step_angle));
if (a>0&&a<=m&&b>0&&b<=n)
Hough_space(a,b,r)=Hough_space(a,b,r)+1;%h(a,b,r)的坐标,圆心和半径
end
end
end
end
% 搜索超过阈值的聚集点,对于多个圆的检测,阈值要设的小一点!通过调此值,可以求出所有圆的圆心和半径返回值就是这个矩阵的最大值
max_para = max(max(max(Hough_space)));
%一个矩阵中,想找到其中大于max_para*p数的位置
index = find(Hough_space>=max_para*p);
length = size(index);%符合阈值的个数
Hough_circle_result=zeros(m,n);
%通过位置求半径和圆心。
for i=1:ecount
for k=1:length
par3 = floor(index(k)/(m*n))+1;
par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;
par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;
if((rows(i)-par1)^2+(cols(i)-par2)^2<(r_min+(par3-1)*Step_r)^2+5&&...
(rows(i)-par1)^2+(cols(i)-par2)^2>(r_min+(par3-1)*Step_r)^2-5)
Hough_circle_result(rows(i),cols(i)) = 1;%检测的圆
end
end
end
% 从超过峰值阈值中得到
for k=1:length
par3 = floor(index(k)/(m*n))+1;%取整
par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;
par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;
circleParaXYR = [circleParaXYR;par1,par2,par3];
Hough_circle_result(par1,par2)= 1; %这时得到好多圆心和半径,不同的圆的圆心处聚集好多点,这是因为所给的圆不是标准的圆
end
%集中在各个圆的圆心处的点取平均,得到针对每个圆的精确圆心和半径;
while size(circleParaXYR,1) >= 1
num=1;
XYR=[];
temp1=circleParaXYR(1,1);
temp2=circleParaXYR(1,2);
temp3=circleParaXYR(1,3);
c1=temp1;
c2=temp2;
c3=temp3;
temp3= r_min+(temp3-1)*Step_r;
if size(circleParaXYR,1)>1
for k=2:size(circleParaXYR,1)
if (circleParaXYR(k,1)-temp1)^2+(circleParaXYR(k,2)-temp2)^2 > temp3^2
XYR=[XYR;circleParaXYR(k,1),circleParaXYR(k,2),circleParaXYR(k,3)]; %保存剩下圆的圆心和半径位置
else
c1=c1+circleParaXYR(k,1);
c2=c2+circleParaXYR(k,2);
c3=c3+circleParaXYR(k,3);
num=num+1;
end
end
end
c1=round(c1/num);
c2=round(c2/num);
c3=round(c3/num);
c3=r_min+(c3-1)*Step_r;
Para=[Para;c1,c2,c3]; %保存各个圆的圆心和半径的值
circleParaXYR=XYR;
end
四、结果
(1)眼睛的虹膜检测属于较难的检测,主函数加强预处理部分可提高效果;
(2)如果直接用Hough变换检测圆,调用该函数会得到较好效果;
(3)如果是彩色复杂图像,则采用预处理将圆大致提取出来,设置好Hough变换的参数,就可以完成识别。
支持可以关注我哦,持续分享编写的代码。