赞
踩
主要分三个部分进行描述,代码运行无问题,个人遇到的细节问题在最后阐述
若使用Qt或者C++进行编写程序,主要在官网下载opencv和eigen库,注意tar结尾文件为Linux系统相应配置,windows系统下载zip
下载链接为
opencv官网
Eigen官网
关于这两个库的导入,见另一篇博客
库的简单导入操作
#include <iostream> //涉及文件读取和字符串类型 #include <string> #include <fstream> #include <sstream> //涉及eigen矩阵运算和向量运算,以及opencv矩阵类型 #include <Eigen/Dense> #include <vector> #include <opencv2/opencv.hpp> //eigen中矩阵和cv中矩阵类型转换 #include <opencv2/core/eigen.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/objdetect/objdetect.hpp> //导入命名空间,在写代码时不用加上cv::等繁琐代码 using namespace std; using namespace Eigen; using namespace cv;
vector<string> read_pos_csv(){ ifstream p; //读入文件流,并打开数据文件 p.open("D:\\test\\true_data.csv"); if (!p) { cout << "打开文件失败!" << endl; exit(1); } string line; vector<string> strArray; int i = 0; while (getline(p, line))//getline(p, line)表示按行读取CSV文件中的数据,每一行的结果保存在line中 { stringstream sin(line); //这里stringstream是一个字符串流类型,用line来初始化变量sin string str; //按照逗号进行分割 while(getline(sin,str,',')) //getline每次把按照逗号分割之后的每一个字符串都保存在str中 { strArray.push_back(str); //这里将str保存在lineArray中 i++; } } p.close(); cout << "读取数据完成" << endl; return strArray; }
对于这些参数的取值
1.检测窗口window
长宽一般都取2的幂次,例如64*128
2. cell
一般取8*8,如果计算梯度的话,需要选取幅度和角度两部分,即需要8*8*2=128个值进行存储,做两个矩阵分别存储,梯度通过差分来实现,按照固有的9个bin(角度范围)画成直方图,表示成一个一维数组
注意: 8*8是实际图片中包含自己感兴趣特征的区域大小,可按照实际情况自定义
9个bin是无符号梯度,范围为0-180°,即0,20,40,60,80,100,120,140,160;
需要按照比例分配不同的幅度到不同的bins中去
3. block
为包含更多的灰度对比信息,需要对于cell进行分组,一个block可以大小16*16,包含4个cell,遍历整张图像,即滑动block进行处理
最终向量大小:
一个16*16的block,包含4个直方图,每个直方图9个bins,即九个向量,那么4个就对应4*9=36,一个36*1的一维向量
block的计算方法
以64*128为例,
水平为 (64-8)/8=7
垂直为 (128-8)/8=15
即最终为7*15=105
向量维数为105*36*1=3780维
4.归一化处理,得到的向量都计算一下,变成[0,1]范围内的,移除了尺度信息
MatrixXd get_hog_feature() { int pos_sample = 332; //正样本个数 int neg_sample = 926; //负样本个数 char adpos[2048],adneg[1024]; // set buff avoid overflow HOGDescriptor hog(Size(32,32),Size(16,16),Size(8,8),Size(8,8),9); //定义hog算子的基本参数 int DescriptorDim; //HOG Dimension Mat samFeatureMat, samLabelMat; vector<string> pos_pic_name; //字符串数组,存储的图片名 vector<string> neg_pic_name; pos_pic_name = read_pos_csv(); neg_pic_name = read_neg_csv(); //get pos_sample feature for (int i = 1;i <= pos_sample ;i++) { string str2_pos = "D:\\test\\" + pos_pic_name[i-1] + ".bmp"; char* str3_pos; strcpy(str3_pos, str2_pos.c_str()); const char* img_path_pos = str3_pos; sprintf_s(adpos, img_path_pos, i); Mat src_pos = imread(adpos); vector<float> descriptors;//HOG descriptor hog.compute(src_pos,descriptors); if ( i == 1) { DescriptorDim = descriptors.size(); samFeatureMat = Mat::zeros(pos_sample +neg_sample , DescriptorDim, CV_32FC1); samLabelMat = Mat::zeros(pos_sample +neg_sample , 1, CV_32FC1); } for(int j=0; j<DescriptorDim; j++) { samFeatureMat.at<float>(i-1,j) = descriptors[j]; samLabelMat.at<float>(i-1,0) = 1; } } // cv::Mat -> eigen::Matrix int row = samFeatureMat.rows; int col = samFeatureMat.cols; MatrixXd samFeatureMatrix(row, col); cv2eigen(samFeatureMat, samFeatureMatrix); return samFeatureMatrix; }
1.获取数据
2.减去均值
3.计算协方差矩阵
4.计算协方差矩阵的特征矢量和特征值
5.选择特征值最大的K个特征值对应的特征向量作为主成分
6.用主成分矩阵乘以原始数据得到降维后的数据
// PCA 降维 Mat pca_1(MatrixXd samFeatureMatrix, int featureNum, int k){ // using PCA to reduce the number of features // input samFeatureMatrix: Hog features // featureNum: feature dimension // k: number of features needed // output features: projections in column MatrixXd X = samFeatureMatrix; //copy // 去均值化 RowVectorXd meanVecRow = X.colwise().mean(); X.rowwise() -= meanVecRow; // 计算方差 MatrixXd cov = X.transpose()*X / X.rows(); // 计算投影矩阵 EigenSolver<MatrixXd> solver(cov); MatrixXd eigenVecors = solver.eigenvectors().real(); MatrixXd projection = eigenVecors.block(0, 0, featureNum, k); // eigen::Matrix -> cv::mat Mat projectionMat; eigen2cv(projection, projectionMat); return projectionMat; }
注意变量类型的定义和返回值,PCA降维所得到的维度一般可以选择初始维度的一半,可以保证方差在95%,再根据实际情况做上下调整即可
维度从324维降为100维的调用情况
// test
int main(){
MatrixXd descriptorValues;
descriptorValues = get_hog_feature();
pca_1(descriptorValues, 324, 100);
return 0;
}
1.读取图片的函数sprintf_s中的图片路径应为常量类型,所以由于多次循环导致的路径+图片改变了,需要另外定义常量,将路径赋值给它;
2.注意字符串数组的赋值操作,c_str()的使用;
3.调试的时候,在多个位置增加输出操作,帮助判断哪里有问题;
4.注意原理的理解以及已有库的直接调用,减少工作量;
5.调用不同库的时候的元素类型的转换,cv->eigen 以及 eigen->cv
7.开个专栏总结复习下C++的知识(getline的基本操作,面向对象,重载,封装等)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。