接着上面的说,要做轮廓重心的提取,首先要对边界进行跟踪,得到边界的链码,但是链码在计算面积以及重心上并不好操作,一般都转为线段表来处理。
重心的提取,这里默认为轮廓中心来使用了,因为我做的是LED中心的测量,得到的图像噪声几乎没有,LED发光还算均匀,所以使用质心发来处理得到的精度还算可以。当然也可以使用椭圆拟合得到的数据或许会比较好,但是在椭圆拟合的算法复杂度很高,相对来说不会提高太多。
下面是特征点提取的一些资料:
基于质心法的:
基于椭圆拟合的文档:基于几何特性的椭圆中心象坐标的快速求取.pdf
基于最小二乘法的椭圆拟合改进算法.PDF
还要考虑的就是:
LED在同的姿态摄影到图像平面以后发生了偏移,这对于精确提取特征点的中心影响很大,下面的文档对这种情况进行了研究视觉检测中椭圆中心成像畸变误差模型研究.pdf
透视投影变换中椭圆中心畸变误差模型及其仿真研究.PDF
这里我已经对边缘的提取以及跟踪做了介绍,代码也有
http://blog.ednchina.com/opencv2008/207551/message.aspx下面只用把链码表转换为线段表就可以了需要注意的是:转换的过程中使用的查表的方式,是建立在“向右看”的准则基础上的还有就是代码的缺点是:排序的时候使用的冒泡排序,效率很低。////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//线性表的操作
/************************************************************************************
链码表转换为第一类线段表
链码表结构的CODE[0] CODE[1] 记录边界的第一个点 CODE[2] 记录表的链码数目
第一类线段表结构记录每个节点表示线段的两端,对于奇异点,左右端点相同
////
链码表到第一类线段表的转换是通过查表来完成的
////
参数
链码表指针
线段表指针
他们的空间在外部已经开辟
0为中间点
1为左端点
2为右端点
3是奇异点
*************************************************************************************/
int ImgCode2Line(int *code,ImgDot *LineCode)
{
int inc[8][2]={{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1},{1,1}}; //搜索方向与坐标
int code2line_table[8][8]=
{
{0,0,0,0,2,2,2,2},
{1,1,1,1,0,3,3,3},
{1,1,1,1,0,0,3,3},
{1,1,1,1,0,0,0,3},
{1,1,1,1,0,0,0,0},
{0,3,3,3,2,2,2,2},
{0,0,3,3,2,2,2,2},
{0,0,0,3,2,2,2,2}
};
int code_num = code[2];//链码的数目
int Line_num = 0; //端点数目
int code_in=0;
int code_out=0;
int Dot_Type=0;
long temp_x = code[0];
long temp_y = code[1];
code[code_num + 3] = code[3]; // 构成一个循环链表
//提取线段的左右端点
for (int i = 3; i < code_num + 3 ; i++)
{
code_in = code
;
code_out = code[i+1];
Dot_Type = code2line_table[code_in][code_out]; //计算端点类型
temp_x = temp_x + inc[code][0]; //得到下一个边界点的坐标
temp_y = temp_y + inc[code][1];
if (Dot_Type)
{
LineCode[Line_num].cx = temp_x; //左、右端点填一次
LineCode[Line_num++].cy = temp_y;
if (Dot_Type==3) //奇异点, 再填1次
{
LineCode[Line_num].cx = temp_x;
LineCode[Line_num++].cy = temp_y;
}
if ( LineCode[Line_num-1].cx==0)
{
AfxMessageBox("0");
}
}
}
if ( Line_num % 2 )
{
return -1;
}
//对Y进行冒泡排序
ImgDot temp={0,0};
int j=0;
for (i = 1 ; i < Line_num ; i++ )
{
for (j = 0;j < Line_num - 1; j++)
{
if (LineCode[j].cy > LineCode[j+1].cy)
{
//temp = LineCode[j];
//LineCode[j] = LineCode[j+1];
//LineCode[j+1] = temp;
memcpy(&temp,&LineCode[j],sizeof(ImgDot));
memcpy(&LineCode[j],&LineCode[j+1],sizeof(ImgDot));
memcpy(&LineCode[j+1],&temp,sizeof(ImgDot));
}
}
}
//对行内的X进行排序
for (i=1;i<Line_num;i++)
{
for (j = 0;j < Line_num -1 ; j++)
{
if (LineCode[j].cy != LineCode[j+1].cy)
continue;
if (LineCode[j].cx > LineCode[j+1].cx)
{
temp = LineCode[j];
LineCode[j] = LineCode[j+1];
LineCode[j+1] = temp;
}
}
}
return Line_num;
}
/***********************************************************************************
求轮廓的面积
线段表的长度累加得到面积
************************************************************************************/
long ImgOutlineArea(ImgDot *LineCode , int Line_num)
{
int i=0;
long area=0;
for (i = 0;i < Line_num ; i+=2)
{
area += ( LineCode[i+1].cx - LineCode.cx + 1 ); //累加线段表的长度
}
return area;
}
/*************************************************************************************
求轮廓的重心
//对二值图像进行操作
*************************************************************************************/
ImgDot ImgCenterGravity(ImgDot *LineCode , int Line_num)
{
double area=0,len=0,tempx=0,tempy=0;
ImgDot tempdot={0,0};
for (int i=0 ; i<Line_num ; i+=2)
{
len = LineCode[i+1].cx - LineCode.cx + 1 ; //计算线段长
area += len; //
tempx += len*(LineCode[i+1].cx + LineCode.cx)/2; //x方向累加
tempy += len*(LineCode[i+1].cy + LineCode.cy)/2;
tempdot.cx = (long)(tempx / area); //计算重心
tempdot.cy = (long)(tempy / area);
}
return tempdot;
}
//
文章评论(0条评论)
登录后参与讨论