1. 程式人生 > >Opencv2.4學習::輪廓矩

Opencv2.4學習::輪廓矩

輪廓矩

原理部分:

一、概率論上的定義

 看到矩這個字,很容易聯想到概率論,在概率論中,定義如下:

或者說:

設 X 和 Y 是隨機變數,c 為常數,k 為正整數,  如果E(|X−c|^k)E(|X−c|^k)存在,則稱E(|X−c|^k)E(|X−c|^k)為 X 關於點 c 的 k 階矩。

  • c = 0 時, 稱為 k 階原點矩;
  • c = E(x) 時,稱為 k 階中心矩。

如果E(|X−c1|^p⋅|Y−c2|^q)存在,則稱其為 X,Y 關於 c 點 p+q 階矩。

 二、在影象學上的定義

一幅M×N的數字影象f(i,j),其p+q階幾何矩m_{pq}和中心矩\mu _{pq}

為:

                                                             m_{pq}= \sum_{i=1}^{M}\sum_{j=1}^{N}i^{p}j^{q}f(i,j)

                                                            \mu _{pq}= \sum_{i=1}^{M}\sum_{j=1}^{N}(i-\bar{i})^{p}(j-\bar{j})^{q}f(i,j)

其中:

  • f(i,j)為影象在座標點(i,j)處的灰度值。
  • 重心:\bar{i}=\frac{m_{10}}{m_{00}},\bar{j}=\frac{m_{01}}{m_{00}},也是影象的一階矩

三、幾何矩m_{pq}的基本意義

(1)零階矩

                                                            m_{00}= \sum_{i=1}^{M}\sum_{j=1}^{N}f(i,j)

可以發現,當影象為二值圖時,m_{00}就是這個影象上白色區域的總和,因此,m_{00}可以用來求二值影象(輪廓,連通域)的面積。

(2)一階矩

                                                           m_{10}= \sum_{i=1}^{M}\sum_{j=1}^{N}i\cdot f(i,j)

                                                           m_{01}= \sum_{i=1}^{M}\sum_{j=1}^{N}j\cdot f(i,j)

當影象為二值圖時,m_{10}就是白色畫素關於x座標的累加和,而m_{01}則是y座標的累加和

由此,可獲得影象的重心:\bar{i}=\frac{m_{10}}{m_{00}},\bar{j}=\frac{m_{01}}{m_{00}},也就是前文提到的。

(3)二階矩

                                                           m_{20}= \sum_{i=1}^{M}\sum_{j=1}^{N}i^{2}\cdot f(i,j)

                                                           m_{02}= \sum_{i=1}^{M}\sum_{j=1}^{N}j^{2}\cdot f(i,j)

                                                           m_{11}= \sum_{i=1}^{M}\sum_{j=1}^{N}i\cdot j \cdot f(i,j)

四、由幾何矩可表示出中心距如下:

為了消除影象比例變化帶來的影響,定義規格化中心矩如下:

利用二階和三階規格中心矩可以匯出下面7個不變矩組(Φ1 Φ7),它們在影象平移、旋轉和比例變化時保持不變

Opencv應用部分

核心函式:

(1)求矩

Moments moments(inputArray array, bool binaryImage=false) 
  • 輸入引數,可以是光柵影象(單通道,8位或浮點的二維陣列)或二維陣列(1N或N1)
  • 預設值false,若此引數取true,則所有非零畫素為1.此引數僅對影象使用

(2)計算輪廓面積

double contourArea(inputArray contour, bool oriented=false) 
  • 輸入的向量,二維點(輪廓頂點)
  • 面向區域識別符號,若為true,該函式返回一個帶符號的面積值,其正負取決於輪廓的方向(順時針還是逆時針)。根據這個特性我們可以根據面積的符號來確定輪廓的位置。需要注意的是,這個引數有預設值false,表示以絕對值返回,不帶符號。

(3) 計算輪廓長度

double arcLength(inputArray curve,bool closed) 
  •  輸入的二維點集
  • 一個用於指示曲線是否封閉的識別符號,預設值closed,表示曲線封閉

一、求影象的重心和方向

前面提到二階矩可以用來求物體形狀的方向。 

其中:

a=\frac{m_{20}}{m_{00}}-\bar{i}^{2},

b=\frac{m_{10}}{m_{00}}-\bar{i}\cdot \bar{j}

c=\frac{m_{02}}{m_{00}}- \bar{j}^2 fastAtan2()為opencv的函式,輸入向量,返回一個0-360的角度。 

個人認為:關於fastAtan2()的推算如下:

 fastAtan2(y,x)=\left\{\begin{matrix} 90+\frac{1}{2}arctan(\frac{y}{x}) &&if(\frac{1}{2}arctan(\frac{y}{x}) )>0 \\180++\frac{1}{2}arctan(\frac{y}{x}) &&if(\frac{1}{2}arctan(\frac{y}{x}) )<0 \end{matrix}\right.

測試程式碼:

#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/features2d/features2d.hpp>
#include<stdlib.h>
#include<stdio.h>
#include<iostream>
using namespace std;
using namespace cv;

void main()
{
	Mat srcImg;
	srcImg = imread("F:\\opencv_re_learn\\flash.jpg");
	if (!srcImg.data){
		cout<< "failed to read" << endl;
		system("pause");
		return;
	}
	Mat srcGray;
	cvtColor(srcImg, srcGray, CV_BGR2GRAY);
	Mat thresh;
	threshold(srcGray, thresh, 100, 255, CV_THRESH_BINARY_INV |
		CV_THRESH_OTSU);//二值化時主要要讓目標部分是白色畫素
	Moments m = moments(thresh, true);//moments()函式計算出三階及一下的矩
	Point2d center(m.m10 / m.m00, m.m01 / m.m00);//此為重心
	//計算方向
	double a = m.m20 / m.m00 - center.x*center.x;
	double b = m.m11 / m.m00 - center.x*center.y;
	double c = m.m02 / m.m00 - center.y*center.y;
	double theta = fastAtan2(2 * b, (a - c)) / 2;//此為形狀的方向
	cout << 2 * b << endl;
	cout << a - c << endl;
	cout <<"角度是:"<< theta << endl;
	//繪製重心
	circle(srcImg, center, 2, Scalar(0, 0, 255),2);
	imshow("src", srcImg);
	imshow("Gray", srcGray);
	imshow("Thresh", thresh);
	waitKey(0);
}

未完待續

 參考文章: