【问题标题】:PCA + SVM using C++ Syntax in OpenCV 2.2在 OpenCV 2.2 中使用 C++ 语法的 PCA + SVM
【发布时间】:2011-06-25 20:57:04
【问题描述】:

我在使用最新的 C++ 语法与 Mat 和 PCA 类一起工作时遇到问题。较旧的 C 语法采用 IplImage* 数组作为参数来执行其处理,而当前 API 仅采用按 Column 或 Row 格式化的 Mat。我采用了 Row 方法,使用 reshape 函数来拟合我的图像矩阵以适合单行。我最终想要获取这些数据,然后使用 SVM 算法进行检测,但是当我这样做时,我的所有数据都只是一个 0 流。有人可以帮我吗?我究竟做错了什么?谢谢!

我看到this question 有点相关,但我不确定解决方案是什么。

这基本上就是我所拥有的:

vector<Mat> images; //This variable will be loaded with a set of images to perform PCA on.
Mat values(images.size(), 1, CV_32SC1); //Values are the corresponding values to each of my images.

int nEigens = images.size() - 1; //Number of Eigen Vectors.

//Load the images into a Matrix
Mat desc_mat(images.size(), images[0].rows * images[0].cols, CV_32FC1);
for (int i=0; i<images.size(); i++) {
  desc_mat.row(i) = images[i].reshape(1, 1);
}

Mat average;
PCA pca(desc_mat, average, CV_PCA_DATA_AS_ROW, nEigens);

Mat data(desc_mat.rows, nEigens, CV_32FC1); //This Mat will contain all the Eigenfaces that will be used later with SVM for detection

//Project the images onto the PCA subspace
for(int i=0; i<images.size(); i++) {
  Mat projectedMat(1, nEigens, CV_32FC1);
  pca.project(desc_mat.row(i), projectedMat);

  data.row(i) = projectedMat.row(0);
}

CvMat d1 = (CvMat)data;
CvMat d2 = (CvMat)values;

CvSVM svm;
svm.train(&d1, &d2);
svm.save("svmdata.xml");

【问题讨论】:

    标签: c++ opencv computer-vision svm pca


    【解决方案1】:

    etarion 说的是对的。

    要复制列或行,您总是必须编写:

    Mat B = mat.col(i);
    A.copyTo(B);
    

    以下程序展示了如何在 OpenCV 中执行 PCA。它将显示平均图像和前三个特征脸。我在那里使用的图片来自http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html

    #include "cv.h"
    #include "highgui.h"
    
    using namespace std;
    using namespace cv;
    
    Mat normalize(const Mat& src) {
        Mat srcnorm;
        normalize(src, srcnorm, 0, 255, NORM_MINMAX, CV_8UC1);
        return srcnorm;
    }
    
    int main(int argc, char *argv[]) {
        vector<Mat> db;
    
        // load greyscale images (these are from http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html)
        db.push_back(imread("s1/1.pgm",0));
        db.push_back(imread("s1/2.pgm",0));
        db.push_back(imread("s1/3.pgm",0));
    
        db.push_back(imread("s2/1.pgm",0));
        db.push_back(imread("s2/2.pgm",0));
        db.push_back(imread("s2/3.pgm",0));
    
        db.push_back(imread("s3/1.pgm",0));
        db.push_back(imread("s3/2.pgm",0));
        db.push_back(imread("s3/3.pgm",0));
    
        db.push_back(imread("s4/1.pgm",0));
        db.push_back(imread("s4/2.pgm",0));
        db.push_back(imread("s4/3.pgm",0));
    
        int total = db[0].rows * db[0].cols;
    
        // build matrix (column)
        Mat mat(total, db.size(), CV_32FC1);
        for(int i = 0; i < db.size(); i++) {
            Mat X = mat.col(i);
            db[i].reshape(1, total).col(0).convertTo(X, CV_32FC1, 1/255.);
        }
    
        // Change to the number of principal components you want:
        int numPrincipalComponents = 12;
    
        // Do the PCA:
        PCA pca(mat, Mat(), CV_PCA_DATA_AS_COL, numPrincipalComponents);
    
        // Create the Windows:
        namedWindow("avg", 1);
        namedWindow("pc1", 1);
        namedWindow("pc2", 1);
        namedWindow("pc3", 1);
    
        // Mean face:
        imshow("avg", pca.mean.reshape(1, db[0].rows));
    
        // First three eigenfaces:
        imshow("pc1", normalize(pca.eigenvectors.row(0)).reshape(1, db[0].rows));
        imshow("pc2", normalize(pca.eigenvectors.row(1)).reshape(1, db[0].rows));
        imshow("pc3", normalize(pca.eigenvectors.row(2)).reshape(1, db[0].rows));
    
        // Show the windows:
        waitKey(0);
    }
    

    如果您想逐行构建矩阵(如上面的原始问题),请改用:

    // build matrix
    Mat mat(db.size(), total, CV_32FC1);
    for(int i = 0; i < db.size(); i++) {
        Mat X = mat.row(i);
        db[i].reshape(1, 1).row(0).convertTo(X, CV_32FC1, 1/255.);
    }
    

    并将 PCA 中的标志设置为:

    CV_PCA_DATA_AS_ROW
    

    关于机器学习。我使用 OpenCV C++ API 编写了一篇关于机器学习的文档,其中包含大多数分类器的示例,包括支持向量机。也许你可以在那里得到一些灵感:http://www.bytefish.de/pdf/machinelearning.pdf

    【讨论】:

      【解决方案2】:
      data.row(i) = projectedMat.row(0);
      

      这行不通。 operator= 是浅拷贝,意味着实际上没有数据被拷贝。使用

      cv::Mat sample = data.row(i); // also a shallow copy, points to old data!
      projectedMat.row(0).copyTo(sample);
      

      同样适用于:

      desc_mat.row(i) = images[i].reshape(1, 1);
      

      【讨论】:

      • 只是浅拷贝有关系吗?它只会更新指向该数据集的指针,该数据集仍然应该可用。对吗?
      • @rsnj:这很重要,因为该声明本质上是无操作的。 data.row(i) 创建一个指向相同数据的新矩阵头,现在operator= 修改这个新返回的矩阵头并且不执行任何其他操作,并且在表达式的末尾,临时矩阵头(您修改的)被破坏。 data 完全未修改。 opencv 手册明确指出这“不起作用”。
      【解决方案3】:

      我建议在 svn head 中查看新签入的测试

      modules/core/test/test_mat.cpp

      在线:https://code.ros.org/svn/opencv/trunk/opencv/modules/core/test/test_mat.cpp

      在旧 c 和新 c++ 中有 PCA 的示例

      希望对您有所帮助!

      【讨论】:

      • 我一直在看测试,我想我比以前更困惑了。我看到他们使用随机数生成器来构建他们的矩阵来执行 PCA。我是否正确地假设我重塑图像垫并将它们添加到新垫的行的方法是正确的方法?在 C 和 C++ 版本之间真正让我感到困惑的是 C 接受图像数组(或矩阵),而 C++ 版本只接受单个矩阵。谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-08
      • 2015-04-18
      • 2012-04-07
      • 2015-04-28
      • 1970-01-01
      • 2014-02-11
      相关资源
      最近更新 更多