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 名稱空間。