矩、中心矩、質心、patch方向

l矩、中心矩、質心、patch方向


author@jason_ql
http://blog.csdn.net/lql0716


1、幾何矩理論

1.1 矩與數學期望

  • 數學期望

定義(一維離散):設X[a,b] ,密度爲f(x) ,數學期望爲:

E(X)=i=1xiP(xi)

定義(一維連續):設X 爲連續型隨機變量,其概率密度爲f(x) ,則X的數學期望爲:

E(X)=+xf(x)dx

注:假定廣義積分絕對收斂,即+|x|f(x)dx 存在


定義(二維離散):對於離散變量(X,Y)P(xi,yi)PX(xi)=jP(xi,yj) 期望爲:

E(X)=ixiPX(xi)=jixiP(xi,yj)

E(Y)=jyjPY(yj)=jiyjP(xi,yj)

定義(二維連續):連續變量(X,Y)f(x,y) :

fX(x)=+f(x,y)dy

E(X)=+xfX(x)dx=+x(+f(x,y)dy)dx=++xf(x,y)dxdy

E(Y)=+yfY(y)dy=++yf(x,y)dxdy
  • 原點矩

定義1:設X 是隨機變量,則稱νk(X)=E(Xk)Xk原點矩

X 是離散型隨機變量,則:

νk(X)=ixkip(xi)

X 是連續型隨機變量,則:

νk(X)=+xkf(x)dx
  • 中心距

定義2:設X 是隨機變量,則稱

μk(X)=E(XE(X))k
Xk中心距

X 是離散型隨機變量,則:

μk(X)=i(xiE(X))kp(xi)

X 是連續型隨機變量,則:

μk(X)=+(xE(X))kf(x)dx
  • 原點矩與中心距

當中心距中的E(X) 爲0時,此時爲k 階原點矩,即原點矩是中心距的特殊情況。

一階原點矩就是數學期望,二階中心距就是方差,在實際中常用低階矩,高於四階矩極少使用。

原點矩與中心距的關係式:

μ2=ν2ν21

μ3=ν33ν2ν1+2ν31

μ4=ν44ν3ν1+6ν2ν213ν41

以上可對μr 用組合數拆開得到。

1.2 圖像的矩

把圖像的像素看做密度函數f(x,y) ,對該像素點求期望E ,即是圖像的矩(原點矩)。具體的求解過程參看下面第2節。

一般來說,一階矩零階矩可以計算某個形狀的重心,二階矩可以計算形狀的方向。

圖像的矩主要表徵了圖像區域的幾何特徵,又稱幾何矩,由於具有旋轉、平移、尺度等不變的特興奮,所以又稱爲不變矩
利用不變矩可以計算出物體的圓形度(物體形狀和園的接近程度)、物體的矩形度(物體形狀和矩形的接近程度)、物體的水平和垂直對稱性、物體的主軸方向、扁度等。

  • 原點矩:

    mpq=x1My1Nxpyqf(x,y)
  • 中心距:

    μpq=x1My1N(xx0)p(yy0)qf(x,y)
  • 歸一化中心距:

    ηpq=μpqμr00

    其中r=p+q+22,p+q=2,3,...
  • 一階矩:
    見下面第2節.

  • 二階矩:

M20=xyx2·I(x,y)

M02=xyy2·I(x,y)

M11=xyx·y·I(x,y)

M20M02 分別表示圖像圍繞通過重心的垂直和水平軸線的慣性矩。
M30M03 可以度量圖像對於垂直和水平軸線的對稱性等。

物體形狀的方向:

θ=arctan(b,(ac))2=arctan(b/(ac))2,θ[9090]

其中:

根據一階矩的質心C=(M10M00,M01M00)

a=M20M00C20

b=2(M11M00C0C1)

c=M02M00C21

2、質心原理

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

  • moments of a patch(矩):

    mpq=x=r,y=rrxpyqI(x,y)(1)
  • 角點爲中心:

    m00=x=r,y=rrx0y0I(x,y)=x=r,y=rrI(x,y)(1-1)
  • 一階矩m01 :

    m01=x=r,y=rrx0y1I(x,y)=x=r,y=rryI(x,y)(1-2)
  • 一階矩m10 :

    m10=x=r,y=rrx1y0I(x,y)=x=r,y=rrxI(x,y)(1-3)
  • centroid(質心,亦可稱爲重心):

    C=(m10m00,m01m00)(2)

    計算質心的優勢:對噪聲不敏感。當有外部噪聲干擾時,計算出的質心不會偏離太大。從數學的角度來看,這種方法是計算一個連通域的質心(或一個團塊兒blob的質心)。
  • 構造一個向量OC ,從角點中心O 到質心C

  • orientation of patch(方向):

    θ=atan2(m01,m10)(3)

    建立以角點爲圓心的座標系,如圖這裏寫圖片描述
    在圖中,P 爲角點,園內爲取點區域,每個方格代表一個像素。
    則質心Q 可根據式(2)求得。

3、中心距函數moments()

