Matlab:图像轮廓的曲率计算

您所在的位置:网站首页 matlab计算圆周长 Matlab:图像轮廓的曲率计算

Matlab:图像轮廓的曲率计算

2023-11-27 01:56| 来源: 网络整理| 查看: 265

给定一个连通区域的图像,如下图所示,想要求其轮廓像素点的曲率。

理论上,下图红框中的轮廓像素的曲率应尽可能大,而蓝框的曲率应比较小。

 1.首先对图像二值化,并通过任意算子的边缘提取,得到初步的轮廓(白色像素点):

I=im2bw(I); eg=edge(I,'canny');

2.但可能存在某处的轮廓的“厚度”大于1, 这样会影响之后按照顺序的轮廓点像素提取,所以对轮廓图像eg使用bwmorph进行细化:

eg=bwmorph(eg,'thin',Inf);

得到细化后的轮廓:

3.细化后的轮廓,保证了某像素点的8邻域内,有且仅有两个其他轮廓点(一个是遍历的上一个像素点,一个是遍历的下一个像素点)。于是我们从任意一个轮廓点(sx,sy)出发, 沿着其8邻域内且未遍历过的像素点前进,直至回到起始点。除初始点外,其他点可供选择的邻域像素有且仅有一个,从而保证了程序能够正确地从初始点出发,遍历所有点后回到初始点。

因此我们定义矩阵x和y,用来存放按照顺序遍历的所有像素点的x坐标和y坐标,sx和sy为当前遍历点的坐标。dir为8邻域的方向,对于(sx,sy)的第i个邻域,其坐标为(sx+dir(i,1),sy+dir(i,2))。定义一个函数findpoint,用来查找当前邻域像素点是否已经遍历过。如果邻域像素点值为1(白色),并且未遍历过,那么sx,xy更新为这个邻域像素点的坐标,并存入x,y。为了防止进入无限循环,设置了状态f,如果8个邻域像素都不能满足条件,说明走到了断头路,那么直接break。

x=[]; y=[]; [sx,sy]=find(eg==1,1);%find the first point x=[x,sx]; y=[y,sy]; dir=[[-1,-1];[-1,0];[-1,1];[0,1];[1,1];[1,0];[1,-1];[0,-1]]; while(true) f=0; for i=1:8 if eg(sx+dir(i,1),sy+dir(i,2))==1 && findpoint(x,y,sx+dir(i,1),sy+dir(i,2)) sx=sx+dir(i,1); sy=sy+dir(i,2); f=1; break end end if (f==0)%结束条件1:走到断头路 一般情况下不会满足 break end if(sx==x(1) && sy==y(1)) %结束条件2:走到起始点 break end x=[x,sx]; y=[y,sy]; end

函数findpoint,仅当sx和sy同时出现在x y的同一个索引下的值时,才返回0,表明该点已经遍历过,否则返回1.

function f=findpoint(x,y,sx,sy) n=length(x); f=1; for i=1:n if x(i)==sx && y(i)==sy f=0; break; end end end

4.这样得到的x和y连个矩阵(列表/向量),每个x(i),y(i)坐标的点,都会和x(i-1),y(i-1)以及x(i+1),y(i+1)相邻,因此用相邻的x(i)和x(i+1)的差值来近似在x方向的导数,y同理。因此得到一阶差值(导数)矩阵dfx1、dfy1;二阶差值(导数)在一阶差值(导数)基础上继续操作即可,得到dfx2,dfy2。

dfx1=[diff(x,1),x(1)-x(end)]; dfy1=[diff(y,1),y(1)-y(end)]; dfx2=[diff(dfx1,1),dfx1(1)-dfx1(end)]; dfy2=[diff(dfy1,1),dfy1(1)-dfy1(end)];

相邻差值可以用diff直接求得,但是会丢失最后一个点的差值,用x(1)-x(end)进行补充即可。

5.现在得到的差值矩阵dfx1(i),dfy(i)表示x(i),y(i)点的“导数”,为了直观地显示图像中每个点的曲率,我们将dfx1等映射到二维矩阵dx1等,dfx1(i,j),dfy(i,j)表示点i,j的“导数”。

dx1=zeros(m,n); dx2=zeros(m,n); dy1=zeros(m,n); dy2=zeros(m,n); for i=1:length(x) dx1(x(i),y(i))=dfx1(i); dy1(x(i),y(i))=dfy1(i); dx2(x(i),y(i))=dfx2(i); dy2(x(i),y(i))=dfy2(i); end

6.按照曲率公式进行计算曲率矩阵K:

K=zeros(m,n); K=abs(dx1.*dy2-dx2.*dy1)./((dx1.^2+dy1.^2+0.1).^(3/2));

为了防止分母为0,在分母加入0.1。

直接绘制K的图像:

颜色越白,代表曲率越大, 但是这样的结果很不满意,究其原因,是因为用离散点的差值来近似连续曲线的导数,误差太大(比如用(sin(1)-sin(0))/(1-0)近似sinx在x=0的导数,得到0.017,这和实际导数为1的误差过大)。因此,通过相邻差值的方法计算导数或者曲率效果并不好。

