矩是描述图像特征的算子,被广泛用于图像检索和识别、图像匹配、图像重建、图像压缩以及运动图像序列分析等领域。
几何矩与中心矩
图像几何矩的计算方式如式
其中I(x,y)是像素(x,y)处的像素值。当x和y同时取值0时称为零阶矩,零阶矩可以用于计算某个形状的质心,当x和y分别取值0和1时被称为一阶矩,以此类推。图像质心的计算公式
图像中心距计算方式
图像归一化几何矩计算方式
计算图像矩的moments()函数
Moments moments( InputArray array, bool binaryImage = false );
- array:计算矩的区域2D像素坐标集合或者单通道的CV_8U图像
- binaryImage:是否将所有非0像素值视为1的标志。
该函数用于计算图像连通域的几何矩和中心距以及归一化的几何矩。函数第一个参数是待计算矩的输入图像或者2D坐标集合。函数第二个参数为是否将所有非0像素值视为1的标志,该标志只在第一个参数输入为图像类型的数据时才会有作用。函数会返回一个Moments类的变量,Moments类中含有几何矩、中心距以及归一化的几何矩的数值属性,例如Moments.m00是零阶矩,Moments.m01和Moments.m10是一阶矩。Moments类中所有的属性
Hu矩
Hu矩具有旋转、平移和缩放不变性,因此在图像具有旋转和放缩的情况下Hu矩具有更广泛的应用领域。Hu矩是由二阶和三阶中心距计算得到七个不变矩,具体计算公式如式
void HuMoments( const Moments& moments, double hu[7] );
void HuMoments( const Moments& m, OutputArray hu );
- moments:输入的图像矩
- hu[7]:输出Hu矩的七个值
- m:输入的图像矩
- hu:输出Hu矩的矩阵
该函数可以根据图像的中心距计算图像的Hu矩。两个函数原型只有第二个参数的数据类型不同,第一个参数是输入图的Moments类的图像矩,第二个参数是输出的Hu矩,第一种函数原型输出值存放在长度为7的double类型数组中,第二种函数原型输出值为Mat类型。
基于Hu矩的轮廓匹配
Hu矩具有旋转、平移和比例不变性,因此可以通过Hu实现图像轮廓的匹配
double matchShapes( InputArray contour1, InputArray contour2,
int method, double parameter );
- contour1:原灰度图像或者轮廓
- contour2:模板图像或者轮廓
- method:匹配方法的标志,可以选择的参数及含义在表7-6给出。
- parameter:特定于方法的参数(现在不支持)
该函数用于实现在图像或者轮廓中寻找与模板图像或者轮廓像素匹配的区域。函数的第一个参数是原灰度图像或者轮廓,第二个参数是模板图像或者轮廓。函数第三个参数是两个轮廓Hu矩匹配的计算方法标志
函数返回值代表相似度大小,完全相同的图像返回值是0
简单示例
//
// Created by smallflyfly on 2021/6/22.
//
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main() {
Mat im = imread("rice.jfif");
Mat gray;
cvtColor(im, gray, CV_BGR2GRAY);
Mat imBin;
threshold(gray, imBin, 150, 255, THRESH_BINARY);
vector<vector<Point>> contours;
findContours(imBin, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
for (auto & contour : contours) {
// 计算图像连通域的几何矩和中心距以及归一化的几何矩
Moments m;
m = moments(contour, true);
cout << "spatial moments: " << endl
<< "m00: " << m.m00 << " m01: " << m.m01 << " m10: " << m.m10 << endl
<< "m11: " << m.m11 << " m02: " << m.m02 << " m20: " << m.m20 << endl
<< "m12: " << m.m12 << " m21: " << m.m21 << " m03: " << m.m03 << " m30: " << m.m30 << endl;
cout << "central moments: " << endl
<< "mu20: " << m.mu20 << " mu02: " << m.mu02 << " mu11: " << m.mu11 << endl
<< "mu30: " << m.mu30 << " mu21: " << m.mu21 << " mu12: " << m.mu12 << " mu03: " << m.mu03 << endl;
cout << "central normalized moments: " << endl
<< "nu20: " << m.nu20 << " nu11: " << m.nu11 << " nu02: " << m.nu02 << endl
<< "nu30: " << m.nu30 << " nu21: " << m.nu21 << " nu12: " << m.nu12 << " nu03: " << m.nu03 << endl;
// 计算 Hu矩
Mat hu;
HuMoments(m, hu);
cout << "************** Hu moments *********************" << endl
<< hu << endl;
}
// 轮廓匹配
Mat fang1 = imread("fang1.png");
Mat fang2 = imread("fang2.png");
Mat gray1, gray2;
cvtColor(fang1, gray1, CV_BGR2GRAY);
cvtColor(fang2, gray2, CV_BGR2GRAY);
Mat fangBin1, fangBin2;
threshold(gray1, fangBin1, 125, 255, THRESH_BINARY_INV);
threshold(gray2, fangBin2, 125, 255, THRESH_BINARY_INV);
vector<vector<Point>> contours1;
vector<vector<Point>> contours2;
findContours(fangBin1, contours1, RETR_EXTERNAL, CHAIN_APPROX_NONE);
findContours(fangBin2, contours2, RETR_EXTERNAL, CHAIN_APPROX_NONE);
imshow("fangBin1", fangBin1);
imshow("fangBin2", fangBin2);
// 模板
Moments m2 = moments(contours2[0]);
Mat hu2;
HuMoments(m2, hu2);
int i = 0;
for (auto &contour : contours1) {
Moments m = moments(contour);
Mat hu;
HuMoments(m, hu);
// match
double dis;
dis = matchShapes(hu, hu2, CONTOURS_MATCH_I1, 0);
if (dis < 1) {
drawContours(fang1, contours1, i, Scalar(0, 0, 255), 2);
}
i++;
}
imshow("fang1", fang1);
waitKey(0);
destroyAllWindows();
return 0;
}