首页 > 编程语言 >图像处理之计算物体的方向(C++)

图像处理之计算物体的方向(C++)

时间:2024-05-28 13:00:54浏览次数:14  
标签:orien dst 物体 C++ mu 图像处理 contours cv

图像处理之计算物体的方向(C++)


文章目录


前言

在图像处理中,物体的方向(倾斜角度)计算的应用非常常见,总结如下方法:PCA获得物体的主要方向以及Hu矩计算物体的主要方向。


一、PCA获取物体主要方向

1.原理

PCA的主要思想是寻找到数据的主轴方向,由主轴构成一个新的坐标系,这里的维数可以比原维数低,然后数据由原坐标系向新的坐标系投影(可以理解为一根轴最能代表物体的方向(红色),另外一根轴最不能代表物体的方向(绿色)),这个投影的过程就可以是降维的过程。(具体推导略,可以参考其他博客)
PCA

2.代码实现

#include <opencv.hpp>
#include <iostream>

struct Orientation
{
	cv::Point centroid;	//重心
	double angle;		//倾斜角度
};

/*
* @param const std::vector<cv::Point>& pts 轮廓点集
* @param Orientation& orientation	物体的主要方向结果
* @brief 计算物体的主要方向
*/
void pca_getOrientation(const std::vector<cv::Point>& pts, Orientation& orientation)
{
	// 构建pca数据,将轮廓信息转换为Mat数据结构,后续pca处理需要Mat数据结构
	cv::Mat contoursMat(pts.size(), 2, CV_64FC1);
	for (int i = 0; i < pts.size(); i++)
	{
		contoursMat.at<double>(i, 0) = pts[i].x;
		contoursMat.at<double>(i, 1) = pts[i].y;
	}

	// 执行pca分析
	cv::PCA pca(contoursMat, cv::Mat(), 0);
	// 获得最主要分量(均值),即图像轮廓中心
	orientation.centroid = cv::Point(pca.mean.at<double>(0, 0), pca.mean.at<double>(0, 1));
	//存储特征向量
	cv::Point2d eigen_vecs= cv::Point2d(pca.eigenvectors.at<double>(0, 0), pca.eigenvectors.at<double>(0, 1));
	orientation.angle = atan2(eigen_vecs.y, eigen_vecs.x);
	std::cout << orientation.angle <<std:: endl;
}

int main()
{
	// 读取图片
	std::string filepath = "F://work_study//algorithm_demo//orientation.jpg";
	cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);
	if (src.empty())
	{
		return -1;
	}

	cv::Mat dst;
	cv::threshold(src, dst, 10, 255, cv::THRESH_BINARY_INV);

	// 寻找轮廓
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(dst, contours, hierarchy,cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
	cv::cvtColor(dst, dst, cv::COLOR_GRAY2BGR);

	// 轮廓分析
	for (size_t i = 0; i < contours.size(); i++)
	{
		cv::drawContours(dst, contours, i, cv::Scalar(0, 255, 0), 2, 8, hierarchy, 0);
		Orientation orien;
		pca_getOrientation(contours[i], orien);
		cv::circle(dst, orien.centroid, 3, cv::Scalar(255, 0, 0),-1);
		cv::line(dst, orien.centroid, orien.centroid + cv::Point(200, 200 * tan(orien.angle)),cv::Scalar(255,0,0),2);
	}
	cv::imshow("dst", dst);
	cv::waitKey(0);
	return 0;
}

结果图

二、Hu矩获取物体主要方向

1.原理

如果把图像看成是一块质量密度不均匀的薄板,其图像的灰度分布函数f(x,y)就是薄板的密度分布函数,则其各阶矩有着不同的含义,如零阶矩表示它的总质量;一阶矩表示它的质心;二阶矩又叫惯性矩,表示图像的大小和方向。
一阶矩
二阶矩
研究表明,只有基于二阶矩的不变矩对二维物体的描述才是真正的与旋转、平移和尺度无关的。较高阶的矩对于成像过程中的误差,微小的变形等因素非常敏感,所以相应的不变矩基本上不能用于有效的物体识别。即使是基于二阶矩的不变矩也只能用来识别外形相差特别大的物理,否则他们的不变矩会因为很相似而不能识别。
**在OpenCV中,还可以很方便的得到Hu不变距,Hu不变矩在图像旋转、缩放、平移等操作后,仍能保持矩的不变性,所以有时候用Hu不变距更能识别图像的特征。**不变矩能够描述图像整体特征就是因为它具有平移不变形、比例不变性和旋转不变性等性质。

2.代码实现

#include <opencv.hpp>
#include <iostream>

struct Orientation
{
	cv::Point centroid;	//重心
	double angle;		//倾斜角度
};