因此,我们试图去使用x(i+1)-x(i-1)代替x(i+1)-x(i)来近似x(i),y(i)在x方向的导数:

dfx1=([diff(x,1),x(1)-x(end)]+[x(1)-x(end),diff(x,1)])/2; dfy1=([diff(y,1),y(1)-y(end)]+[y(1)-y(end),diff(y,1)])/2; dfx2=([diff(dfx1,1),dfx1(1)-dfx1(end)]+[dfx1(1)-dfx1(end),diff(dfx1,1)])/2; dfy2=([diff(dfy1,1),dfy1(1)-dfy1(end)]+[dfy1(1)-dfy1(end),diff(dfy1,1)])/2;

但是结果仍不可观,似乎准确地在离散的点中准确地近似导数是不太可能的事(或者尝试到附近n个点的差值?),特别是在特别小的图像而言。试想曲率的定义似乎和“直率”恰恰相反,也就是说这个像素点附近(这里指的是连续)的点呈现地越接近于直线,曲率越低,反而如果发生拐弯,曲率应该比较大,这不和线性回归有点像么!

因此,采用了线性回归的思路,对每个点的附近的点组成的线段的弯曲程度进行计算。假设取点x(i),y(i)之前共pre和点,之后共next个点,那么线段由pre+next+1个点组成,其坐标为x(i-pre:i+next),y(i-pre:i+next)。

比如当前点用红色表示,之前的点用绿色表示,之后的点用蓝色表示,对他们进行线性回归后,所得的不呈直线的概率,或者回归的误差等参数都可以作为这个点的值。在此以regress返回的stats(1)为例,直线越直,这个stats(1)越接近于1,反之接近于0.因此在这用1-stats(1)表示弯曲程度。依次计算后得到的K为:

白色亮点分布在拐弯处,还是比较符合我们的要求的,OVER。

完整代码:

(为了避免讨论临界值问题,在线性回归时对x和y的边界进行了前后扩充)  

I=imread('平滑.jpg'); I=im2bw(I); [m,n]=size(I); eg=edge(I,'canny'); eg=bwmorph(eg,'thin',Inf); x=[]; y=[]; [sx,sy]=find(eg==1,1);%find the first point x=[x,sx]; y=[y,sy]; dir=[[-1,-1];[-1,0];[-1,1];[0,1];[1,1];[1,0];[1,-1];[0,-1]]; while(true) f=0; for i=1:8 if eg(sx+dir(i,1),sy+dir(i,2))==1 && findpoint(x,y,sx+dir(i,1),sy+dir(i,2)) sx=sx+dir(i,1); sy=sy+dir(i,2); f=1; break end end if (f==0) break end if(sx==x(1) && sy==y(1)) break end x=[x,sx]; y=[y,sy]; end %用线性回归类比曲率 pre=5; next=5; x=[x(end-pre+1:end),x,x(1:next)]; y=[y(end-pre+1:end),y,y(1:next)]; K=zeros(m,n); for i=pre+1:length(x)-next X=x(i-pre:i+next); X=[ones(size(X')),X']; Y=y(i-pre:i+next); [b,bint,r,rint,stats]=regress(Y',X,0.05); K(x(i),y(i))=1-stats(1); end %传统方式计算曲率的代码 % dx1=zeros(m,n); % dx2=zeros(m,n); % dy1=zeros(m,n); % dy2=zeros(m,n); % dfx1=[diff(x,1),x(1)-x(end)]; % dfy1=[diff(y,1),y(1)-y(end)]; % dfx2=[diff(dfx1,1),dfx1(1)-dfx1(end)]; % dfy2=[diff(dfy1,1),dfy1(1)-dfy1(end)]; % % dfx1=([diff(x,1),x(1)-x(end)]+[x(1)-x(end),diff(x,1)])/2; % dfy1=([diff(y,1),y(1)-y(end)]+[y(1)-y(end),diff(y,1)])/2; % dfx2=([diff(dfx1,1),dfx1(1)-dfx1(end)]+[dfx1(1)-dfx1(end),diff(dfx1,1)])/2; % dfy2=([diff(dfy1,1),dfy1(1)-dfy1(end)]+[dfy1(1)-dfy1(end),diff(dfy1,1)])/2; % % for i=1:length(x) % dx1(x(i),y(i))=dfx1(i); % dy1(x(i),y(i))=dfy1(i); % dx2(x(i),y(i))=dfx2(i); % dy2(x(i),y(i))=dfy2(i); % end % % K=zeros(m,n); % K=abs(dx1.*dy2-dx2.*dy1)./((dx1.^2+dy1.^2+0.1).^(3/2)); function f=findpoint(x,y,sx,sy) n=length(x); f=1; for i=1:n if x(i)==sx && y(i)==sy f=0; break; end end end



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3