35.图像矩的计算与应用
矩是描述图像特征的算子,被广泛用于图像检索和识别、图像匹配、图像重建、图像压缩以及运动图像序列分析等领域。本节中将介绍几何矩与Hu矩的计算方法以及应用Hu矩实现图像轮廓的匹配。
1、几何矩与中心矩
OpenCV 4提供了计算图像矩的moments()函数,该函数的函数原型在代码清单7-28中给出。
代码清单7-28 moments()函数原型 Moments cv::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类中所有的属性在表7-5给出。
代码清单7-29 myMoments.cpp计算图像的矩 #include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace cv; using namespace std; int main() { Mat img = imread("approx.png"); // 二值化 Mat gray, binary; cvtColor(img, gray, COLOR_BGR2GRAY); threshold(gray, binary, 105, 255, THRESH_BINARY); //开运算消除细小区域 Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(binary, binary, MORPH_OPEN, k); // 轮廓发现 vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(binary, contours, hierarchy, 0, 2, Point()); for (int n = 0; n < contours.size(); n++) { Moments M; M = moments(contours[n], 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 << " nu02:" << M.nu02 << " nu11:" << M.nu11 << endl << "nu30:" << M.nu30 << " nu21:" << M.nu21 << " nu12:" << M.nu12 << " nu03:" << M.nu03 << endl; } return 0; }
2、Hu矩
Hu矩具有旋转、平移和缩放不变性,因此在图像具有旋转和放缩的情况下Hu矩具有更广泛的应用领域。Hu矩是由二阶和三阶中心距计算得到七个不变矩,具体计算公式如式(7.12)所示:
OpenCV 4提供了用于计算Hu矩的HuMoments()函数,根据参数类型的不同该函数具有两种原型。在代码清单7-30中给出这两种函数原型。
代码清单7-30 HuMoments()函数原型 void cv::HuMoments(const Moments & moments, double hu[7] ) void cv::HuMoments(const Moments & m, OutputArray hu )
- moments:输入的图像矩
- hu[7]:输出Hu矩的七个值
- m:输入的图像矩
- hu:输出Hu矩的矩阵
该函数可以根据图像的中心距计算图像的Hu矩。两个函数原型只有第二个参数的数据类型不同,第一个参数是输入图的Moments类的图像矩,第二个参数是输出的Hu矩,第一种函数原型输出值存放在长度为7的double类型数组中,第二种函数原型输出值为Mat类型。
代码清单7-31 myHuMoments.cpp计算图像的Hu矩 #include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace cv; using namespace std; int main() { system("color F0"); //更改输出界面颜色 Mat img = imread("approx.png"); if (img.empty()) { cout << "请确认图像文件名称是否正确" << endl; return -1; } // 二值化 Mat gray, binary; cvtColor(img, gray, COLOR_BGR2GRAY); threshold(gray, binary, 105, 255, THRESH_BINARY); //开运算消除细小区域 Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(binary, binary, MORPH_OPEN, k); // 轮廓发现 vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(binary, contours, hierarchy, 0, 2, Point()); for (int n = 0; n < contours.size(); n++) { Moments M; M = moments(contours[n], true); Mat hu; HuMoments(M, hu); //计算Hu矩 cout << hu << endl; } return 0; }
3、基于Hu矩的轮廓匹配
Hu矩具有旋转、平移和比例不变性,因此可以通过Hu实现图像轮廓的匹配。OpenCV 4提供了利用Hu矩进行轮廓匹配的matchShapes()函数,该函数的函数原型在代码清单7-32中给出。
代码清单7-32 matchShapes()函数原型 double cv::matchShapes(InputArray contour1, InputArray contour2, int method, double parameter )
- contour1:原灰度图像或者轮廓
- contour2:模板图像或者轮廓
- method:匹配方法的标志,可以选择的参数及含义在表7-6给出。
- parameter:特定于方法的参数(现在不支持)
该函数用于实现在图像或者轮廓中寻找与模板图像或者轮廓像素匹配的区域。函数的第一个参数是原灰度图像或者轮廓,第二个参数是模板图像或者轮廓。函数第三个参数是两个轮廓Hu矩匹配的计算方法标志,可以选择的参数和每种方法相似性计算公式在表7-6给出。函数最后一个参数在目前的OpenCV 4版本中没有意义,可以将参数设置为0。
代码清单7-33 myMatchShapes.cpp基于Hu矩的轮廓匹配 #include <opencv2/opencv.hpp> #include <iostream> #include <vector> using namespace cv; using namespace std; void findcontours(Mat &image, vector<vector<Point>> &contours) { Mat gray, binary; vector<Vec4i> hierarchy; //图像灰度化 cvtColor(image, gray, COLOR_BGR2GRAY); //图像二值化 threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU); //寻找轮廓 findContours(binary, contours, hierarchy, 0, 2); } int main() { Mat img = imread("ABC.png"); Mat img_B = imread("B.png"); if (img.empty() || img_B.empty()) { cout << "请确认图像文件名称是否正确" << endl; return -1; } resize(img_B, img_B, Size(), 0.5, 0.5); imwrite("B.png", img_B); imshow("B", img_B); // 轮廓提取 vector<vector<Point>> contours1; vector<vector<Point>> contours2; findcontours(img, contours1); findcontours(img_B, contours2); // hu矩计算 Moments mm2 = moments(contours2[0]); Mat hu2; HuMoments(mm2, hu2); // 轮廓匹配 for (int n = 0; n < contours1.size(); n++) { Moments mm = moments(contours1[n]); Mat hum; HuMoments(mm, hum); //Hu矩匹配 double dist; dist = matchShapes(hum, hu2, CONTOURS_MATCH_I1, 0); if (dist < 1) { drawContours(img, contours1, n, Scalar(0, 0, 255), 3, 8); } } imshow("match result", img); waitKey(0); return 0; }