Commit 228938c5834a8eb44d27ef2d37adc1d5553bb103
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 | ... | ... |