我已经编写了用于计算PCA的this matlab函数的C ++版本(注意这里的数据是列式的,就像在原始的matlab代码中一样)。如果您有兴趣:
U
是获得的旋转矩阵lams
是x
Utmu
只是U.t()*mu
x
是数据矩阵nPCs
我们要保留的主要组件的数量这是我的C ++代码(随意查看):
template <typename T>
void cc::relja_PCA(T &U, T &lams, T &mu, T &Utmu, const T &x, size_t nPCs, const bool substractMean){
int nPoints = x.cols;
int nDims = x.rows;
if(nPCs <= 0)
nPCs = nDims;
if(substractMean){
cv::reduce(x,mu, 1, CV_REDUCE_AVG);
for(int i=0; i<nPoints; i++){
x.col(i) = x.col(i) - mu;
}
}
else
mu = cv::Mat::zeros(1, nDims, x.type());
bool doDual;
T X2;
if(nDims<=nPoints){
doDual = false;
cv::mulTransposed(x,X2, false);
}
else{
doDual = true;
cv::mulTransposed(x, X2, true);
}
X2 /= (nPoints-1);
cv::eigen(X2, lams, U);
U = U.t();
//save only the first nPCs elements of L, which is already a vector
if(nPCs>0 && nPCs<X2.rows){
lams(cv::Range(0, nPCs), cv::Range(0, lams.cols)).copyTo(lams);
U(cv::Range(0,U.rows),cv::Range(0,nPCs)).copyTo(U);
}
if (doDual){
cv::Mat1f diag = lams.clone();
for(int i=0; i<diag.rows; i++)
diag.at<float>(i,1) = diag.at<float>(i,1) > 1e-9 ? diag.at<float>(i,1) : 1e-9;
cv::sqrt(diag, diag);
diag = 1.0f/diag;
diag = cv::Mat::diag(diag);
U = x * (U * diag / std::sqrt(nPoints-1)) ;
}
Utmu = U.t() * mu;
}
使用以下函数调用该函数:
//given some nPCs
cv::Mat1f U, lams, mu, Utmu;
cc::relja_PCA(U, lams, mu, Utmu, mat, nPCs);
一个特征列向量feat
的投影用:
feat = U.t()*feat - Utmu; //or equivalently feat = U.t() * (feat- mu);
我有两个问题:
U
中的cv::eigen(X2, lams, U);
是[U, L]= eigs(X2, nPCs);
的转置版本?如您所见,为了保证原始代码的结果相同,我必须U=U.t();
。为什么?我在使用5x4玩具矩阵和nPC = 3测试代码时注意到了这一点。U
的一些列与w.r.t相反。来自matlab的那些。