1. 程式人生 > >opencv筆記三十二(影象矩moments,輪廓面積contourArea,輪廓周長arcLength)

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