1.30【OpenCV圖像處理】圖像矩

計算所有矩moments() 、質心坐標Point()、面積m00或contourArea()、弧長arcLength()、Hu矩HuMoments()

1.30.1 圖像矩(Image Moments)

矩:是描述圖像特徵的運算元。如今矩技術已廣泛應用於圖像檢索和識別 、圖像匹配 、圖像重建 、數字壓縮 、數字水印及運動圖像序列分析等領域。

常見的矩描述運算元可以分為以下幾種:幾何矩(HU)、正交矩(Zernike)、複數矩和旋轉矩。

幾何矩:圖像的矩主要表徵圖像區域的幾何特徵。形式簡單研究充分,幾何矩對簡單圖像有一定的描述能力,區分度上不如其他三種矩,但極其的簡單,一般只需用一個數字就可表達。所以,一般我們是用來做大粒度的區分,用來過濾顯然不相關的文檔。

特徵提取:是圖像識別的一個核心問題,簡單描述即為用一組簡單的數據(圖像描述量)來描述整個圖像,這組數據越簡單越有代表性越好。良好的特徵不受光線、噪點、幾何形變的干擾。圖像識別不斷有新的特徵提出,而圖像不變矩就是其中一個特徵。

特徵匹配和灰度匹配概念見:baike.baidu.com/item/%E

(1)數學中的矩

矩是概率與統計中的一個概念,是隨機變數的一種數字特徵,本質是數學期望。

設X為隨機變數,c為常數,k為正整數。則量

稱為X關於c點的k階矩。比較重要的有兩種情況:

一階原點矩就是期望。一階中心矩μ1=0,二階中心矩μ2就是X的方差Var(X)。μ3可以去衡量分布是否有偏。μ4可以去衡量分布(密度)在均值附近的陡峭程度如何。在統計學上,高於4階的矩極少使用。

(2)圖像矩(Image Moments):通常描述了該圖像形狀的全局特徵。針對於一幅圖像,我們把像素的坐標看成是一個二維隨機變數(X,Y),那麼一幅灰度圖像可以用二維灰度密度函數來表示,因此可以用矩來描述灰度圖像的特徵。

一階矩與形狀有關,二階矩顯示曲線圍繞直線平均值的擴展程度,三階矩則是關於平均值的對稱性的測量。由二階矩和三階矩可以組成一組共7個不變矩,而不變矩則是圖像的統計特性,滿足平移、伸縮、旋轉、均不變的不變性。

(上圖節選自博客:圖像的幾何矩 - CSDN博客)

1.30.2 常見的不變矩

不變矩(Invariant Moments):是一處高度濃縮的圖像特徵,具有平移、尺度、旋轉不變性,主要目的是描述事物(圖像)的特徵。M.K.Hu在1961年首先提出了不變矩的概念。1979年M.R.Teague根據正交多項式理論提出了Zernike矩。下面主要介紹這兩種矩特徵的演算法原理與實現。

(1)Hu矩

1)矩的計算公式:(OpenCV中moments()函數可用於計算最高到3階的幾何矩、中心矩、歸一化矩)

幾何矩(原點矩) m:

(一定範圍內各個像素P乘以位置的和)

中心矩 mu:

中心歸一化距 nu:

圖像對象質心(重心、中心)坐標:

當圖像發生變化時,原點矩Mji也發生變化,原點矩具有平移不變性但對旋轉依然敏感。如果用歸一化中心距,則其特徵不僅具有平移不變性,還具有比例不變性。

2)Hu矩原理:(OpenCV中HuMoments()函數可用於由中心矩計算Hu矩)

Hu利用二階和三階中心矩構造了7個不變矩,他們在連續圖像條件下可保持平移、縮放和旋轉不變,7個不變矩定義如下:

只有基於二階矩的不變矩對二維物體的描述才是真正的與旋轉、平移和尺度無關的。較高階的矩對於成像過程中的誤差,微小的變形等因素非常敏感,所以相應的不變矩基本上不能用於有效的物體識別。即使是基於二階矩的不變矩也只能用來識別外形相差特別大的物理,否則他們的不變矩會因為很相似而不能識別。

由Hu矩組成的特徵量對圖片進行識別,優點就是速度很快,缺點是識別率比較低。Hu不變矩一般用來識別圖像中大的物體,對於物體的形狀描述得比較好,圖像的紋理特徵不能太複雜,像識別水果的形狀,或者對於車牌中的簡單字元的識別效果會相對好一些。