Calculates all of the moments up to the third order of a polygon or rasterized shape.

C++: Moments moments(InputArray array, bool binaryImage=false )

Python: cv2.moments(array[, binaryImage]) → retval

C: void cvMoments(const CvArr* arr, CvMoments* moments, int binary=0 )

Python: cv.Moments(arr, binary=0) → moments

Parameters:
array – Raster image (single-channel, 8-bit or floating-point 2D array) or an array ( 1 \times N or N \times 1 ) of 2D points (Point or Point2f ).
binaryImage – If it is true, all non-zero image pixels are treated as 1’s. The parameter is used for images only.
moments – Output moments.

  • The function computes moments, up to the 3rd order, of a vector shape or a rasterized shape. The results are returned in the structure Moments defined as:
class Moments
{
public:
    Moments();
    Moments(double m00, double m10, double m01, double m20, double m11,
            double m02, double m30, double m21, double m12, double m03 );
    Moments( const CvMoments& moments );
    operator CvMoments() const;

    // spatial moments
    double  m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
    // central moments
    double  mu20, mu11, mu02, mu30, mu21, mu12, mu03;
    // central normalized moments
    double  nu20, nu11, nu02, nu30, nu21, nu12, nu03;
}

4、中心矩示例代碼

  • opencv2.4.13

4.1 C++版代碼

#include <QCoreApplication>
#include <opencv2/opencv.hpp>
//  Qt Creator 4.2.0(Based on Qt 5.7.1)
//  OpenCV 2.4.13
using namespace cv;
using namespace std;

#define name1 "原圖"
#define name2 "效果圖"

cv::Mat img, gray;
int nThresh = 100;
int nMaxThresh = 255;
cv::RNG rng(12345);  //產生一個隨機數
cv::Mat cannyImg;
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;

//void on_ThreshChange( int, void* ){
//    //canny邊緣檢測
//    cv::Canny( gray, cannyImg, nThresh, nThresh*2, 3 );
//    //找輪廓
//    cv::findContours( cannyImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point( 0, 0 ) );
//    //計算矩
//    std::vector<cv::Moments> mu( contours.size() );
//    for(unsigned int i = 0; i < contours.size(); i++){
//        mu[i] = cv::moments( contours[i], false);
//    }
//    //計算中心矩
//    std::vector<cv::Point2f> mc( contours.size() );
//    for( unsigned int i = 0; i < contours.size(); i++ ){
//        mc[i] = cv::Point2f( static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
//    }
//    //畫輪廓
//    cv::Mat drawing = cv::Mat::zeros( cannyImg.size(), CV_8UC3);
//    for( unsigned int i = 0; i < contours.size(); i++ ){
//        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
//        cv::drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
//        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
//    }

//    cv::namedWindow( name2, cv::WINDOW_NORMAL);
//    cv::imshow( name2, drawing );

//    std::cout << "輸出內容: 面積和輪廓長度 \n" << std::endl;
//    for(unsigned int i = 0; i < contours.size(); i++ ){
//        std::cout << ">通過m00計算出輪廓[" << i << "]的面積:(M_00) =" << mu[i].m00 << "\n OpenCV 函數計算出的面積 = " << cv::contourArea(contours[i]) << "長度:" << cv::arcLength( contours[i], true) <<  "\n\n" << std::endl;
//        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
//        cv::drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
//        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
//     }
//}


int main(){
    img = cv::imread( "/home/jason/jason2/photo/1.jpg" );
    cv::cvtColor( img, gray, cv::COLOR_BGR2GRAY );
    cv::blur( gray, gray, cv::Size(3, 3) );

    cv::namedWindow( name1, cv::WINDOW_NORMAL );
    cv::imshow( name1, img );

//    cv::createTrackbar( "閾值", name1, &nThresh, nMaxThresh, on_ThreshChange );
//    on_ThreshChange( 0, 0 );

    //canny邊緣檢測
    cv::Canny( gray, cannyImg, nThresh, nThresh*2, 3 );
    //找輪廓
    cv::findContours( cannyImg, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point( 0, 0 ) );
    //計算矩
    std::vector<cv::Moments> mu( contours.size() );
    for(unsigned int i = 0; i < contours.size(); i++){
        mu[i] = cv::moments( contours[i], false);
    }
    //計算中心矩
    std::vector<cv::Point2f> mc( contours.size() );
    for( unsigned int i = 0; i < contours.size(); i++ ){
        mc[i] = cv::Point2f( static_cast<float>(mu[i].m10 / mu[i].m00), static_cast<float>(mu[i].m01 / mu[i].m00));
    }
    //畫輪廓
    cv::Mat drawing = cv::Mat::zeros( cannyImg.size(), CV_8UC3);
    for( unsigned int i = 0; i < contours.size(); i++ ){
        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
        cv::drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
    }

    cv::namedWindow( name2, cv::WINDOW_NORMAL);
    cv::imshow( name2, drawing );

    std::cout << "輸出內容: 面積和輪廓長度 \n" << std::endl;
    for(unsigned int i = 0; i < contours.size(); i++ ){
        std::cout << ">通過m00計算出輪廓[" << i << "]的面積:(M_00) =" << mu[i].m00 << "\n OpenCV 函數計算出的面積 = " << cv::contourArea(contours[i]) << "長度:" << cv::arcLength( contours[i], true) <<  "\n\n" << std::endl;
        cv::Scalar color = cv::Scalar( rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255) );
        cv::drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
        cv::circle( drawing, mc[i], 4, color, -1, 8, 0 );
     }
    cv::waitKey(0);

    return 0;
}

