特征点识别(Breakpoint/Dominant-points Classsification)——AKC函数的分析与实现
Posted on 2012-11-18 20:43 子水 阅读(1919) 评论(4) 收藏 举报摘要:本文所述算法来自IEEE PAMI的文章"Multiprimitive Segmenatation of Planar Curves-A Two-Level Breakpoint Classification and Tuning Approach"。大家可以到百度文库:http://wenku.baidu.com/view/e01f28f10242a8956bece43f.html?st=1下载。如果没有文库号的,可以到新浪爱问:http://ishare.iask.sina.com.cn/f/34715669.html免费下载。本文算法程序均系作者原创,欢迎各位研究指正。如需转载,请注明出处。
一、AKC函数介绍
首先把特征点分为两类,一类角点(corners),一类平滑结点(Smooth joints)。(注意:论文中把特征点称为Breakpoints断点,两者是同一概念。如果宽泛的将,有时我们也把特征点或者断点统称为角点)。其中角点类表明其两侧具有不连续的切线。其中平滑结点类表明其两侧具有不连续的曲率。如图1图2。

所谓AKC函数,即Adaptive k-Curvature Function自适应的k曲率函数。所谓k曲率有兴趣的读者,可以参考其鼻祖文章"Angle Detection on Digital Curves",下载地址:http://ishare.iask.sina.com.cn/f/34715883.html。其实AKC函数定义就是基于k曲率值。其定义如下:考虑三个连续的特征点Pi-1,Pi和Pi+1,由Pi-1Pi,PiPi+1组成的两条线段Si和Si+1。定义Si和Si+1线段长度为l1和l2,k=min(l1,l2),并且Pi点的ROS(支撑区域)为[Pi-k~, Pi+k~],其中k~=k/2。定义点Pj属于[Pi-k~, Pi+k~]的k向量为:
a~jk=(Pj+(x)-Pj(x),Pj+(y)-Pj(y)), (1a)
a~jk=(Pj-(x)-Pj(x),Pj-(y)-Pj(y)), (1b)
这里Pj+=Pj+k~,Pj-=Pj-k~,那么ajk和bjk之间的k-cosine的值为
cjk=(a~jk•b~jk)/||a~jk||||b~jk|| (2)
定义Pi点的AKC函数为cjk任意的Pj属于[Pi-k~,Pi+k~],如图3。

注意到AKC函数是水平对称的,而且和起点无关。这就克服了其他算法中方向和起始点的问题。那么图1和2的AKC函数到底如何呢?请看图4。

注意到在图4a,4b,4c,4d和4fAKC函数中存在全局最大值,这5幅图均来自角点的AKC函数。由图可见在角点处切线不连续,且k-cosine值很大。然而,对于平滑结点,其切线连续,故其k-cosine值平滑变化,而且其局部极大值并不显著。如图4g,4h和4i。很显然,如果在断点处存在全局最大值,那么为角点;否则为平滑结点。
二、算法实现
1、定义数据结构
由于算法处理对象为平面曲线的轮廓,因此需要考虑轮廓点,角点,平滑结点等数据。因此定义结构体如下图5:

其中x:轮廓点的横坐标
y:轮廓点的纵坐标
csp[0]:轮廓点顺序1,2,3,...
csp[1]:角点顺序1,2,3,...不为角点的轮廓点记为0
csp[2]:该点为corner置1;smooth joint置2
csp[3]:该点前面的曲线为corner置1;smooth joint置2
csp[4]:该点后面的曲线为corner置1;smooth joint置2
csp[5...]:冗余空间先置为0
prior:结点的前驱指针
next:结点的后继指针
View Code
1 typedef struct LNode 2 { 3 struct LNode *prior; 4 int x; 5 int y; 6 int csp[6]; 7 struct LNode *next; 8 }LNode, *Link;
其中LNode为结点类型,Link为指向结点类型的指针。定义完结点,下面就是创建结点,初始化结点,释放结点等函数的编写。在此不再赘述。
View Code
1 bool MakeNode(Link &p) 2 { 3 // 构造p指向的LNode结点,成功返回true,失败返回false 4 p = new LNode; 5 return p!=NULL; 6 } 7 8 void FreeNode(const Link &p) 9 { 10 // 释放p指向的LNode结点 11 delete p; 12 } 13 14 void InitNode(const Link &p) 15 { 16 // 初始化p指向的LNode结点 17 p->prior = NULL; 18 p->x = 0; 19 p->y = 0; 20 p->csp[0] = 0; 21 p->csp[1] = 0; 22 p->csp[2] = 0; 23 p->csp[3] = 0; 24 p->csp[4] = 0; 25 p->csp[5] = 0; 26 p->next = NULL; 27 }
OK!那么最终的平面曲线上的点应该被表现为图6,其中黑色代表头结点,内容为空。从第二结点开始存储轮廓点,其中红色的轮廓点为特征点,蓝色的轮廓点为普通非特征点。考虑到曲线轮廓的闭合性,也方便将来的遍历操作,这里设置为循环链表。
现在基本的数据结构有了,下面讨论AKC函数的实现。其设计思想,上一部分和论文已经说得很清楚了。首先就是遍历整个轮廓中的特征点,对于每一个特征点,确定其计算区间,再求出其a~jk和b~jk,然后算出一系列的cjk,通过找寻特征点出是否有最大值来判断是否为角点or平滑点。下面对每一个特征点设计AKC函数如下:
View Code
1 double* AKC(Link &p, int kb) 2 { 3 // AKC函数 4 int i=0; 5 Link PJ = kPriorPoint(p, kb); // PJ is Pj Pj∈(p-k, p+k) 6 Link PJN = NULL; // PJN is Pj-, Pj-=Pj-k 7 Link PJP = NULL; // PJP is Pj+, Pj+=Pj+k 8 int ajx, ajy, bjx, bjy; 9 int tk=2*kb; 10 double *c = new double [tk+1]; 11 while(i<=tk) 12 { 13 PJN = kPriorPoint(PJ, kb); 14 PJP = kNextPoint(PJ, kb); 15 ajx = PJP->x-PJ->x; 16 ajy = PJP->y-PJ->y; 17 bjx = PJN->x-PJ->x; 18 bjy = PJN->y-PJ->y; 19 c[i] = (ajx*bjx+ajy*bjy)/(sqrt(ajx*ajx+ajy*ajy)*sqrt(bjx*bjx+bjy*bjy)); 20 PJ = PJ->next; 21 if (PJ->csp[0] == 0) 22 { 23 PJ=PJ->next; 24 } 25 i++; 26 } 27 return c; 28 }
其中Link kPriorPoint(Link &p, int k);和Link kNextPoint(Link &p, int k);函数为寻找当前节点的前面k个结点,和后面k个结点。
三、实验结果
对下图进行操作。



上图的角点(正坐标在前,纵坐标在后)49 75;49 100;65 117;156 119;156 56;72 56;共6个角点。那么使用AKC函数后,结果如何?先看看每一点的AKC函数图,从72 56点开始,逆时钟转依次显示每一点的AKC,共6幅图。






从图中可以清晰看出最后两幅图对应为角点,其余为smooth点。
四、结束语
其实AKC函数是为后面的PHF函数做准备的,因此本文没有列举大量的实例。后面的PHF函数将会有更直观的分类感受。写博客真不是件轻松的事情啊,图片还得一张张传,字还得慢慢敲......好不容易写了篇,加油吧!

浙公网安备 33010602011771号