您的位置:首页 > 运维架构

封闭连续区域的面积和周长计算【2】--面积与周长

2013-12-13 09:24 507 查看
上次本来想介绍怎么计算封闭连续区域的面积和周长,结果不小心说成了sobel算子的介绍,这次真的介绍如何计算面积和周长。这里试验用到三幅图片,test1,test2,test3.test4如下图所示:

 



                                            图1 实际场景中图片





图2 人造数据集,场景包含多个物体





图3 人造数据集,边界棱角较多





图4 假想图像

注意,这里已经将物体从背景中分离开,即背景为0,前景在计算中会被设置为255,即黑色代表背景,白色代表物体,分离的办法很多,暂不解释,不然又要走题了。

首先介绍的是脚趾头法,为什么叫脚趾头法呢,因为用脚趾头都能想到这个方法,就是直接统计像素点,对于周长就计算边界像素点个数,面积就计算整个物体包含像素点个数。对图片test1和test3计算结果如表1所示。

代码如下,注意这里使用的是VS208+OpenCV 2.4 环境:

int main( int argc, char**
argv )

{

    Mat image,image_gray,image_edge,image_bin; //

        image=cv::imread("test3.bmp",1);

        cvtColor( image, image_gray, CV_BGR2GRAY );//变成灰度图

       threshold(image_gray,image_bin,10,255,THRESH_BINARY);//二值化图像

    int start;

       double timeConsume;  

       // 统计像素点个数,慢速

       double circle=0;

       double area=0;

       start=GetTickCount();

       Canny(image_bin,image_edge,100,200,3);

       image_bin.convertTo(image_bin,CV_64FC1);

       image_edge.convertTo(image_edge,CV_64FC1);

       for(int p=0;p<image_bin.rows;p++)

       for(int q=0;q<image_bin.cols;q++)

       {     {

              if(image_edge.at<double>(p,q)>0)
 circle++;

              if(image_bin.at<double>(p,q)>0)
         area++;          

              }           

       }

              timeConsume=(GetTickCount()-start)*1.0/1000;//
test  0.171 test3 1.782

       return 0;

}

 看起来很简单,如其名字,但是现在用的是效率很高的C/C++尚且需要那么长时间,想想其他编译器吧,只能说这个算法效率太差!算法的总结如表2所述。

       接着头脑法,本质还是统计像素点,但用大脑对统计的方法做出了改进,使之效率飞升,在计算中我们常常用到像素点的统计,但是没有引起重视,本质原因就是统计像素点我们一般直接在matlab中敲命令add即可,真方便,可惜离开了matlab命令就用不上去了。所以用大脑考虑从这一点出发,设计快速统计的方法,计算结果如表1.

 

代码如下:

 

       // //统计像素点个数,快速

       ////////canny算子来提取边界

       // start=GetTickCount();

       // Canny(image_bin,image_edge,100,200,3);

   //image_edge.convertTo(image_edge,CV_64FC1);

       //image_edge=addmatrix(image_edge);

       //image_edge=addmatrix(image_edge.t());

       //double circle=image_edge.at<double>(0,0)/255;

 

       //image_bin.convertTo(image_bin,CV_64FC1);

       //image_bin=addmatrix(image_bin);

       //image_bin=addmatrix(image_bin.t());

       //double area=image_bin.at<double>(0,0)/255;

        //timeConsume=(GetTickCount()-start)*1.0/1000;

   //addmatrix为我自己写的函数,但根据函数格式,应该能猜到算法

 

如表1所示效率得到了极大提高,然而本质的缺陷没有得到解决, 对该算法的分析如表2所述。

       下面就得坐下来用心好好想想问题了,考虑通过边界直接计算面积,就像计算长方形正方形面积一样,通过边界就能给出面积和周长,当然这里的图形明显比长方形和正方形复杂,但是我们学过微积分,不断分割,总能分割成近似的正方形和三角形。这时候从边界信息入手,考虑到了用链码的方法来计算周长和面积。

 

代码如下:

…………..

…………..

 

(等待添加)

…………..

…………..

此时效率得到了更大的提高,毕竟只计算了边界像素点,对其详细分析如表2所述。

 

和大家经验一样,很多好的算法OpenCV里面已经自带了,这些工具包的目的是方便大家使用,但是使用多了就会发现很多很常用的算法其实都根本不懂,时间长了之后很容易忘掉。但这里还是介绍下OpenCV自带的链码方法,名字叫眼睛法,因为用眼睛看就行了,这种方法。计算结果如表1所示

 

代码如下:

//自带函数计算

start=GetTickCount();

vector<vector<Point> > contours;

findContours(image_bin, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); double Leafarea=contourArea(contours[0]);

double Leaflength=arcLength(contours[0],1);

timeConsume=(GetTickCount()-start)*1.0/1000;

此时效率大大提高,时间消耗已经接近0,具体分析如表2所示。







 

 这里提到了OPenCV自带轮廓相关的函数,顺便给出绘制轮廓的相关代码如下

////绘制出轮廓

//image=cv::imread("test2.bmp",1);

//cvtColor( image, image_gray, CV_BGR2GRAY );//变成灰度图

//Mat dst = Mat::zeros(image.rows, image.cols, CV_8UC3);

//vector<Vec4i> hierarchy;

//findContours( image_gray, contours, hierarchy,       CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE );

//int idx = 0;

//for( ; idx >=0; idx = hierarchy[idx][0] )//画出所有的轮廓,idx为索引

//{

//Scalar color( rand()&255, rand()&255, rand()&255 );

//drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );

//}

//namedWindow( "Components", 1 );

//imshow( "Components", dst );

//waitKey(0);

从表1细心的可以发现,用像素点统计的方法和链码的方法在计算面积上差异比较大,在test1测试中差异所占比例为(386607-385016)/386607=0.41%,在test3测试中差异所占比例为(82376-81428)/82376=1.15%.

假设像素点为矩形,放大像素点如图4所示,有个物体覆盖了三个像素带,按照统计像素点个数的方法面积计算为3. 而用链码计算面积则为2*2/2=2,此时明白统计像素点仅仅是统计像素点个数而不是计算面积,用像素点个数来逼近面积,当边角比较多的时候这种差异会更加明显。

源自:http://blog.sina.com.cn/s/blog_c144a0e40101aa9s.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  opencv VC 算法
相关文章推荐