上次本來想介紹怎麼計算封閉連續區域的面積和周長,結果不小心說成了sobel算子的介紹,這次真的介紹如何計算面積和周長。這裏試驗用到三幅圖片,test1,test2,test3.test4如下圖所示:
圖1 實際場景中圖片
注意,這裏已經將物體從背景中分離開,即背景爲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