该节来进一步讨论怎么样进行虹膜眼睛的位置确认,对一副图像
首先我们可以很直观的看出眼睛包括内外两个明显的轮廓,并且都呈现圆形,正是这种特征给了我们检测虹膜的一些方法启示。首先我们说怎么样检测内圆轮廓。
Hough变换在图像的形状检测上非常有效,具体就不介绍了,自己去百度下大概吧,贴一个参考下:
http://blog.163.com/yuyang_tech/blog/static/21605008320130233343990/
它使用的前提是输入图像必须是二值图,也就是黑白图,比如说像下面这种(这个是大圆了,提前用了一下):
这种二值图中,形成的形状越好,检测出来的效果越好。像这个,很明显就可以看出一个圆的轮阔了,加上它周围的噪声不是很多(多了检测的效果就很差了),那么我们在用hough变换的圆检测的时候就很容易检测出来了。
回归正题,我们先讲怎么才能得到内圆的二值棱阔图了?这里我们依照前面已经求出了圆心坐标,使用区域生长方法来进行求取轮廓。
(上节求圆心坐标程序可以表示为:)
%先自己读入图像
%滤波的大小一般为20~30效果较好
x = 30;
y = 30;
img = medfilt2(img,[x y],'symmetric');
num_x = sum(img);
num_y = sum(img');
circle_in_x = find_min(num_x);
circle_in_y = find_min(num_y);
那么中心点可以认为(circle_in_x,circle_in_y ),中心大致在这个范围。
区域生长方法简单来说就是从最初给的一个点或者一个区域作为母点,在母点的邻域点,如果灰度值之差小于某一个阈值,就认为这个区域与母区域相同,把它们作为同一体,形成类似新的母点,然后重复上面的操作,直到最边缘点的附近点灰度值都相差很大时,停止生长,那么这个过程形成的整个区域就是区域生长的结果。区域生长的最大好处是可以把灰度值在一定阈值内相同的部分结合起来,也就是找到相同的部分。这种方法之所以可以用到内圆检测上是因为内外圆灰度值差别大,而且他们各自内部的灰度值基本上一样。
像上面的图,假设点1是母点,此时对点1进行四连通区域生长处理,它的附近有4个像素点,分别把他们的灰度值与点1的进行比较,在设置一个阈值(这个值很讲究,设的大小直接影响生长的区域大小),如果灰度差在阈值范围内,就认为它们是同一类点,在下一次计算时就用最外面的点进行向外生长了。
好了,对于一副虹膜灰度图,已经知道了眼睛中心大概位置,那么把这个点当做母点进行区域生长。设置阈值为25(图可以看出内圆基本都是黑色的),下面是参考修改的matlab程序:
function J=regiongrowing(I,y,x,Threshold) % % 函数功能:是找到一片与种子点灰度相近的一片区域 % %------------------------------输入参数----------------------------- % I:灰度图像 % 阀值选择范围 Threshold :(附近点的灰度与种子点的差值) % 区分内轮廓时: 20~30 ,30较好 % 种子点位置 (x,y) %------------------------------输出参数----------------------------- % Note: 函数返回: J最终为轮廓的边缘图像,为hough变换寻找内圆做准备 %------------------------------------------------------------------- I = double(I); %转换类型 J = zeros(size(I)); % 定义一个输出 Isizes = size(I); %图像大小 reg_mean = I(x,y); % 种子点灰度值 reg_size = 1; % 分割区域内点的个数(初始值就为种子点一个) %开辟一个区域存放满足区域条件的点 neg_free = 10000; neg_pos=0; neg_list = zeros(neg_free,3); pixdist=0; %检测点与阈值的差值 %种子点附近--四连通区域的点 neigb=[-1 0; 1 0; 0 -1;0 1]; %相对种子点的x与y的增量 number = Isizes(1)*Isizes(2); %循环的最大次数 % %循环开始,结束条件:当整个可用种子列表中最小的灰度值都小于阈值 % 或者所有点都检测完了 while(pixdist<Threshold&&number>0) number = number - 1; % num = num + 1; % Add new neighbors pixels for j=1:4, %计算种子点附近的四连通区域点 xn = x +neigb(j,1); yn = y +neigb(j,2); % 检测是否在图像范围内 ins=(xn>=1)&&(yn>=1)&&(xn<=Isizes(1))&&(yn<=Isizes(2)); % Add neighbor if inside and not already part of the segmented area if(ins&&(J(xn,yn)==0)) %在范围之内并且该点没有被检测到 neg_pos = neg_pos+1; neg_list(neg_pos,:) = [xn yn I(xn,yn)]; %记录该点位置以及灰度值 J(xn,yn)=1; %并且标记该点 end end %-------继续扩充列表内存 if neg_pos+10>neg_free neg_free=neg_free+10000; neg_list((neg_pos+1):neg_free,:)=0; end %找到整个可用候选种子列表中灰度最近的那个点作为新的检测种子点 dist = abs(neg_list(1:neg_pos,3)-reg_mean); %选择候选种子点中灰度差异性最小的那个点作为下一个要进行检测扩展的种子点 [pixdist, index] = min(dist); %最小值是pixdist 在列向量的第index个 %满足要求的点记录下来 J(x,y)=2; reg_size=reg_size+1; %已经找到的区域内的点的平均灰度值 %这个时候不再是以原始的种子点灰度值作为检测值,加大准确性 reg_mean = (reg_mean*reg_size + neg_list(index,3))/(reg_size+1); x = neg_list(index,1); y = neg_list(index,2); %下一个检测的新种子坐标 %将这个要检测的点从候选种子列表中剔除掉 %并非真的剔除了,只是指向了下一次形成的新的种子点位置,看起来就是剔除了 neg_list(index,:)=neg_list(neg_pos,:); neg_pos=neg_pos-1; end %找到区域内的点 J=J>1; %将二值图进一部转化为边缘检测的图像 edg = edge(J,'canny'); J=edg;
整个程序就是这样(对区域生长算法不明白的可以去搜相关原理介绍)。中间可能还有一些注意,比如阈值不能太大,否则内存不足等,具体就不讲解了,有点复杂,自己慢慢研究吧,看懂了就行。
最后不断调整阈值大小直到达到最佳效果,这里运行如下:
J=regiongrowing(img,circle_in_x,circle_in_y,25);
imshow(J),
就可以得到下下面这样的图了:
这个图是不是看到还行,基本上已经把内圆分出来了,剩下的在把上图进行一次边缘检测,也就是这段程序:edg = edge(J,'canny');
J=edg;
这样得到的图像为:
至此我们得到了较完美的内圆轮廓图了,前面讲到只需要在对这个二值图进行hough圆检测就可以检测出内圆了(下一节...)。
为什么不直接对原来的图像进行边缘检测处理后再用hough变换检测了?首先原来图像存在好几个圆,你能检测到哪一个?其次,各种噪声影响很大,直接hough变换很不好,像上述处理过后只有简单的二值图,这样的houg变换不仅速度快,而且准确率很高。大家仔细研究下hough变换会发现这种方法其实有很多缺点,运行占内存、速度慢、受噪声影响大等等,这些条件就要求你必须准备一个很好的二值图来喂它,这样结果才会好些。