(2)其他常見的正交矩:

連續正交矩:Zernike矩和Legendre矩

離散正交矩:Tchebichef矩、Krawtchouk矩、Hahn矩、Racah矩等。

雖然非正交矩形式簡單 、計算快、易於實現,但抗噪性差、基函數非正交 ,且具有較大的信息冗餘。連續正交矩的基函數正交,不僅其矩變換可逆 ,且易於圖像重建 , 由於其各階矩相互獨立,故具有最小的信息冗餘。其不足之處是該矩的基函數只在特定範圍內是正交的。

Hu矩在圖像描述上有廣泛的應用,但是其低階幾何矩與圖像整體特徵有關,不包含太多的圖像細節信息,而高階幾何矩易受雜訊影響,因此很難利用幾何矩恢復圖像。

Zernike矩能夠很容易地構造圖像的任意高階矩,並能夠使用較少的矩來重建圖像。Zernike矩是基於Zernike多項式的正交化函數,雖然其計算比較複雜,但是Zernide矩在圖像旋轉和低雜訊敏感度方面具有較大的優越性。由於Zernike矩具有圖像旋轉不變性,而且可以構造任意高階矩,所以被廣泛應用對目標進行識別中。

Hu矩和Zernike矩參考博客:cnblogs.com/ronny/p/398

圖像的幾何矩參考博客:blog.csdn.net/GDFSG/art

1.30.2 相關API

(1)計算所有矩:計算矢量形狀或柵格形狀的矩,最高到3階(幾何矩、中心距、中心歸一化矩 如下圖),結果存儲在cv::Moments數據結構中並可返回。(

moments(

InputArray array, //輸入光柵圖像(單通道、8位或浮點2d陣列)或者二維數組

bool binaryImage=false // 是否為二值圖像,默認為false不是二值

)

/*計算每個輪廓對象的所有矩(幾何矩、中心距、中心歸一化矩)*/nvector<Moments> contours_moments(contours.size()); //定義一個儲存所有矩的Moments數據結構數組nfor (size_t i = 0; i < contours.size(); i++) {n contours_moments[i] = moments(contours[i]); //計算每個輪廓對象的所有矩,並將數據輸出給數組:輸入輪廓數組n}n

(2)用幾何矩公式計算每個輪廓對象的中心(質心)坐標:

vector<Point2f> ccs(contours.size()); //定義放中心坐標的數組nccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00), static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));n//用幾何矩計算每個輪廓對象的中心(質心)坐標:公式x0=m10/m00,y0=m01/m00(static_cast<float>強制類型轉換)nprintf("center point x : %.2f y : %.2fn", ccs[i].x, ccs[i].y); //列印每個輪廓對象的中心(質心)坐標n

(3)0階幾何矩m00求輪廓內面積:

Area_(M_00) = contours_moments[i].m00; nprintf("Contour[%d] Area_(M_00) = %.2f Area_OpenCV: %.2f arcLength: %.2f n", i, contours_moments[i].m00, contourArea(contours[i]), arcLength(contours[i], true));n//輸出輪廓元素索引號,0階幾何矩m00,每個輪廓對象的面積,每個輪廓對象的弧長(0階幾何矩m00等於contourArea()計算出來的面積)n

(4)函數法求輪廓內面積:

contourArea(

InputArray contour, //輸入輪廓數據

bool oriented // 默認false,返回絕對值

)

(5)求弧長:

arcLength(

InputArray curve, //輸入曲線數據

bool closed //是否是封閉曲線

)

(6)計算Hu矩(7個Hu不變矩)

先用moments()函數計算中心矩,再用HuMoments函數由中心矩計算Hu矩。

HuMoments(

const Moments & moments, //取中心矩

double/OutputArray hu[7] //計算(輸出)7個Hu不變矩

)

以下程序測驗一幅圖像在旋轉、雜訊與模糊時的7個Hu不變矩是不是具有平移、尺度、旋轉不變性:

int main(int argc, char** argv)n{n Mat image = imread(argv[1]); n cvtColor(image, image, CV_BGR2GRAY); //轉灰度n Moments mts = moments(image); //計算所有矩n double hu[7];n HuMoments(mts, hu); //計算Hu矩n for (int i=0; i<7; i++)n {n cout << log(abs(hu[i])) <<endl; //輸出7個Hu不變矩 log|Φi|n }n return 0;n}n

//Hu矩和Zernike矩效果見博客cnblogs.com/ronny/p/398

實現步驟:

1)提取圖像邊緣 - Canny()

2)發現輪廓 - findContours()

