二值圖像 b(x,y) = 1 表示前景部分,b(x,y) = 0 表示背景部分。其基本幾何特性包括:‘
1 面積
對整個圖像區域進行積分,使用零階矩表示為 。
2 位置
將圖像區域看作一種均勻物質構成得平面,物體得質心即為區域中心;使用一階矩表示如下:
,
,進一步改寫得:
,
。
3 朝向
假設物體沿某一方向比較長,其正交方向比較短,該方向定義為物體朝向。使用最小轉動慣量來定義物體長軸,即尋找一條直線,使得物體上所有點到直線上距離平方和最小,定義如下:
, r 表示物體上點到直線的最小距離。
通過最小化 E,可以計算出物體朝向直線,具體如下:
1)假設二值圖像朝向直線已知,使用 定義為
,如下圖:
如上圖所示,由於 ,可以建立等式
,化簡得
。
2)對直線 L 上任意點 ,以點
作為參考點,建立參數方程如下:
,s 表示點
距離參考點
的距離。
3)由於 ,(x,y) 表示圖像上的點,
表示直線上的點,將參數方程帶入該等式,使得兩個變量
簡化為一個變量 s,如下:
,
,
對 s 求導,當導數為零時表示(x,y)到直線 L 上距離最近 ,
計算得 ,將 s 帶入
得
,
,最終推導出轉動慣量方程為
,其中,
為待求解直線參數。
4)令 ,
,
將無關變量提出積分符號前,
同時除以 得
,
由於 為圖像中心,則最小轉動慣量對應得軸過圖像中心。
5)通過 4)結論,直線 L 的確定可轉換為對選擇角度的求解,具體如下:
令 ,將圖像上點絕對坐標轉換為相對於圖像中心的相對坐標,帶入直線 L 方程得:
,重新改寫
,
當前 E 僅包含未知量 ,再次改寫
,其中
,
,
,
使用倍角公式 ,
,
,
通過以上分析,二值圖像朝向直線為經過中心點,且滿足 的 直線,其中,a, b, c 為圖像二階矩。
4 形狀
在分析二值圖像朝向時,,該方程是關於
的二次方程,
其系數 a, b, c 為可構成一個 2*2 矩陣,通過分析該矩陣的特征值與特征向量可以估計出二值圖像的形狀,具體如下:
,通過分析特征值與特征向量,可的如下結論:
1)較大特征值對應的特征向量方向即為二值圖像朝向;
2)兩個特征值相差越小,二值圖像越接近圓形。
5 示例
以下給出代碼,使用二階矩計算二值圖像朝向及形狀,使用直線擬合計算二值圖像朝向。可以發現,兩種求朝向方案都可得到基本一致的結果。
1 cv::Mat img = cv::imread(str, IMREAD_GRAYSCALE); 2 cv::Mat img_show; 3 img.copyTo(img_show); 4 5 // 提取二值圖像輪廓 6 std::vector<std::vector<cv::Point>> contours; 7 cv::findContours(img, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); 8 if (contours.size() <= 0) 9 return; 10 11 // 只考慮每個圖像上僅有一個二值圖像 12 cv::Moments moms = cv::moments(cv::Mat(contours[0])); 13 double cx = moms.m10 / moms.m00; 14 double cy = moms.m01 / moms.m00; 15 double angle = atan2(moms.mu11 * 2., moms.mu20 - moms.mu02); 16 double tan = tanf(angle * .5); 17 double dx[2] = { -300., 300. }; 18 double dy[2] = { dx[0] * tan, dx[1] * tan }; 19 20 cv::Point pt1(cx + dx[0], cy + dy[0]); 21 cv::Point pt2(cx + dx[1], cy + dy[1]); 22 cv::circle(img_show, cv::Point(cx, cy), 10, cv::Scalar(125), 3); 23 cv::line(img_show, pt1, pt2, cv::Scalar(125), 3); 24 cv::imwrite("rst1.bmp", img_show); 25 26 // 計算二值圖像圓形度 27 // 特征值比值(最大/最小)越接近1,圓形度越好 28 cv::Mat sym = cv::Mat::zeros(2, 2, CV_32FC1); 29 float *datasym = sym.ptr<float>(0); 30 datasym[0] = moms.mu20; 31 datasym[1] = moms.mu11; 32 datasym = sym.ptr<float>(1); 33 datasym[0] = moms.mu11; 34 datasym[1] = moms.mu02; 35 36 cv::Mat eg; 37 cv::eigen(sym, eg); 38 float e1 = eg.ptr<float>(0)[0]; 39 float e2 = eg.ptr<float>(1)[0]; 40 float circular = e1 / e2; 41 42 // 直線擬合 43 cv::Vec4f line_param; 44 cv::fitLine(contours[0], line_param, CV_DIST_L2, 0, .001, .001); 45 46 cv::Point pt0; 47 pt0.x = line_param[2]; 48 pt0.y = line_param[3]; 49 50 double k = line_param[1] / line_param[0]; 51 pt1.x = 0; 52 pt1.y = k * (0 - pt0.x) + pt0.y; 53 pt2.x = 480; 54 pt2.y = k * (480 - pt0.x) + pt0.y; 55 56 cv::line(img_show, pt1, pt2, cv::Scalar(50), 3); 57 cv::imwrite("rst2.bmp", img_show);
參考資料 Robot Vision Berthold Klaus Paul Horn