opencv筆記三十二(影象矩moments,輪廓面積contourArea,輪廓周長arcLength)
理論來自:
1.概述
影象識別的一個核心問題是影象的特徵提取,簡單描述即為用一組簡單的資料(資料描述量)來描述整個影象,這組資料月簡單越有代表性越好。良好的特徵不受光線、噪點、幾何形變的干擾,影象識別技術的發展中,不斷有新的描述影象特徵提出,而影象不變矩就是其中一個。
從影象中計算出來的矩通常描述了影象不同種類的幾何特徵如:大小、灰度、方向、形狀等,影象矩廣泛應用於模式識別、目標分類、目標識別與防偽估計、影象編碼與重構等領域。
嚴格來講矩是概率與統計中的一個概念,是隨機變數的一種數字特徵。設 xx 為隨機變數,C為常數,則量E[(x−c)^k]稱為X關於C點的k階矩。比較重要的兩種情況如下:
1.c=0,這時a_k=E(X^k)稱為X的k階原點矩;
2.c=E(X),這時μ_k=E[(X−EX)^k]稱為X的k階中心矩
一階原點矩就是期望,一階中心矩μ_1=0,二階中心矩μ_2就是X的方差Var(X)。在統計學上,高於4階的矩極少使用,μ_3可以去衡量分佈是否有偏,μ_4可以衡量分佈(密度)在均值拘謹的陡峭程度。
針對一幅影象,我們把畫素的座標看成是一個二維隨機變數(X, Y),那麼一副灰度圖可以用二維灰度圖密度函式來表示,因此可以用矩來描述灰度影象的特徵。
不變矩(Invariant Moments)是一種高度濃縮的影象特徵,具有平移、灰度、尺度、旋轉不變性,由M.K.Hu在1961年首先提出,1979年M.R.Teague根據正交多項式理論提出了Zernike矩
opencv中提供的API用來計算中心矩和Hu矩,下面主要介紹Hu的原理。
2.原理
一幅M×N的數字影象f(i,j),其p+q階幾何矩m_pq和中心矩μ_pq為:
其中f(i,j)為影象在座標點(i,j)處的灰度值。
若將m_00看做影象的灰度質量,則(i¯,j¯)為影象的質心座標,那麼難中心矩μ_pq反應的是影象灰度相對於其灰度質心的分佈情況,可以用幾何矩來表示中心矩0~3階中心矩與幾何矩的關係如下:
為了消除影象比例變化帶來的影響,定義規格化中心矩如下:
利用二階和三階規格中心矩可以匯出下面7個不變矩組(Φ1 Φ7),它們在影象平移、旋轉和比例變化時保持不變
3.opencv API
opencv中提供了moments()來計算影象中的中心矩(最高到三階),HuMoments()用於由中心矩計算Hu矩.同時配合函式contourArea函式計算輪廓面積和arcLength來計算輪廓或曲線長度
moments()
cv::moments ( InputArray array,
bool binaryImage = false
)
array:輸入陣列,可以是光柵影象(單通道,8-bit或浮點型二維陣列),或者是一個二維陣列(1 X N或N X 1),二維陣列型別為Point或Point2f
binaryImage:預設值是false,如果為true,則所有非零的畫素都會按值1對待,也就是說相當於對影象進行了二值化處理,閾值為1,此引數僅對影象有效。
contourArea()
double cv::contourArea ( InputArray contour,
bool oriented = false
)
contour:是一個向量,二維點,可以是vector或Mat型別
oriented:有預設值false,面向區域識別符號,如果為true,該函式返回一個帶符號的面積,其正負取決於輪廓的方向(順時針還是逆時針)。根據這個特性可以根據面積的符號來確定輪廓的位置。如果是預設值false,則面積以絕對值的形式返回.
arcLength()
用於計算封閉輪廓的周長或曲線的長度
double cv::arcLength ( InputArray curve,
bool closed
)
程式碼如下:
#include<opencv2\opencv.hpp>
#include<math.h>
#include<iostream>
using namespace cv;
using namespace std;
Mat t1, t2, t3, t4;
char *c1 = "OLD", *c2 = "MID", *c3 = "NEW", *c4 = "END";
RNG rng(123456);
int main(int agrc, char** agrv) {
t1=imread("test1.jpg");
if (!t1.data) {
cout << "WRONG";
return -1;
}
GaussianBlur(t1, t2, Size(3, 3), 0, 0, BORDER_CONSTANT);
cvtColor(t2, t2, CV_BGR2GRAY);
threshold(t2, t3, 0, 255, THRESH_OTSU);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(t3, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<Moments> mom(contours.size());
vector<Point2f> m(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
mom[i] = moments(contours[i], false);
m[i] = Point(static_cast<float>(mom[i].m10 / mom[i].m00), static_cast<float>(mom[i].m01 / mom[i].m00));
//static_cast< type-id >( expression ) 該運算子把expression轉換為type - id型別
}
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 180), rng.uniform(0, 180), rng.uniform(0, 180));
drawContours(t1, contours, i, color, 1, 8, hierarchy);
circle(t1, m[i], 5, color, 2, 8);
cout << "i=" << i << endl;
cout << "x=" << m[i].x << " y=" << m[i].y << " area=" << contourArea(contours[i], false) << " arcLength" << arcLength(contours[i], true)<<endl;
}
imshow(c1, t1);
imshow(c2, t3);
waitKey(0);
return 0;
}