OpenCV 4.12.0
开源计算机视觉
加载中...
搜索中...
无匹配项
samples/cpp/pca.cpp

使用 PCA 进行降维同时保持一定方差的示例

/*
* pca.cpp
*
* 作者
* Kevin Hughes <kevinhughes27[at]gmail[dot]com>
*
* 特别感谢
* Philipp Wagner <bytefish[at]gmx[dot]de>
*
* 此程序演示如何使用 OpenCV PCA 并
* 指定要保留的方差量。效果
* 通过使用轨迹栏进一步说明
* 更改保留方差的值。
*
* 该程序将一个文本文件作为输入,每行
* 以图像的完整路径开始。PCA 将执行
* 在此图像列表上。作者建议使用
* AT&T 人脸数据集的前 15 张人脸
* http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html
*
* 例如,您的输入文本文件如下所示
*
* <path_to_at&t_faces>/orl_faces/s1/1.pgm
* <path_to_at&t_faces>/orl_faces/s2/1.pgm
* <path_to_at&t_faces>/orl_faces/s3/1.pgm
* <path_to_at&t_faces>/orl_faces/s4/1.pgm
* <path_to_at&t_faces>/orl_faces/s5/1.pgm
* <path_to_at&t_faces>/orl_faces/s6/1.pgm
* <path_to_at&t_faces>/orl_faces/s7/1.pgm
* <path_to_at&t_faces>/orl_faces/s8/1.pgm
* <path_to_at&t_faces>/orl_faces/s9/1.pgm
* <path_to_at&t_faces>/orl_faces/s10/1.pgm
* <path_to_at&t_faces>/orl_faces/s11/1.pgm
* <path_to_at&t_faces>/orl_faces/s12/1.pgm
* <path_to_at&t_faces>/orl_faces/s13/1.pgm
* <path_to_at&t_faces>/orl_faces/s14/1.pgm
* <path_to_at&t_faces>/orl_faces/s15/1.pgm
*
*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <opencv2/core.hpp>
using namespace cv;
using namespace std;
// 函数
static void read_imgList(const string& filename, vector<Mat>& images) {
std::ifstream file(filename.c_str(), ifstream::in);
if (!file) {
string error_message = "未给出有效的输入文件,请检查给定的文件名。";
CV_Error(Error::StsBadArg, error_message);
}
string line;
while (getline(file, line)) {
images.push_back(imread(line, IMREAD_GRAYSCALE));
}
}
static Mat formatImagesForPCA(const vector<Mat> &data)
{
Mat dst(static_cast<int>(data.size()), data[0].rows*data[0].cols, CV_32F);
for(unsigned int i = 0; i < data.size(); i++)
{
Mat image_row = data[i].clone().reshape(1,1);
Mat row_i = dst.row(i);
image_row.convertTo(row_i,CV_32F);
}
return dst;
}
static Mat toGrayscale(InputArray _src) {
Mat src = _src.getMat();
// 只允许一个通道
if(src.channels() != 1) {
CV_Error(Error::StsBadArg, "仅支持具有一个通道的矩阵");
}
// 创建并返回标准化图像
Mat dst;
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
return dst;
}
struct params
{
Mat data;
int ch;
int rows;
PCA pca;
string winName;
};
static void onTrackbar(int pos, void* ptr)
{
cout << "保留方差 = " << pos << "% ";
cout << "重新计算 PCA..." << std::flush;
double var = pos / 100.0;
struct params *p = (struct params *)ptr;
p->pca = PCA(p->data, cv::Mat(), PCA::DATA_AS_ROW, var);
Mat point = p->pca.project(p->data.row(0));
Mat reconstruction = p->pca.backProject(point);
reconstruction = reconstruction.reshape(p->ch, p->rows);
reconstruction = toGrayscale(reconstruction);
imshow(p->winName, reconstruction);
cout << "完成!主成分数量:" << p->pca.eigenvectors.rows << endl;
}
// 主函数
int main(int argc, char** argv)
{
cv::CommandLineParser parser(argc, argv, "{@input||图像列表}{help h||显示帮助消息}");
if (parser.has("help"))
{
parser.printMessage();
exit(0);
}
// 获取 CSV 文件的路径。
string imgList = parser.get<string>("@input");
if (imgList.empty())
{
parser.printMessage();
exit(1);
}
// 用于保存图像的向量
vector<Mat> images;
// 读入数据。如果无效,可能会失败
try {
read_imgList(imgList, images);
} catch (const cv::Exception& e) {
cerr << "打开文件 \"" << imgList << "\" 时出错。原因:" << e.msg << endl;
exit(1);
}
// 如果没有足够的图像用于此演示,则退出。
if(images.size() <= 1) {
string error_message = "此演示需要至少 2 张图像才能工作。请向您的数据集添加更多图像!";
CV_Error(Error::StsError, error_message);
}
// 将图像重塑并堆叠到 rowMatrix 中
Mat data = formatImagesForPCA(images);
// 执行 PCA
PCA(data, cv::Mat(), PCA::DATA_AS_ROW, 0.95); // 轨迹栏最初在此处设置,这也是保留方差的常用值
// 演示保留方差对第一张图像的影响
Mat point = pca.project(data.row(0)); // 投影到特征空间中,因此图像变为“点”
Mat reconstruction = pca.backProject(point); // 从“点”重新创建图像
reconstruction = reconstruction.reshape(images[0].channels(), images[0].rows); // 从行向量重塑为图像形状
reconstruction = toGrayscale(reconstruction); // 重新缩放以用于显示目的
// 初始化 highgui 窗口
string winName = "重建 | 按 'q' 退出";
namedWindow(winName, WINDOW_NORMAL);
// params 结构体传递给轨迹栏处理程序
params p;
p.data = data;
p.ch = images[0].channels();
p.rows = images[0].rows;
p.pca = pca;
p.winName = winName;
// 创建轨迹栏
int pos = 95;
createTrackbar("保留方差 (%)", winName, &pos, 100, onTrackbar, (void*)&p);
// 显示直到用户按下 q
imshow(winName, reconstruction);
char key = 0;
while(key != 'q')
key = (char)waitKey();
return 0;
}
如果数组没有元素,则返回 true。
int64_t int64
传递给错误的类。
定义 core.hpp:120
String msg
格式化的错误消息
定义 core.hpp:139
n 维密集数组类
定义 mat.hpp:830
Mat row(int y) const
为指定的矩阵行创建矩阵头。
Mat reshape(int cn, int rows=0) const
在不复制数据的情况下更改二维矩阵的形状和/或通道数。
int channels() const
返回矩阵通道数。
void convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const
使用可选缩放将数组转换为另一种数据类型。
主成分分析。
定义 core.hpp:2503
这是用于将只读输入数组传递到 OpenCV 函数中的代理类。
定义 mat.hpp:161
Mat getMat(int idx=-1) const
void normalize(InputArray src, InputOutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray())
对数组的范数或值范围进行归一化。
#define CV_32F
Definition interface.h:78
#define CV_8UC1
定义 interface.h:88
#define CV_Error(code, msg)
调用错误处理程序。
定义 base.hpp:399
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
创建窗口。
int createTrackbar(const String &trackbarname, const String &winname, int *value, int count, TrackbarCallback onChange=0, void *userdata=0)
创建滑动条并将其附加到指定窗口。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
PyParams params(const std::string &tag, const std::string &model, const std::string &weights, const std::string &device)
定义 core.hpp:107
STL 命名空间。