int main()
{
	// 读取图片
	std::string filepath = "F://work_study//algorithm_demo//orientation.jpg";
	cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE);
	if (src.empty())
	{
		return -1;
	}

	cv::Mat dst;
	cv::threshold(src, dst, 10, 255, cv::THRESH_BINARY_INV);

	// 寻找轮廓
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(dst, contours, hierarchy,cv::RETR_LIST, cv::CHAIN_APPROX_NONE);
	cv::cvtColor(dst, dst, cv::COLOR_GRAY2BGR);

	// 轮廓分析
	// 计算每个轮廓所有矩
	std::vector<cv::Moments> mu(contours.size());    // 创建一个vector,元素个数为contours.size()
	for (int i = 0; i < contours.size(); i++)
	{
		mu[i] = moments(contours[i], false);   // 获得轮廓的所有最高达三阶所有矩
	}
	// 计算轮廓的质心
	std::vector<Orientation> oriens(contours.size());
	for (size_t i = 0; i < contours.size(); i++)
	{
		cv::drawContours(dst, contours, i, cv::Scalar(0, 255, 0), 2, 8, hierarchy, 0);
		Orientation orien;
		cv::Point2d center(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);		 // 质心的 X,Y 坐标:(m10/m00, m01/m00)
		// 计算方向
		double a = mu[i].m20 / mu[i].m00 - center.x * center.x;
		double b = 2*(mu[i].m11 / mu[i].m00 - center.x * center.y);
		double c = mu[i].m02 / mu[i].m00 - center.y * center.y;
		orien.angle = atan2(b, (a - c))/2;
		orien.centroid = cv::Point(static_cast<int>(center.x), static_cast<int>(center.y));   // 质心的 X,Y 坐标:(m10/m00, m01/m00)
		cv::circle(dst, orien.centroid, 3, cv::Scalar(255, 0, 0), -1);
		cv::line(dst, orien.centroid, orien.centroid + cv::Point(200, 200 * tan(orien.angle)),cv::Scalar(255,0,0),2);
	}
	cv::imshow("dst", dst);
	cv::waitKey(0);
	return 0;
}

总结

本文总结了两种计算物体主要方向的方法,分别是PCA以及HU矩实现的计算物体主要方向,欢迎大家有问题交流。

参考资料:
opencv学习——Moments()函数,计算物体形状方向
图像的矩,以及利用矩求图像的重心,方向

[opencv实战——PCA算法的应用 (https://www.cnblogs.com/xyf327/p/14824106.html#!comments)

标签:orien,dst,物体,C++,mu,图像处理,contours,cv
From: https://blog.csdn.net/qq_45045175/article/details/139243169

相关文章

  • 基于C++的二叉树的创建与遍历(免费提供源码)
    下载地址如下:上传明细-CSDN创作中心项目介绍背景二叉树作为一种常见的数据结构,在计算机科学和编程实践中占有重要地位。它广泛应用于搜索算法、排序算法、表达式解析、符号表以及各种数据库索引结构中。因此,掌握二叉树的创建和遍历是计算机科学领域的一项基本技能。本项目......
  • C++中定义和声明的区别
     直接上例子在类里面声明(这里以静态数据成员和静态成员函数为例)classMyClass{public:staticintstaticDataMember;//静态数据成员的声明staticvoidstaticFunction();//静态成员函数的声明//其他成员...};在cpp文件中定义 静态成员的声明......
  • 【c++基础(三)】类和对象中--构造和析构函数
    1.前言本章重点本篇文章着重讲解类中的两个默认函数,分别为:构造函数,析构函数这是c++六个默认成员函数中的两个(其他四个在后面章节讲解) 我们平时在写基础的数据机构时,例如栈和队列如果自己没有注意没有进行初始化,就有可能导致出错,同理,在写完代码后,忘记销毁开辟好......
  • c++函数
    哈喽大家好,我是@菜就多练,输不起,就别玩,今天我来和大家分享函数函数就是在主函数上方的位置写,但也可以在下面写,常见函数类型有intdoubleboolstringchar.......不同函数类型它们的用法也不同,有判断的(bool),有计算的(intlonglong double),还有字符串的(charstring)等等等等那......
  • c++ 类型转换
     ========在C++中,类型转换(typeconversion)是将一个数据类型的变量转换为另一个数据类型的变量的过程。类型转换可以分为隐式转换(implicitconversion)和显式转换(explicitconversion)。下面详细介绍C++中的类型转换方式。隐式转换隐式转换也称为自动类型转换,是由编译器自动......
  • c++动态内存管理干货
     c++兼容c所以之前C语言使用的方式在c++中同样可以使用,但c++给出了更加简便的动态内存管理方法.1.申请内置类型空间c++定义了新的关键字new和delete(都是操作符不是函数),使用方法如图:需要注意的是,用new申请空间默认不会初始化,但是可以初始化。如图:另外,【】里面可以是变量:......
  • C++ - TCP粘包解决方法
     下面的代码演示了粘包问题,客户端连续三次向服务器端发送数据,服务器端却一次性接收到所有数据。服务器代码#define_WINSOCK_DEPRECATED_NO_WARNINGS#include<iostream>usingnamespacestd;//#include<stdio.h>#include<WinSock2.h>​//#pragmacomment(lib,"ws2_......
  • C++中的异类:“#” 符号背后的故事
    最近在写编程语言的书,聊到C++的宏,感觉很有意思,搬运过来。在C++语言中,# 符号是一个独特的符号。它似乎不在语言核心中,但是在源码里却又无处不在。在语法上,#的语法规则在C++体系里独具一格,和C++语法相比像是两个语言似的。这些差别让我们感受到#背后的故事不简单。今天,我们......
  • c++函数指针
     c/c++函数指针的用法【目录】基本定义c函数指针使用举例c++函数指针使用举例函数指针作为函数参数函数指针作为函数返回值函数指针数组typedef简化函数指针操作 c语言函数指针的定义形式:返回类型 (*函数指针名称)(参数类型,参数类型,参数类型,…);c++函数指针......
  • lambda表达式的用例 c++
    出自:  https://blog.csdn.net/qq_45604814/article/details/132687858一、Lambda表达式概述1.介绍Lambda表达式是C++11标准引入的一种特性,它提供了一种方便的方式来定义匿名函数。Lambda表达式是一种能够捕捉外部变量并使用它们的函数对象。由捕获列表、参数列表、返......