图像处理之PCA(C++)

时间:2024-05-07 20:16:03

图像处理之PCA(C++)


文章目录

  • 图像处理之PCA(C++)
  • 前言
  • 一、PCA原理
    • 1.原理思想
    • 2.实现步骤
  • 二、代码实现
  • 总结


前言

在科研、工程应用中,我们往往所获取的数据都包含着很多冗余的信息,这些冗余的信息会对我们分析数据造成干扰。因此,我们可以采用PCA(主成分分析法)去除冗余信息,抓住主要特征,减少数据量。


一、PCA原理

1.原理思想

PCA(Principal Component Analysis),即主成分分析方法,是一种使用最广泛的数据降维算法。PCA的主要思想是将n维特征映射到k维上,这k维是全新的正交特征也被称为主成分,是在原有n维特征的基础上重新构造出来的k维特征。
PCA的工作就是从原始的空间中顺序地找一组相互正交的坐标轴,新的坐标轴的选择与数据本身是密切相关的。其中,第一个新坐标轴选择是原始数据中方差最大的方向,第二个新坐标轴选取是与第一个坐标轴正交的平面中使得方差最大的,第三个轴是与第1,2个轴正交的平面中方差最大的。
主要的应用是:数据压缩(降维)、可视化高维特征、缓解维度灾难、特征独立、降噪、计算物体的方向

2.实现步骤

假设m条n维数据。(使用PCA主成分分析得到的结果就是得到降维后的m条k维数据)

  1. 将原始数据按列组成n行m列矩阵X0(n*m)
  2. 将矩阵X0的每一行进行零均值化,得到矩阵X(n*m),即减去这一行的均值
  3. 求出协方差矩阵C(n*n)(m为矩阵X0的列数)
    协方差矩阵
  4. 求出协方差矩阵的特征值及其对应的特征向量。得到n个特征值,对应n个特征向量,其中每个特征向量的长度又为n,也即所有特征向量组成n行n列的矩阵。
  5. 将特征向量按照特征值从大到小的顺序从上到下排列成矩阵,取前k行组成矩阵P(k*n)
  6. 根据Y=PX即可获得降维到k维后的数据矩阵Y(k*m)

二、代码实现

#include <iostream>
#include <opencv.hpp>
using namespace std;
using namespace cv;


/*
* @param cv::Mat src 输入图像
* @param cv::Mat pca 输出图像
* @param int	k	 降维后的维度
* @brief pca主成分分析法降维
*/
void do_pca(cv::Mat src,cv::Mat& pca,int k)
{
	if (src.rows <= 1 || src.cols <= 1)
	{
		return;
	}

	k = k > src.rows ? src.rows : k;		//取src.rows和k的较小值

	int h = src.rows;
	int w = src.cols;

	// 1.求每一行的平均值
	std::vector<float> meanVec(src.rows,0);
	for (int i = 0; i < h; i++)
	{
		for (int j = 0; j < w; j++)
		{
			meanVec[i] += src.at<uchar>(i, j);
		}
		meanVec[i] /= w;
	}

	// 2.去平均处理
	src.convertTo(src, CV_32F);
	for (int i = 0; i < h; i++)
	{
		for (int j = 0; j < w; j++)
		{
			src.at<float>(i, j) -= meanVec[i];
		}
	}

	// 3.计算协方差矩阵
	cv::Mat covMat;
	covMat = src * src.t()/w;


	// 4\5.求出协方差矩阵的特征值和对应的特征向量
	cv::Mat eigenValues;	//特征值
	cv::Mat eigenVectors;	//特征值对应的特征向量
	cv::eigen(covMat, eigenValues, eigenVectors);	//cv::eigen()输入必须是CV_32F或者CV_64F类型的对称矩阵,
													//且该函数输出的eigenValues和eigenVectors已经按照从大到小的顺序排列

	// 5.取前k行的矩阵P
	cv::Mat kMat;
	eigenVectors(cv::Rect(0, 0, eigenVectors.cols, k)).copyTo(kMat);


	// 6.获得降维后的矩阵Y
	cv::Mat yMat;
	yMat = kMat * src;
	pca = yMat.clone();		//数据类型CV_32FC1
}

int main()
{
	string path = "F://work_study//algorithm_demo//";
	cv::Mat src = cv::imread(path + "baby.jpg", cv::IMREAD_GRAYSCALE);
	if (src.empty())
		return -1;

	//自己实现的pca
	cv::Mat pca_dst;
	int k = 100;	//降维后维度
	do_pca(src, pca_dst, k);

	//opencv实现的pca
	cv::PCA pca(src, Mat(), PCA::DATA_AS_COL, k);
	cv::Mat dst = pca.project(src);

	imshow("pca", pca_dst);
	imshow("opencv_pca", dst);
	cv::waitKey(0);
	return 0;
}

原图
自己实现的pca
自己实现的pca
opencv实现的pca
opencv实现的pca


总结

本文使用C++借助opencv实现了pca算法,介绍了pca原理、计算过程以及源代码的实现,主要核心是利用opencv中的cv::eigen()对协方差矩阵进行特征值和特征向量的计算,欢迎大家提出问题。
本文代码均已经在本地运行成功,欢迎阅读交流。