Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

opencv's LDA is quite simple to use:

    LDA lda(num_components); // retain N elements (e.g. numClasses-1)
    lda.compute(trainData, trainLabels); // compute eigenvectors

    Mat projected = lda.project(feature_row); // project feature vecs, then compare in lda-space

but it comes with a restriction: you need more rows than cols in your trainData Mat, which means, if you're e.g. using 100x100 pixel images as features, you got 10000 row elements, so you either need more than 10000 images, or you have to shorten your row vectors.

that's why usually a PCA is applied in front of the LDA, to reduce the feature vectors to about the size of the image-count. all in all we got this:

// we need to keep 4 items from the training, to do tests later:
// Mat labels; // class labels
// Mat mean; // mean from trainData
// Mat eigenvectors // projection Matrix
// Mat projections  // cached preprojected trainData (so we don't need to do it again and again)
void train(const Mat &trainData, const Mat &trainLabels)
{
    set<int> classes;
    for (size_t i=0; i<trainLabels.total(); ++i)
         classes.insert(trainLabels.at<int>(i));
    int C = classes.size(); // unique labels
    int N = trainData.rows;
    int num_components = (C-1); // to keep for LDA

    // step one, do pca on the original data:
    PCA pca(trainData, Mat(), cv::PCA::DATA_AS_ROW, (N-C));
    mean = pca.mean.reshape(1,1);

    // step two, do lda on data projected to pca space:
    Mat proj = pca.project(trainData);

    LDA lda(proj, trainLabels, num_components);

    // step three, combine both:
    Mat leigen;
    lda.eigenvectors().convertTo(leigen, pca.eigenvectors.type());
    gemm(pca.eigenvectors, leigen, 1.0, Mat(), 0.0, eigenvectors, GEMM_1_T);

    // step four, keep labels and projected dataset:
    Mat features; 
    for (int i=0; i<trainData.rows; i++)
    {
        Mat proj = LDA::subspaceProject(eigenvectors, mean, trainData.row(i));
        features.push_back( proj );
    }
    labels = trainLabels;
 }

 // later:
 int predict(Mat &feature)
 {
        Mat proj = LDA::subspaceProject(eigenvectors, mean, feature);
        // compare to pre-projected train feature.row(i), return id of item with shortest distance
 }

opencv's LDA is quite simple to use:

    LDA lda(num_components); // retain N elements (e.g. numClasses-1)
    lda.compute(trainData, trainLabels); // compute eigenvectors

    Mat projected = lda.project(feature_row); // project feature vecs, then compare in lda-space

but it comes with a restriction: you need more rows than cols in your trainData Mat, which means, if you're e.g. using 100x100 pixel images as features, you got 10000 row elements, so you either need more than 10000 images, or you have to shorten your row vectors.

that's why usually a PCA is applied in front of the LDA, to reduce the feature vectors to about the size of the image-count. all in all we got this:

// we need to keep 4 items from the training, to do tests later:
// Mat labels; // class labels
// Mat mean; // mean from trainData
// Mat eigenvectors // projection Matrix
// Mat projections  // cached preprojected trainData (so we don't need to do it again and again)
void train(const Mat &trainData, const Mat &trainLabels)
{
    set<int> classes;
    for (size_t i=0; i<trainLabels.total(); ++i)
         classes.insert(trainLabels.at<int>(i));
    int C = classes.size(); // unique labels
    int N = trainData.rows;
    int num_components = (C-1); // to keep for LDA

    // step one, do pca on the original data:
    PCA pca(trainData, Mat(), cv::PCA::DATA_AS_ROW, (N-C));
    mean = pca.mean.reshape(1,1);

    // step two, do lda on data projected to pca space:
    Mat proj = pca.project(trainData);

    LDA lda(proj, trainLabels, num_components);

    // step three, combine both:
    Mat leigen;
    lda.eigenvectors().convertTo(leigen, pca.eigenvectors.type());
    gemm(pca.eigenvectors, leigen, 1.0, Mat(), 0.0, eigenvectors, GEMM_1_T);

    // step four, keep labels and projected dataset:
    Mat features; 
    for (int i=0; i<trainData.rows; i++)
    {
        Mat proj = LDA::subspaceProject(eigenvectors, mean, trainData.row(i));
        features.push_back( proj );
    }
    labels = trainLabels;
 }

 // later:
 int predict(Mat &feature)
 {
        Mat proj = LDA::subspaceProject(eigenvectors, mean, feature);
        // compare to pre-projected train feature.row(i), feature.row(i),
        // return id of item with shortest distance
 }

opencv's LDA is quite simple to use:

    LDA lda(num_components); // retain N elements (e.g. numClasses-1)
    lda.compute(trainData, trainLabels); // compute eigenvectors

    Mat projected = lda.project(feature_row); // project feature vecs, then compare in lda-space

but it comes with a restriction: you need more rows than cols in your trainData Mat, which means, if you're e.g. using 100x100 pixel images as features, you got 10000 row elements, so you either need more than 10000 images, or you have to shorten your row vectors.

that's why usually a PCA is applied in front of the LDA, to reduce the feature vectors to about the size of the image-count. all in all we got this:

// we need to keep 4 items from the training, to do tests later:
// Mat labels; // class labels
// Mat mean; // mean from trainData
// Mat eigenvectors // projection Matrix
// Mat projections  // cached preprojected trainData (so we don't need to do it again and again)
void train(const Mat &trainData, const Mat &trainLabels)
{
    set<int> classes;
    for (size_t i=0; i<trainLabels.total(); ++i)
         classes.insert(trainLabels.at<int>(i));
    int C = classes.size(); // unique labels
    int N = trainData.rows;
    int num_components = (C-1); // to keep for LDA

    // step one, do pca on the original data:
    PCA pca(trainData, Mat(), cv::PCA::DATA_AS_ROW, (N-C));
    mean = pca.mean.reshape(1,1);

    // step two, do lda on data projected to pca space:
    Mat proj = pca.project(trainData);

    LDA lda(proj, trainLabels, num_components);

    // step three, combine both:
    Mat leigen;
    lda.eigenvectors().convertTo(leigen, pca.eigenvectors.type());
    gemm(pca.eigenvectors, leigen, 1.0, Mat(), 0.0, eigenvectors, GEMM_1_T);

    // step four, keep labels and projected dataset:
    Mat features; 
    for (int i=0; i<trainData.rows; i++)
    {
        // here's the actual magic. we don't use the lda's eigenvecs,
        // but the *product* of pca and lda eigenvecs to do the projection:
        Mat proj = LDA::subspaceProject(eigenvectors, mean, trainData.row(i));
        features.push_back( proj );
    }
    labels = trainLabels;
 }

 // later:
 int predict(Mat &feature)
 {
        Mat proj = LDA::subspaceProject(eigenvectors, mean, feature);
        // compare to pre-projected train feature.row(i),
        // return id of item with shortest distance
 }