当前位置: 首页 > news >正文

open cv图形矩

矩的概念介绍

1.矩是描述图像特征的算子

图形矩(Image Moments)- 求取图像质心,面积,长度

1.几何矩(空间矩)- P(x,y) —像素值,x,y x,yx,y表示像素点的位置,有各阶矩
空间矩的实质为面积或者质量。可以通过一阶矩计算质心/重心

2.中心矩- mu(ji)
中心矩体现的是图像强度的最大和最小方向(中心矩可以构建图像的协方差矩阵)
其只具有平移不变性,所以用中心矩做匹配效果不会很好

3.中心归一化矩- nu(ji)
归一化后具有尺度不变性

4.Hu矩
由于具有尺度、旋转、平移不变性,可以用来做匹配


中心矩计算公式

中心距与中心归一化距是几何矩的变种,矩用来对二值图像的轮廓进行计算从而得到图像的轮廓特征
弧矩(用来进行模式识别)…还有很多其他的矩

图像中心Center: 与对象的位置有关系,是对象的质心位置
Center(x_0, y_0)
x0=​m10/m00
y0=m01/m00

API介绍与使用cv::moments计算生成数据

图像矩计算API
moments()函数用来计算多边形和光栅形状的最高达三阶的所有矩
求出图像从1阶到3阶的所有图像矩用来计算形状的重心、面积、主轴和其他形状特征
得到的结果是存储了几何矩、中心距、中心归一化矩的结果
moments
(
InputArray array,//输入数据为findContours()计算出来的数据
bool binaryImage=false//是否为二值图像
)

计算出轮廓的面积(用来计算整个轮廓或部分轮廓的面积)
contourArea
(
InputArray contour,//输入轮廓数据
bool oriented//默认false,返回绝对值
)

计算出轮廓曲线的弧长(用来计算封闭轮廓的周长或曲线的长度)
arcLength
(
InputArray curve,//输入曲线数据
bool closed//是否是封闭曲线
)

步骤

  1. 转灰度cvtColor
  2. 提取图像边缘Canny
  3. 发现轮廓findContours
  4. 计算每个轮廓对象的矩moments
  5. 计算每个对象的中心、弧长、面积contourArea arcLength
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

Mat src, dst,srcgray;
int thre = 50,threMax=255;
int CSize;

int kksize = 1;
int dp = 1;
int mindist = 39;
int thlow = 100;
int thcp = 33;
int minr = 94;
int maxr = 157;
int Max = 250;

void Moment(int, void*);

int main()
{

	src = imread("D:/实验台/机器视觉/测试图片/硬币2.jpg");
	if (src.empty())//如果src这个数据库属性为空
	{
		cout << "无法打开" << endl;
		return -1;
	}
	imshow("原图", src);
	
	//检测不规则轮廓前要进行灰度处理和高斯模糊等预处理
	//cvtColor(src, srcgray, CV_BGR2GRAY);
	//GaussianBlur(srcgray, srcgray, Size(3, 3), 0, 0);

	namedWindow("图形矩", CV_WINDOW_AUTOSIZE);
	namedWindow("圆检测参数调节", CV_WINDOW_NORMAL);
	createTrackbar("阈值调节", "图形矩", &thre, threMax, Moment);
    createTrackbar("轮廓阈值", "图形矩", &CSize, threMax, Moment);
	
	createTrackbar("中值滤波核子大小", "圆检测参数调节", &kksize, Max, Moment);
	createTrackbar("检测尺度", "圆检测参数调节", &dp, Max, Moment);
	createTrackbar("短距圆心辨别", "圆检测参数调节", &mindist, Max, Moment);
	createTrackbar("低阈值过滤", "圆检测参数调节", &thlow, Max, Moment);
	createTrackbar("中心点累加器阈值", "圆检测参数调节", &thcp, Max, Moment);
	createTrackbar("圆最小半径", "圆检测参数调节", &minr, Max, Moment);
	createTrackbar("圆最大半径", "圆检测参数调节", &maxr, Max, Moment);
	Moment(0, 0);
	waitKey(0);
	return 0;
}

