PCA + SVM с использованием синтаксиса C++ в OpenCV 2.2

У меня проблемы с работой PCA и Eigenfaces с использованием последнего синтаксиса C++ с классами Mat и PCA. Старый синтаксис C принимал массив IplImage * в качестве параметра для выполнения его обработки, а текущий API принимает только Mat, отформатированный по столбцу или строке. Я использовал подход Row, используя функцию изменения формы, чтобы подогнать матрицу моего изображения под одну строку. В конечном итоге я хочу взять эти данные, а затем использовать алгоритм SVM для обнаружения, но когда я это делаю, все мои данные представляют собой просто поток нулей. Может кто-нибудь, пожалуйста, помогите мне? Что я делаю неправильно? Спасибо!

Я видел этот вопрос, и он несколько связан, но я м не уверен, что решение.

Это в основном то, что у меня есть:

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");

person Roland Nasr    schedule 11.02.2011    source источник


Ответы (3)


То, что сказал Этарион, верно.

Чтобы скопировать столбец или строку, вам всегда нужно писать:

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

Следующая программа показывает, как выполнить PCA в OpenCV. Он покажет среднее изображение и первые три собственных лица. Изображения, которые я использовал, доступны на 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.

person bytefish    schedule 19.04.2011

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);
person etarion    schedule 12.02.2011
comment
Имеет ли значение, что это всего лишь мелкая копия? Он просто обновит указатель на тот набор данных, который все еще можно использовать. Правильный? - person Roland Nasr; 15.02.2011
comment
@rsnj: Это имеет значение, потому что это утверждение, по сути, недопустимо. data.row(i) создает новый заголовок матрицы, который указывает на те же данные, теперь operator= изменяет этот новый возвращаемый заголовок матрицы и ничего больше не делает, а в конце выражения временный заголовок матрицы (который вы изменили) уничтожается. data полностью не изменен. в руководстве opencv явно указано, что это не работает. - person etarion; 15.02.2011

Я бы посоветовал посмотреть недавно проверенные тесты в svn head

модули/ядро/тест/test_mat.cpp

онлайн здесь: https://code.ros.org/svn/opencv/trunk/opencv/modules/core/test/test_mat.cpp

есть примеры для PCA в старом C и новом C++

Надеюсь, это поможет!

person macarthy    schedule 11.02.2011
comment
Я просматривал тесты и думаю, что запутался еще больше, чем раньше. Я вижу, что они используют генератор случайных чисел, чтобы построить свою матрицу для выполнения PCA. Правильно ли я предполагаю, что мой подход к изменению формы моего коврика для изображений и добавлению их в ряды нового коврика является правильным подходом? Что меня действительно смущает между версиями C и C++, так это то, что C принимает массив изображений (или матрицу), а версия C++ принимает только одну матрицу. Спасибо! - person Roland Nasr; 15.02.2011