How to perform Linear Discriminant Analysis with OpenCV
Final Update: Based on the help berak gave me, I've written a new question including all the code that he has helped me with in one place, but also with the aim to calculate the probability when classifying instead of finding the nearest datapoint.
I recently tested out scikit-learn's LDA with some different images and could see clear clusters form. Now I want to translate that code into C++ for my main program I was wondering if anyone had any knowledge/experience working with the OpenCV library doing things like Eigenfaces or Fisherfaces. I'm particularly interested in whether I can use LDA directly without having to use one of the pre-written facial recognition libraries.
Update Thank-you berak for your amazing help and great examples in the answer. I hope it would be ok if I double checked a few things that I'm a little confused about?
So if I have my training data set up like this:
Mat trainData; // 256 cols (flat 16*16 tags) and x thousand rows (each tag)
Mat trainLabels; // 1D matrix of class labels e.g. 1, 2, 1, 1, 3, 3
int C = 3; // 3 tag types
int num_components = (C-1);
Then I initialise the LDA:
LDA lda(num_components);
lda.compute(trainData, trainLabels); // compute eigenvectors
Next, I need to get the mean, eigenvectors and projections like you suggested. In your comment above you stated how lda.compute computes the eigenvectors, so does this mean I can retrieve the eigenvectors with this command?
Mat eigenvectors = lda.eigenvectors();
I'm still a little confused as to how I retrieve the mean and also where does feature_row in this code come from?
Mat projected = lda.project(feature_row); // project feature vecs, then compare in lda-space
Once I now have the Mat Projected matrix, the mean and eigenvectors, I then use this bit of your code to get the features Matrix
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;
}
Now I have this training done, can I use the function you wrote below to pass a new 1D tag matrix (that's what Mat feature is right?) and predict what type it is?
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
}
So the final step is for me to take the new tag (feature) and then to iterate through each row of the features matrix I created during the training step, and find the item with the shortest distance and return it's label. Will the data be in x, y coordinate format or is there another way I should try to find the shortest distance?
Update 2 Thanks so much for the clarification, I think I understand, is this correct?
LDA lda(num_components);
lda.compute(trainData, trainLabels); // compute eigenvectors
Mat features = lda.project(trainData);
Then when I want to predict I take my ...
i had a look at the code, and:
is the same as:
on the other hand, you could use reduce to acquire a mean feature vector:
you probably have to try if it works better with or without ;)
then, for prediction, just take the norm to find the closest dist:
Thank-you so much, I think I might understand now. I wrote a second update to the question using your answers and code which I think is correct, is there any chance you could check that I've finally understood? I really can't thank-you enough.
update2: i made a typo, it's labels.at<int>(bestId); in the last line.
then, you probably can project the whole trainData Mat in 1 go, it does not need to iterate over rows.
i think it can handle classlabels on rows or cols, but 1 per row seems the best fit (since your traindata is like that).
Ah I see! So instead of iterating through I can just use "Mat features = lda.project(trainData);" to extract the matrix without iterating (I changed that in update 2). Then to predict I project the new 1D tag array "Mat proj_tag = lda.project(new_tag)" and can iterate through the features matrix when I compare distance between the normalised projected row and the test feature. Does that sound about right?
yes, sounds right.
Wonderful, I'm working to implement it now with my tag extraction program. Berak, thank-you so much for your patience and wonderful explanations!
Ok! Update 3 has the code I've now written based on your wonderful advice and am integrating into the program so I really hope it works. I've extracted 2376 16*16 pixel images of the 3 tag types (they're in different folders) and used them to create the training set. Hope everything looks ok!
hmm for some reason I keep getting this error: "Image step is wrong (The matrix is not continuous, thus its number of rows can not be changed) in reshape". How strange.
Think I fixed it! Although since this question is getting a little crowded, I hope it's ok that I moved to a new question based on the code you taught me. I was hoping it might be possible to expand the predict to be based on probability rather than the nearest point. Thanks so much again and completely understand if you don't have time to answer.