void Moment(int, void*)
{
//检测不规则形状时直接使用Cannny边缘检测算子进行边缘提取
//	Mat cannyimg;
//  Canny(srcgray, cannyimg, thre, threMax,3, false);
//	imshow("Canny边缘检测结果", cannyimg);

	//在检测圆形形状时 可使用霍夫圆检测大幅提升检测的精度
	//使用灰度化后的中值模糊进行霍夫圆检测预处理
	int ksize = kksize * 2 + 1;
	cvtColor(src, srcgray, CV_BGR2GRAY);
	medianBlur(srcgray, srcgray, ksize);
	imshow("预处理", srcgray);
	
	//把检测出的圆形画在纯黑背景中 便于后期图形矩的计算和轮廓绘制
	Mat circleimg;
	circleimg = Mat::zeros(src.size(), CV_8UC3);
	vector<Vec3f>Pcircles;//定义动态向量3维数组来存储潜在的圆形坐标和半径信息(x0,y0,r)

	//霍夫圆检测
    HoughCircles(srcgray, Pcircles, CV_HOUGH_GRADIENT, dp, mindist, thlow, thcp, minr, maxr);

	for (size_t i = 0; i < Pcircles.size(); i++)//这里的Pcircles.size()是获取的疑似圆心的数量 根据这个数量确认需要绘制几个圆心
	{
		Vec3f cc = Pcircles[i];//传递第i个潜在圆的信息
		circle(circleimg,Point(cc[0], cc[1]), cc[2], Scalar(255,0, 255), 2, LINE_AA);
		//画圆(导入图片,Point(圆心x坐标位置 圆心y坐标位置),圆的半径,颜色,线条粗细,线条平滑化);
	}

	imshow("圆检测结果", circleimg);
	//把之前绘制在3通道黑色背景中的图形转换为灰度单通道空间并进行canny边缘提取
	cvtColor(circleimg, circleimg, CV_BGR2GRAY);
	Canny(circleimg, circleimg, thre, threMax,3, false);
	imshow("基于霍夫圆检测结果的预处理和边缘提取", circleimg);

	vector<vector<Point>>contours;//存放findContours寻找到的轮廓二维点集
	vector<Vec4i>hierachy;//轮廓结构信息数组
	findContours(circleimg, contours, hierachy,RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
	
	vector<Moments>contoursMoments(contours.size());//存放轮廓阶距的动态向量数组(一共有24个元素)
	//(一个空间矩m的0阶矩,二个空间矩m的1阶矩,三个空间矩m的2阶矩,四个空间矩m的3阶矩)
	//其中空间矩的0阶矩求面积 1阶矩确定重心 2和3阶距用来推导出具有平移伸缩旋转不变性的hu不变矩 
	
	//(三个中心矩mu的2阶矩,四个中心矩mu的3阶矩) -由空间距的2阶矩和三阶矩 推导出的7个hu不变矩
	
	//(三个中心归一化矩nu的2阶矩,四个中心归一化矩nu的3阶矩)-推导出的7个hu不变矩
	
	vector<Point2f>ccs(contours.size());//存放轮廓中心距计算出的轮廓质心(圆心)结果的数组 (2个参数 ccs.x和ccs.y 表示某轮廓圆心的XY坐标)

	//注意: findContours找到的图像轮廓有很多,应使用for循环将轮廓数据进行全部处理
	for (size_t i = 0; i < contours.size(); i++)
	{
		//moments()函数用来计算多边形和光栅形状的最高达三阶的所有矩,用来计算形状的重心、面积、主轴和其他形状特征
		contoursMoments[i] = moments(contours[i]);//获得第i个轮廓的矩
	    //moments-计算矩,返回 0阶到3阶所有的几何矩,2阶到3阶所有的中心矩,2阶到3阶所有的中心归一矩
		//array:输入数组,可以是光栅图像(单通道,8-bit或浮点型二维数组 放大会失真),或者是一个二维数组(1 X N或N X 1),二维数组类型为Point或Point2f
		//binaryImage:默认值是false,如果为true,则所有非零的像素都会按值1对待,也就是说相当于对图像进行了二值化处理,阈值为1,此参数仅对图像有效
		
	//  按照上方的中心矩计算公式,计算中心矩,即图像的质心:
		ccs[i] = Point(static_cast<float>(contoursMoments[i].m10 / contoursMoments[i].m00), static_cast<float>(contoursMoments[i].m01 / contoursMoments[i].m00));
		//轮廓中心=坐标(X-m10/m00,Y-m01/m00)
		//static_cast<float>() 强制类型转换 把精度小的类型转换成精度大的类型(此案例不转换也可以)
	}

	RNG rng(13245);
	Mat drawimg;
	//drawimg = Mat::zeros(src.size(), CV_8UC3);
	src.copyTo(drawimg);
	for (size_t i = 0; i < contours.size(); i++)
	{
		if (contours[i].size() <CSize)//设置一个阈值,当轮廓点数大于某个阈值时,才视为轮廓
		{
			continue; //条件为真执行continue 作用是不执行包含它的循环体的剩余部分 直接进入下次循环
		}
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		
		//contourArea()-用来计算整个轮廓或部分轮廓的面积
		//参数1.contour:是一个计算素材数组,动态向量数组,二维点集合,可以是vector或Mat类型
		//参数2.默认为false,面向区域标识符,为true的时候返回一个带符号的面积,正负取决于轮廓方向,根据这个特性进行轮廓位置判断,如果为默认值false,面积值以绝对值返回
		//注意! 该函数使用格林公式该函数使用Green formula计算轮廓面积,返回面积和非零像素数量如果使用drawContours或fillPoly绘制轮廓,可能导致不同

		//arcLength()-用来计算封闭轮廓的周长或曲线的长度
		//参数1.输入的二维点集,可以是vector或Mat类型
		//参数2.曲线是否封闭的标志位,true为封闭,false为不封闭(此参数无默认值,必须给定一个标志)
		printf("轮廓 %d 的面积为 %.2f  弧长为%.2f ", i+1, contourArea(contours[i]), arcLength(contours[i], false));//计算轮廓的面积和弧长
		printf("中心点X为 %.2f   Y为 %.2f \n ", ccs[i].x, ccs[i].y);
		drawContours(drawimg, contours, i, Scalar(0, 255, 255), 2, 8, hierachy, 0, Point(0, 0));//绘制轮廓
		circle(drawimg, ccs[i], 2, color, 2, 8); //绘制质点

		//使用sprintf_s将数据格式化输出到字符串
		//sprintf_s()是sprintf()的安全版本,通过指定缓冲区长度来避免sprintf()存在的溢出风险
		char buffer[255];//需要预先分配缓冲区 缓冲区过小会溢出报错
		sprintf_s(buffer, "NUM%d,Y%.2f,X%.2f,S%.2f,L%.2f",i+1,ccs[i].y, ccs[i].x, contourArea(contours[i]), arcLength(contours[i], false));
		//数据格式化字符(字符串容器的指针,"需要写入的字符串不能包含汉字",写入的数据信息);	
		putText(drawimg, buffer, Point(ccs[i].x, ccs[i].y), FONT_HERSHEY_COMPLEX, 0.4, Scalar(0, 0, 255), 1, 8);//在图片上显示图像信息
    }
	imshow("图形矩", drawimg);

	//显示vector<Moments>数组中Moment轮廓阶距数组的元素内容
	for (int i = 0; i < contoursMoments.size(); i++)
	{
		cout << "轮廓" << i << "  的空间矩 0阶层m00数据为" << contoursMoments[i].m00 << endl;
		cout << "轮廓" << i << "  的空间矩 1阶层m10数据为" << contoursMoments[i].m10 << "阶层m01数据为" << contoursMoments[i].m01 << endl;
		cout << "轮廓" << i << "  的空间矩 2阶层m02数据为" << contoursMoments[i].m02 << "阶层m11数据为" << contoursMoments[i].m11 << "阶层m02数据为" << contoursMoments[i].m02 << endl;
		cout << "轮廓" << i << "  的空间矩 3阶层m30数据为" << contoursMoments[i].m30 << "阶层m21数据为" << contoursMoments[i].m21 << "阶层m12数据为" << contoursMoments[i].m12 << "阶层m03数据为" << contoursMoments[i].m03 << endl << endl;

		cout << "轮廓" << i << "  的中心矩 2阶层mu20数据为" << contoursMoments[i].mu20 << "阶层mu11数据为" << contoursMoments[i].mu11 << "阶层mu02数据为" << contoursMoments[i].mu02 << endl;
		cout << "轮廓" << i << "  的中心矩 3阶层mu30数据为" << contoursMoments[i].mu30 << "阶层mu21数据为" << contoursMoments[i].mu21 << "阶层mu12数据为" << contoursMoments[i].mu12 << "阶层mu03数据为" << contoursMoments[i].mu03 << endl << endl;

		cout << "轮廓" << i << "  的中心归一化矩 2阶层nu20数据为" << contoursMoments[i].nu20 << "阶层nu11数据为" << contoursMoments[i].nu11 << "阶层nu02数据为" << contoursMoments[i].nu02 << endl;
		cout << "轮廓" << i << "  的中心归一化矩 3阶层nu30数据为" << contoursMoments[i].nu30 << "阶层nu21数据为" << contoursMoments[i].nu21 << "阶层nu12数据为" << contoursMoments[i].nu12 << "阶层nu03数据为" << contoursMoments[i].nu03 << endl << endl;
	}
	return;
}

关于图形矩项目的详解
https://blog.csdn.net/qq_42887760/article/details/86585701
https://blog.csdn.net/m0_37350758/article/details/89639787
https://blog.csdn.net/kuweicai/article/details/79027388?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159306878419195188462277%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159306878419195188462277&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-79027388.first_rank_ecpm_v3_pc_rank_v3&utm_term=%E5%9B%BE%E5%BD%A2%E7%9F%A9

关于图像moment求阶矩中阶矩的详解
https://blog.csdn.net/yang6464158/article/details/42459595?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase
https://www.jianshu.com/p/3cb0b97da76e
https://blog.csdn.net/Edgar_U/article/details/78483505?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-4

相关文章:

  • Python标准库的学习准备
  • open cv点多边形测试
  • 将redhat的yum换成centos的yum
  • open cv基于距离变换与分水岭的图像分割
  • poj 1661 Help Jimmy (动态规划)
  • 呕心沥血!open cv4.1.2添加contrib4.1.2扩展模块
  • java基础----垃圾回收器如何工作
  • open cvSURF特征检测
  • php条件判断
  • open cvSIFT特征点检测
  • open cvHOG特征检测
  • todo格式定义
  • open cv积分图计算
  • 《卖》读书笔记
  • open cv特征描述子和BruteForce暴力匹配
  • JavaScript 如何正确处理 Unicode 编码问题!
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【刷算法】从上往下打印二叉树
  • canvas 五子棋游戏
  • co.js - 让异步代码同步化
  • ECMAScript入门(七)--Module语法
  • HTML5新特性总结
  • JavaScript HTML DOM
  • java正则表式的使用
  • Linux编程学习笔记 | Linux多线程学习[2] - 线程的同步
  • node和express搭建代理服务器(源码)
  • PermissionScope Swift4 兼容问题
  • spring boot下thymeleaf全局静态变量配置
  • SQLServer之索引简介
  • TCP拥塞控制
  • V4L2视频输入框架概述
  • Vue.js 移动端适配之 vw 解决方案
  • 从0到1:PostCSS 插件开发最佳实践
  • 对象管理器(defineProperty)学习笔记
  • 爬虫进阶 -- 神级程序员:让你的爬虫就像人类的用户行为!
  • 前端自动化解决方案
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 我的zsh配置, 2019最新方案
  • 线性表及其算法(java实现)
  • PostgreSQL之连接数修改
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​决定德拉瓦州地区版图的关键历史事件
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #mysql 8.0 踩坑日记
  • #调用传感器数据_Flink使用函数之监控传感器温度上升提醒
  • (arch)linux 转换文件编码格式
  • (二)Linux——Linux常用指令
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (提供数据集下载)基于大语言模型LangChain与ChatGLM3-6B本地知识库调优:数据集优化、参数调整、Prompt提示词优化实战
  • (一)插入排序
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转)shell调试方法
  • .bashrc在哪里,alias妙用
  • .Net Core缓存组件(MemoryCache)源码解析
  • .net 获取url的方法