Commit 228938c5834a8eb44d27ef2d37adc1d5553bb103

Authored by Brendan Klare
2 parents dff99e3e 90cc9e87

New LDA class that uses a sparse set of the original features to learn a subspace

Showing 1 changed file with 114 additions and 4 deletions
openbr/plugins/eigen3.cpp
... ... @@ -19,6 +19,7 @@
19 19  
20 20 #include "openbr/core/common.h"
21 21 #include "openbr/core/eigenutils.h"
  22 +#include "openbr/core/opencvutils.h"
22 23  
23 24 namespace br
24 25 {
... ... @@ -296,6 +297,8 @@ BR_REGISTER(Transform, DFFSTransform)
296 297 */
297 298 class LDATransform : public Transform
298 299 {
  300 + friend class SparseLDATransform;
  301 +
299 302 Q_OBJECT
300 303 Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false)
301 304 Q_PROPERTY(bool pcaWhiten READ get_pcaWhiten WRITE set_pcaWhiten RESET reset_pcaWhiten STORED false)
... ... @@ -328,7 +331,7 @@ class LDATransform : public Transform
328 331 pca.keep = pcaKeep;
329 332 pca.whiten = pcaWhiten;
330 333 pca.train(trainingSet);
331   - mean = pca.mean;
  334 + mean = Eigen::MatrixXf(pca.mean);
332 335  
333 336 TemplateList ldaTrainingSet;
334 337 static_cast<Transform*>(&pca)->project(trainingSet, ldaTrainingSet);
... ... @@ -499,21 +502,30 @@ class LDATransform : public Transform
499 502  
500 503 // Do projection
501 504 outMap = projection.transpose() * (inMap - mean);
502   -
503 505 if (normalize && isBinary)
504 506 dst.m().at<float>(0,0) = dst.m().at<float>(0,0) / stdDev;
505 507 }
506 508  
507 509 void store(QDataStream &stream) const
508 510 {
509   - stream << pcaKeep << directLDA << directDrop << dimsOut << mean << projection;
  511 + stream << pcaKeep;
  512 + stream << directLDA;
  513 + stream << directDrop;
  514 + stream << dimsOut;
  515 + stream << mean;
  516 + stream << projection;
510 517 if (normalize && isBinary)
511 518 stream << stdDev;
512 519 }
513 520  
514 521 void load(QDataStream &stream)
515 522 {
516   - stream >> pcaKeep >> directLDA >> directDrop >> dimsOut >> mean >> projection;
  523 + stream >> pcaKeep;
  524 + stream >> directLDA;
  525 + stream >> directDrop;
  526 + stream >> dimsOut;
  527 + stream >> mean;
  528 + stream >> projection;
517 529 if (normalize && isBinary)
518 530 stream >> stdDev;
519 531 }
... ... @@ -522,6 +534,104 @@ class LDATransform : public Transform
522 534 BR_REGISTER(Transform, LDATransform)
523 535  
524 536 /*!
  537 + * \ingroup transforms
  538 + * \brief Projects input into learned Linear Discriminant Analysis subspace
  539 + * learned on a sparse subset of features with the highest weight
  540 + * in the original LDA algorithm.
  541 + * \author Brendan Klare \cite bklare
  542 + */
  543 +class SparseLDATransform : public Transform
  544 +{
  545 + Q_OBJECT
  546 + Q_PROPERTY(float varThreshold READ get_varThreshold WRITE set_varThreshold RESET reset_varThreshold STORED false)
  547 + Q_PROPERTY(float pcaKeep READ get_pcaKeep WRITE set_pcaKeep RESET reset_pcaKeep STORED false)
  548 + Q_PROPERTY(bool normalize READ get_normalize WRITE set_normalize RESET reset_normalize STORED false)
  549 + BR_PROPERTY(float, varThreshold, 1.5)
  550 + BR_PROPERTY(float, pcaKeep, 0.98)
  551 + BR_PROPERTY(bool, normalize, true)
  552 +
  553 + LDATransform ldaSparse;
  554 + int dimsOut;
  555 + QList<int> selections;
  556 +
  557 + Eigen::VectorXf mean;
  558 +
  559 + void init()
  560 + {
  561 + ldaSparse.init();
  562 + ldaSparse.pcaKeep = pcaKeep;
  563 + ldaSparse.inputVariable = "Label";
  564 + ldaSparse.isBinary = true;
  565 + ldaSparse.normalize = true;
  566 + }
  567 +
  568 + void train(const TemplateList &_trainingSet)
  569 + {
  570 +
  571 + LDATransform ldaOrig;
  572 + ldaOrig.init();
  573 + ldaOrig.inputVariable = "Label";
  574 + ldaOrig.pcaKeep = pcaKeep;
  575 + ldaOrig.isBinary = true;
  576 + ldaOrig.normalize = true;
  577 +
  578 + ldaOrig.train(_trainingSet);
  579 +
  580 + //Only works on binary class problems for now
  581 + assert(ldaOrig.projection.cols() == 1);
  582 + float ldaStd = eigStd(ldaOrig.projection);
  583 + for (int i = 0; i < ldaOrig.projection.rows(); i++)
  584 + if (abs(ldaOrig.projection(i)) > varThreshold * ldaStd)
  585 + selections.append(i);
  586 +
  587 + TemplateList newSet;
  588 + for (int i = 0; i < _trainingSet.size(); i++) {
  589 + cv::Mat x(_trainingSet[i]);
  590 + cv::Mat y = cv::Mat(selections.size(), 1, CV_32FC1);
  591 + int idx = 0;
  592 + int cnt = 0;
  593 + for (int j = 0; j < x.rows; j++)
  594 + for (int k = 0; k < x.cols; k++, cnt++)
  595 + if (selections.contains(cnt))
  596 + y.at<float>(idx++,0) = x.at<float>(j, k);
  597 + newSet.append(Template(_trainingSet[i].file, y));
  598 + }
  599 + ldaSparse.train(newSet);
  600 + dimsOut = ldaSparse.dimsOut;
  601 + }
  602 +
  603 + void project(const Template &src, Template &dst) const
  604 + {
  605 + Eigen::Map<Eigen::MatrixXf> inMap((float*)src.m().ptr<float>(), src.m().rows*src.m().cols, 1);
  606 + Eigen::Map<Eigen::MatrixXf> outMap(dst.m().ptr<float>(), dimsOut, 1);
  607 +
  608 + int d = selections.size();
  609 + cv::Mat inSelect(d,1,CV_32F);
  610 + for (int i = 0; i < d; i++)
  611 + inSelect.at<float>(i) = src.m().at<float>(selections[i]);
  612 + ldaSparse.project(Template(src.file, inSelect), dst);
  613 + }
  614 +
  615 + void store(QDataStream &stream) const
  616 + {
  617 + stream << pcaKeep;
  618 + stream << ldaSparse;
  619 + stream << dimsOut;
  620 + stream << selections;
  621 + }
  622 +
  623 + void load(QDataStream &stream)
  624 + {
  625 + stream >> pcaKeep;
  626 + stream >> ldaSparse;
  627 + stream >> dimsOut;
  628 + stream >> selections;
  629 + }
  630 +};
  631 +
  632 +BR_REGISTER(Transform, SparseLDATransform)
  633 +
  634 +/*!
525 635 * \ingroup distances
526 636 * \brief L1 distance computed using eigen.
527 637 * \author Josh Klontz \cite jklontz
... ...