3)計算每個輪廓對象的矩 - moments()

4)計算每個對象的中心、弧長、面積 - Point()、arcLength()、contourArea()、m00


完整程序:

/*1.30 圖像矩*/n#include <opencv2/opencv.hpp>n#include <iostream>n#include <math.h>nnusing namespace std;nusing namespace cv;nnMat src, gray_src;nint threshold_value = 100;nint threshold_max = 255;nconst char* output_win = "image moents demo";nRNG rng(12345);nvoid Demo_Moments(int, void*);nnint main(int argc, char** argv) {n src = imread("E:/OpenCV/testimage/circle.png");nif (!src.data) {n printf("could not load image...n");nreturn -1;n }nn cvtColor(src, gray_src, CV_BGR2GRAY); //轉灰度n GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0); //高斯濾波nchar input_win[] = "input image";n namedWindow(input_win, CV_WINDOW_AUTOSIZE);n namedWindow(output_win, CV_WINDOW_AUTOSIZE);n imshow(input_win, src);n createTrackbar("Threshold Value : ", output_win, &threshold_value, threshold_max, Demo_Moments); //跟蹤條控制邊緣檢測的低閾值n Demo_Moments(0, 0);nn waitKey(0);nreturn 0;n}nnvoid Demo_Moments(int, void*) {n/*Canny邊緣檢測*/nMat canny_output;n Canny(gray_src, canny_output, threshold_value, threshold_value * 2, 3, false);nn/*發現輪廓*/nvector<vector<Point>> contours;nvector<Vec4i> hierachy;n findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));nn/*計算每個輪廓對象的所有矩,計算每個輪廓對象的中心坐標*/nvector<Moments> contours_moments(contours.size()); //定義一個儲存所有矩的Moments數據結構數組nvector<Point2f> ccs(contours.size()); //定義放中心坐標的數組nfor (size_t i = 0; i < contours.size(); i++) {n contours_moments[i] = moments(contours[i]); //計算每個輪廓對象的所有矩,並將數據輸出給數組nn ccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00), static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));n//用幾何矩計算每個輪廓對象的中心(質心)坐標:公式x0=m10/m00,y0=m01/m00(static_cast<float>強制類型轉換) n }nn/*通過0階幾何矩m00計算輪廓面積並且和contourArea()比較,列印輪廓對象的中心坐標*/nMat drawImg;// = Mat::zeros(src.size(), CV_8UC3);n src.copyTo(drawImg);nfor (size_t i = 0; i < contours.size(); i++) {nif (contours[i].size() < 100) { //每個輪廓元素包含一組連續的輪廓點集,點集小於該值的捨棄,點集越大該組輪廓越長ncontinue;n }nScalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));n printf("center point x : %.2f y : %.2fn", ccs[i].x, ccs[i].y); //列印每個輪廓對象的中心(質心)坐標 nn printf("Contour[%d] Area_(M_00) = %.2f Area_OpenCV: %.2f arcLength: %.2f n", i, contours_moments[i].m00, contourArea(contours[i]), arcLength(contours[i], true));n//輸出輪廓元素索引號,0階幾何矩m00,每個輪廓對象的面積,每個輪廓對象的弧長(0階幾何矩m00等於contourArea()計算出來的面積)nn drawContours(drawImg, contours, i, color, 2, 8, hierachy, 0, Point(0, 0)); //畫輪廓n circle(drawImg, ccs[i], 2, color, 2, 8); //畫每個輪廓對象的中心坐標圓點n }n imshow(output_win, drawImg);nnreturn;n}n

運行結果:

推薦閱讀:

[171111] Python OpenCV 中 SIFT 特徵點檢測和匹配
如何用 PS 把這種線條手摳出來?
Byakuren:一個 C 實現的主題色提取庫
燃燒船分形(Burning Ship fractal)
拉普拉斯運算元

TAG:OpenCV | 图像处理 | 计算机视觉 |