原圖:
這裏寫圖片描述

效果圖:
這裏寫圖片描述

部分打印結果:
這裏寫圖片描述


4.2 Python版代碼

# -*- coding: utf-8 -*-
"""
Created on Sun Mar 26 18:36:19 2017

@author: lql0716
"""

import cv2
import numpy as np

nThresh = 100
nMaxThresh = 255

img = cv2.imread('D:/photo/04.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray = cv2.blur(gray, (3,3))
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.imshow('img', img)

cannyImg = cv2.Canny(gray, nThresh, nThresh*2, 3)
contours, hierarchy = cv2.findContours(cannyImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

mu = []
mc = []
retval = np.array([])
for i in range(0, np.array(contours).shape[0]):
    retval = cv2.moments(contours[i], False)
    mu.append(retval)
mu = np.array(mu)
#print mu[0]['m10']
thetas = []
for i in range(0, np.array(contours).shape[0]):
    if mu[i]['m00'] == 0.0:
        a=0
        b=0
    else:
        a = mu[i]['m10'] / mu[i]['m00']  #質心x座標
        b = mu[i]['m01'] / mu[i]['m00']  #質心y座標

        #根據二階矩計算物體形狀的方向
        r1 = mu[i]['m20'] / mu[i]['m00'] - a*a
        r2 = 2.0*(mu[i]['m11'] / mu[i]['m00'] - a*b)
        r3 = mu[i]['m02'] / mu[i]['m00'] - b*b
#        print r1-r3
        if r1-r3==0:
            theta = np.pi / 2
        else:
            theta = np.arctan(r2/(r1-r3)) / 2
        thetas.append(theta)
    mc.append([a,b])
mc = np.array(mc)
drawing = np.zeros(img.shape, dtype = np.uint8)
for i in range(0, mc.shape[0]):
    c1 = np.random.randint(0, 256)
    c2 = np.random.randint(0, 256)
    c3 = np.random.randint(0, 256)
    cv2.drawContours(drawing, contours, i, (c1, c2, c3), 2, 8)
    cv2.circle(drawing, (int(round(mc[i][0])), int(round(mc[i][1]))), 4, (c1, c2, c3), -1, 8, 0)
cv2.namedWindow('img2', cv2.WINDOW_NORMAL)
cv2.imshow('img2', drawing)   
cv2.waitKey(0) 

原圖:
這裏寫圖片描述

效果圖:
這裏寫圖片描述

5、Hu矩HuMoments()

原點矩:

mpq=x1My1Nxpyqf(x,y)

中心距:

μpq=x1My1N(xx0)p(yy0)qf(x,y)

歸一化中心距:

ηpq=μpqμr00

其中r=p+q+22,p+q=2,3,...

當圖像變化時,mpq 也變化,而μpq 具有平移不變形,但對旋轉依然敏感。如果用歸一化中心距,則可以保持平移不變性、伸縮不變性。

Hu矩利用二階、三階中心距構造了7個不變矩,它們在連續圖像條件下可保持平移、旋轉、伸縮不變,公式如下:
M1=η20+η02
M2=(η20η02)2+4η211
M3=(η303η12)2+(3η21η03)2
M4=(η30+η12)2+(η21+η03)2
M5=(η303η12)(η30+η12)((η30+η12)23(η21+η03)2)+(3η21η03)(η21+η03)(3(η30+η12)2(η21+η03)2)
M6=(η20η02)((η30+η12)2(η21+η03)2)+4η11(η30+η12)(η21+η03)
M7=(3η21η03)(η30+η12)((η30+η12)23(η21+η03)2)(η303η12)(η21+η03)(3(η30+η12)2(η21+η03)2)

以下公式爲官方文檔的公式:
此處輸入圖片的描述

在對圖片識別過程中,只有M1M2 不變性保持的比較好,其他幾個不變矩帶來的誤差比較大,有學者認爲只有基於二階矩的不變矩對二維物體的描述才真正具有旋轉,縮放,平移不變性(M1M2 均爲二階矩構成).

Hu矩的優勢:速度快
Hu矩的劣勢:識別率低

Hu矩一般用來識別圖像中大的物體,對物體的形狀描述得比較好,圖像的紋理特徵不能太複雜,如識別水果形狀或車牌字符的效果較好.


  • 參考:

1、圖像的幾何矩淺析
2、opencv官方文檔-moments()
3、形狀描述與識別
4、第三章 二值圖像分析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章