文章目录

一、简介

图像识别的一个核心问题是图像的特征提取,简单描述即为用一组简单的数据(数据描述符)来描述整个图像,这组数据越简单越有代表性越好。良好的特征不受光线、噪点、几何形变的干扰,图像识别技术的发展中,不断有新的描述图像特征提出,而图像不变矩就是其中一个。

矩函数在图像分析中有着广泛的应用,如模式识别、目标分类、图像编码与重构等。从一幅数字图形中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量的关于该图像不同类型的几何特性信息,比如大小、位置、方向及形状等。图像矩的这种特性描述能力被广泛的应用在各种图像处理、计算机视觉和机器人技术领域的目标识别与方位估计中。


Hu矩

​官网介绍​

  1. 几何矩, 其中 OpenCV + CPP 系列(廿八)图像矩(Image Moments)_#include 的和为几 就是几阶矩。
    OpenCV + CPP 系列(廿八)图像矩(Image Moments)_归一化_02
  2. 中心矩, 其中 OpenCV + CPP 系列(廿八)图像矩(Image Moments)_#include_03 表示它的中心质心。
    中心矩是平移不变的。换句话说,无论斑点在图像的什么地方,如果形状是一样的,力矩是一样的。(平移不变性)
    OpenCV + CPP 系列(廿八)图像矩(Image Moments)_#include_04
  3. 中心归一化矩
    中心归一化矩后,矩不随比例变化而变化。(平移,尺度不变性)
    OpenCV + CPP 系列(廿八)图像矩(Image Moments)_归一化_05

Hu利用二阶和三阶归一化中心矩构造了7个不变矩,不变矩是一处高度浓缩的图像特征,在连续图像下具有平移、灰度、尺度、旋转不变性。其具体定义如下:

OpenCV + CPP 系列(廿八)图像矩(Image Moments)_opencv_06

这7个不变矩构成一组特征量,实际上,在对图片中物体的识别过程中,只有M1和M2不变性保持的比较好,其他的几个不变矩带来的误差比较大,有学者认为只有基于二阶矩的不变矩对二维物体的描述才是真正的具有旋转、缩放和平移不变性(M1和M2刚好都是由二阶矩组成的)。

由Hu矩组成的特征量对图片进行识别,优点就是速度很快,缺点是识别率比较低。Hu不变矩一般用来识别图像中大的物体,对于物体的形状描述得比较好,图像的纹理特征不能太复杂,像识别水果的形状,或者对于车牌中的简单字符的识别效果会相对好一些。

不变矩的物理含义
如果把图像看成是一块质量密度不均匀的薄板,其图像的灰度分布函数f(x,y)就是薄板的密度分布函数,则其各阶矩有着不同的含义,如零阶矩表示它的总质量一阶矩表示它的质心二阶矩又叫惯性矩,表示图像的大小和方向。事实上,如果仅考虑阶次为2的矩集,则原始图像等同于一个具有确定的大小、方向和离心率,以图像质心为中心且具有恒定辐射率的椭圆。当密度分布函数发生改变时,图像的实质没有改变,仍然可以看做一个薄板,只是密度分布有所改变。虽然此时各阶矩的值可能发生变化,但由各阶矩计算出的不变矩仍具有平移、旋转和尺度不变性。通过这个思想,可对图像进行简化处理,保留最能反映目标特性的信息,再用简化后的图像计算不变矩特征,可减少计算量。

moments 计算生成数据

spatial moments:一阶,二阶,三阶

central moments:二阶,三阶

central normalized moments:二阶,三阶

OpenCV + CPP 系列(廿八)图像矩(Image Moments)_归一化_07


Moments moments( InputArray array,     //输入数据

bool binaryImage=false   // 是否为二值图像


)

double contourArea( InputArray contour,    //输入轮廓数据

bool oriented        // 默认false、返回绝对值)

double arcLength( InputArray curve,    //输入曲线数据

bool closed        // 是否是封闭曲线)


演示步骤

  1. 提取图像边缘
  2. 发现轮廓
  3. 计算每个轮廓对象的矩
  4. 通过矩:计算每个对象的中心、弧长、面积

头文件 ​​quick_opencv.h​​:声明类与公共函数

#pragma once
#include <opencv2\opencv.hpp>
using namespace cv;

class QuickDemo {
public:
...
void Moments_Demo(Mat& image1);

};

主函数调用该类的公共成员函数

#include <opencv2\opencv.hpp>
#include <quick_opencv.h>
#include <iostream>
using namespace cv;


int main(int argc, char** argv) {
Mat src = imread("D:\\Desktop\\maomao.png");
if (src.empty()) {
printf("Could not load images...\n");
return -1;
}
namedWindow("input", WINDOW_NORMAL);
imshow("input", src);

QuickDemo qk;
qk.Moments_Demo(src);
waitKey(0);
destroyAllWindows();
return 0;
}

二、演示

源文件 ​​quick_demo.cpp​​:实现类与公共函数

void QuickDemo::Moments_Demo(Mat& image) {
Mat gray_img, bin_img;
cvtColor(image, gray_img, COLOR_BGR2GRAY);
GaussianBlur(gray_img, gray_img, Size(3, 3), 0, 0);
threshold(gray_img, bin_img, 80, 255, THRESH_BINARY_INV);
imshow("bin_img", bin_img);

vector<vector<Point>> contours;
findContours(bin_img, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
if (contours.size() == 0) { return; }

vector<Moments> contour_moments(contours.size());
vector<Point2f> ccs(contours.size());
for (size_t i = 0; i < contours.size(); i++) {
contour_moments[i] = moments(contours[i]);
ccs[i] = Point(
static_cast<float>(contour_moments[i].m10 / contour_moments[i].m00),
static_cast<float>(contour_moments[i].m01 / contour_moments[i].m00)
);
}

Mat drawImg = image.clone();
RNG rng(1234);
for (size_t i = 0; i < contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
float Area = contourArea(contours[i]);
if (Area > 10) {
cout << "contours Area:" << Area << "\tcontours arcLenght: " << arcLength(contours[i], true) << endl;
cout << "center point.x:" << ccs[i].x << "\tcenter point.y:" << ccs[i].y << endl;
drawContours(drawImg, contours, i, color, 2, 8);
circle(drawImg, ccs[i], 2, color, 2, 8);
}
}
imshow("drawImg", drawImg);
}

OpenCV + CPP 系列(廿八)图像矩(Image Moments)_#include_08


OpenCV + CPP 系列(廿八)图像矩(Image Moments)_归一化_09