原创 (原创)图像处理学习系列4:轮廓重心的提取

2009-3-10 15:36 7215 7 7 分类: 软件与OS

    接着上面的说,要做轮廓重心的提取,首先要对边界进行跟踪,得到边界的链码,但是链码在计算面积以及重心上并不好操作,一般都转为线段表来处理。
    重心的提取,这里默认为轮廓中心来使用了,因为我做的是LED中心的测量,得到的图像噪声几乎没有,LED发光还算均匀,所以使用质心发来处理得到的精度还算可以。当然也可以使用椭圆拟合得到的数据或许会比较好,但是在椭圆拟合的算法复杂度很高,相对来说不会提高太多。

  下面是特征点提取的一些资料:
基于质心法的rar


基于椭圆拟合的文档:



基于几何特性的椭圆中心象坐标的快速求取.pdfpdf

基于最小二乘法的椭圆拟合改进算法.PDFPDF


还要考虑的就是LED在同的姿态摄影到图像平面以后发生了偏移,这对于精确提取特征点的中心影响很大,下面的文档对这种情况进行了研究

视觉检测中椭圆中心成像畸变误差模型研究.pdfpdf
透视投影变换中椭圆中心畸变误差模型及其仿真研究.PDFPDF

这里我已经对边缘的提取以及跟踪做了介绍,代码也有

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;   
}

//




    
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
7
关闭 站长推荐上一条 /3 下一条