From 91bce9ac69a0d6c69d99b632535bfd64838901c7 Mon Sep 17 00:00:00 2001 From: matlabbe Date: Mon, 1 Feb 2016 12:53:16 -0500 Subject: [PATCH] Added "NearestNeighbor/7ConvertBinToFloat" parameter: This will convert binary descriptors to float so that vocabulary approaches with KD-Tree or K-means can be used with binary descriptors like Brief/ORB/FREAK (use MANHATTAN_L1 too) --- include/find_object/Settings.h | 2 + src/FindObject.cpp | 3 +- src/ParametersToolBox.cpp | 256 ++++++++++++++++++++------------- src/ParametersToolBox.h | 5 +- src/Vocabulary.cpp | 26 +++- 5 files changed, 187 insertions(+), 105 deletions(-) diff --git a/include/find_object/Settings.h b/include/find_object/Settings.h index d8390469..1df5de64 100644 --- a/include/find_object/Settings.h +++ b/include/find_object/Settings.h @@ -234,6 +234,8 @@ class FINDOBJECT_EXP Settings PARAMETER(NearestNeighbor, 4nndrRatio, float, 0.8f, "Nearest neighbor distance ratio."); PARAMETER(NearestNeighbor, 5minDistanceUsed, bool, false, "Minimum distance with the nearest descriptor to accept a match."); PARAMETER(NearestNeighbor, 6minDistance, float, 1.6f, "Minimum distance. You can look at top of this panel where minimum and maximum distances are shown to properly set this parameter depending of the descriptor used."); + PARAMETER(NearestNeighbor, 7ConvertBinToFloat, bool, false, "Convert binary descriptor to float before quantization, so you can use FLANN strategies with them."); + PARAMETER(NearestNeighbor, BruteForce_gpu, bool, false, "Brute force GPU"); diff --git a/src/FindObject.cpp b/src/FindObject.cpp index 36844400..4bab85da 100644 --- a/src/FindObject.cpp +++ b/src/FindObject.cpp @@ -1368,7 +1368,8 @@ bool FindObject::detect(const cv::Mat & image, find_object::DetectionInfo & info vocabulary_->size() && !vocabulary_->indexedDescriptors().empty() && vocabulary_->indexedDescriptors().cols == info.sceneDescriptors_.cols && - vocabulary_->indexedDescriptors().type() == info.sceneDescriptors_.type(); + (vocabulary_->indexedDescriptors().type() == info.sceneDescriptors_.type() || + (Settings::getNearestNeighbor_7ConvertBinToFloat() && vocabulary_->indexedDescriptors().type() == CV_32FC1)); // COMPARE UDEBUG("COMPARE"); diff --git a/src/ParametersToolBox.cpp b/src/ParametersToolBox.cpp index 3550e6d4..e45608ef 100644 --- a/src/ParametersToolBox.cpp +++ b/src/ParametersToolBox.cpp @@ -482,7 +482,7 @@ void ParametersToolBox::addParameter(QVBoxLayout * layout, QCheckBox * widget = new QCheckBox(this); widget->setChecked(value); widget->setObjectName(key); - connect(widget, SIGNAL(stateChanged(int)), this, SLOT(changeParameter(int))); + connect(widget, SIGNAL(toggled(bool)), this, SLOT(changeParameter(bool))); addParameter(layout, key, widget); } @@ -549,98 +549,149 @@ void ParametersToolBox::changeParameter() } } -void ParametersToolBox::changeParameter(const int & value) +void ParametersToolBox::changeParameter(bool value) { if(sender()) + { + // Workaround as stateChanged(int) is not always emitted, using toggled(bool) instead + changeParameter(sender(), value?Qt::Checked:Qt::Unchecked); + } +} + +void ParametersToolBox::changeParameter(int value) +{ + if(sender()) + { + changeParameter(sender(), value); + } +} + +void ParametersToolBox::changeParameter(QObject * sender, int value) +{ + if(sender) { QStringList paramChanged; - QComboBox * comboBox = qobject_cast(sender()); - QCheckBox * checkBox = qobject_cast(sender()); - if(comboBox) + QComboBox * comboBox = qobject_cast(sender); + QCheckBox * checkBox = qobject_cast(sender); + + bool descriptorChanged = false; + if(comboBox && comboBox->objectName().compare(Settings::kFeature2D_1Detector()) == 0) { - bool descriptorChanged = false; - if(comboBox->objectName().compare(Settings::kFeature2D_1Detector()) == 0) + QComboBox * descriptorBox = (QComboBox*)this->getParameterWidget(Settings::kFeature2D_2Descriptor()); + if(comboBox->objectName().compare(Settings::kFeature2D_1Detector()) == 0 && + comboBox->currentText() != descriptorBox->currentText() && + Settings::getFeature2D_2Descriptor().contains(comboBox->currentText())) { - QComboBox * descriptorBox = (QComboBox*)this->getParameterWidget(Settings::kFeature2D_2Descriptor()); - if(comboBox->objectName().compare(Settings::kFeature2D_1Detector()) == 0 && - comboBox->currentText() != descriptorBox->currentText() && - Settings::getFeature2D_2Descriptor().contains(comboBox->currentText())) + QMessageBox::StandardButton b = QMessageBox::question(this, + tr("Use corresponding descriptor type?"), + tr("Current selected detector type (\"%1\") has its own corresponding descriptor type.\n" + "Do you want to use its corresponding descriptor?") + .arg(comboBox->currentText()), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + if(b == QMessageBox::Yes) { - QMessageBox::StandardButton b = QMessageBox::question(this, - tr("Use corresponding descriptor type?"), - tr("Current selected detector type (\"%1\") has its own corresponding descriptor type.\n" - "Do you want to use its corresponding descriptor?") - .arg(comboBox->currentText()), - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - if(b == QMessageBox::Yes) + int index = descriptorBox->findText(comboBox->currentText()); + if(index >= 0) { - int index = descriptorBox->findText(comboBox->currentText()); - if(index >= 0) - { - QStringList tmp = Settings::getFeature2D_2Descriptor().split(':'); - UASSERT(tmp.size() == 2); - QString newTmp = QString('0'+index)+":"+tmp.back(); - Settings::setFeature2D_2Descriptor(newTmp); - descriptorBox->blockSignals(true); - this->updateParameter(Settings::kFeature2D_2Descriptor()); - descriptorBox->blockSignals(false); - paramChanged.append(Settings::kFeature2D_2Descriptor()); - descriptorChanged = true; - } - else - { - UERROR("Combo box detector type not found \"%s\"?!", comboBox->currentText().toStdString().c_str()); - } + QStringList tmp = Settings::getFeature2D_2Descriptor().split(':'); + UASSERT(tmp.size() == 2); + QString newTmp = QString('0'+index)+":"+tmp.back(); + Settings::setFeature2D_2Descriptor(newTmp); + descriptorBox->blockSignals(true); + this->updateParameter(Settings::kFeature2D_2Descriptor()); + descriptorBox->blockSignals(false); + paramChanged.append(Settings::kFeature2D_2Descriptor()); + descriptorChanged = true; + } + else + { + UERROR("Combo box detector type not found \"%s\"?!", comboBox->currentText().toStdString().c_str()); } } } + } - bool nnStrategyChanged = false; - //verify binary issue with nearest neighbor strategy - if(comboBox->objectName().compare(Settings::kFeature2D_2Descriptor()) == 0 || - comboBox->objectName().compare(Settings::kNearestNeighbor_1Strategy()) == 0 || - descriptorChanged) + bool nnStrategyChanged = false; + //verify binary issue with nearest neighbor strategy + if((comboBox && (comboBox->objectName().compare(Settings::kFeature2D_2Descriptor()) == 0 || + comboBox->objectName().compare(Settings::kNearestNeighbor_1Strategy()) == 0)) || + descriptorChanged || + (checkBox && checkBox->objectName().compare(Settings::kNearestNeighbor_7ConvertBinToFloat()) == 0)) + { + QComboBox * descriptorBox = (QComboBox*)this->getParameterWidget(Settings::kFeature2D_2Descriptor()); + QComboBox * nnBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_1Strategy()); + QComboBox * distBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_2Distance_type()); + QCheckBox * binToFloatCheckbox = (QCheckBox*)this->getParameterWidget(Settings::kNearestNeighbor_7ConvertBinToFloat()); + bool isBinaryDescriptor = descriptorBox->currentText().compare("ORB") == 0 || + descriptorBox->currentText().compare("Brief") == 0 || + descriptorBox->currentText().compare("BRISK") == 0 || + descriptorBox->currentText().compare("FREAK") == 0 || + descriptorBox->currentText().compare("AKAZE") == 0 || + descriptorBox->currentText().compare("LATCH") == 0 || + descriptorBox->currentText().compare("LUCID") == 0; + bool binToFloat = binToFloatCheckbox->isChecked(); + if(isBinaryDescriptor && !binToFloat && nnBox->currentText().compare("Lsh") != 0 && nnBox->currentText().compare("BruteForce") != 0) { - QComboBox * descriptorBox = (QComboBox*)this->getParameterWidget(Settings::kFeature2D_2Descriptor()); - QComboBox * nnBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_1Strategy()); - QComboBox * distBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_2Distance_type()); - bool isBinaryDescriptor = descriptorBox->currentText().compare("ORB") == 0 || - descriptorBox->currentText().compare("Brief") == 0 || - descriptorBox->currentText().compare("BRISK") == 0 || - descriptorBox->currentText().compare("FREAK") == 0 || - descriptorBox->currentText().compare("AKAZE") == 0 || - descriptorBox->currentText().compare("LATCH") == 0 || - descriptorBox->currentText().compare("LUCID") == 0; - if(isBinaryDescriptor && nnBox->currentText().compare("Lsh") != 0 && nnBox->currentText().compare("BruteForce") != 0) + QMessageBox::warning(this, + tr("Warning"), + tr("Current selected descriptor type (\"%1\") is binary while nearest neighbor strategy is not (\"%2\").\n" + "Falling back to \"BruteForce\" nearest neighbor strategy with Hamming distance (by default).") + .arg(descriptorBox->currentText()) + .arg(nnBox->currentText())); + QString tmp = Settings::getNearestNeighbor_1Strategy(); + *tmp.begin() = '6'; // set BruteForce + Settings::setNearestNeighbor_1Strategy(tmp); + tmp = Settings::getNearestNeighbor_2Distance_type(); + *tmp.begin() = '8'; // set HAMMING + Settings::setNearestNeighbor_2Distance_type(tmp); + nnBox->blockSignals(true); + distBox->blockSignals(true); + this->updateParameter(Settings::kNearestNeighbor_1Strategy()); + this->updateParameter(Settings::kNearestNeighbor_2Distance_type()); + nnBox->blockSignals(false); + distBox->blockSignals(false); + if(sender == nnBox) + { + this->updateParametersVisibility(); + return; + } + nnStrategyChanged = true; + paramChanged.append(Settings::kNearestNeighbor_1Strategy()); + paramChanged.append(Settings::kNearestNeighbor_2Distance_type()); + } + else if((!isBinaryDescriptor || binToFloat) && nnBox->currentText().compare("Lsh") == 0) + { + if(binToFloat) { QMessageBox::warning(this, tr("Warning"), - tr("Current selected descriptor type (\"%1\") is binary while nearest neighbor strategy is not (\"%2\").\n" - "Falling back to \"BruteForce\" nearest neighbor strategy with Hamming distance (by default).") + tr("Current selected descriptor type (\"%1\") is binary, but binary to float descriptors conversion is activated while nearest neighbor strategy is (\"%2\").\n" + "Disabling binary to float descriptors conversion and use Hamming distance (by default).") .arg(descriptorBox->currentText()) .arg(nnBox->currentText())); - QString tmp = Settings::getNearestNeighbor_1Strategy(); - *tmp.begin() = '6'; // set BruteForce - Settings::setNearestNeighbor_1Strategy(tmp); - tmp = Settings::getNearestNeighbor_2Distance_type(); + + binToFloatCheckbox->blockSignals(true); + if(checkBox && checkBox->objectName().compare(Settings::kNearestNeighbor_7ConvertBinToFloat()) == 0) + { + Settings::setParameter(checkBox->objectName(), false); + value = 0; + } + else + { + paramChanged.append(Settings::kNearestNeighbor_7ConvertBinToFloat()); + } + this->updateParameter(Settings::kNearestNeighbor_7ConvertBinToFloat()); + binToFloatCheckbox->blockSignals(false); + + QString tmp = Settings::getNearestNeighbor_2Distance_type(); *tmp.begin() = '8'; // set HAMMING Settings::setNearestNeighbor_2Distance_type(tmp); - nnBox->blockSignals(true); distBox->blockSignals(true); - this->updateParameter(Settings::kNearestNeighbor_1Strategy()); this->updateParameter(Settings::kNearestNeighbor_2Distance_type()); - nnBox->blockSignals(false); distBox->blockSignals(false); - if(sender() == nnBox) - { - this->updateParametersVisibility(); - return; - } - nnStrategyChanged = true; - paramChanged.append(Settings::kNearestNeighbor_1Strategy()); paramChanged.append(Settings::kNearestNeighbor_2Distance_type()); } - else if(!isBinaryDescriptor && nnBox->currentText().compare("Lsh") == 0) + else { QMessageBox::warning(this, tr("Warning"), @@ -648,6 +699,7 @@ void ParametersToolBox::changeParameter(const int & value) "Falling back to \"KDTree\" nearest neighbor strategy with Euclidean_L2 distance (by default).") .arg(descriptorBox->currentText()) .arg(nnBox->currentText())); + QString tmp = Settings::getNearestNeighbor_1Strategy(); *tmp.begin() = '1'; // set KDTree Settings::setNearestNeighbor_1Strategy(tmp); @@ -660,7 +712,7 @@ void ParametersToolBox::changeParameter(const int & value) this->updateParameter(Settings::kNearestNeighbor_2Distance_type()); nnBox->blockSignals(false); distBox->blockSignals(false); - if(sender() == nnBox) + if(sender == nnBox) { this->updateParametersVisibility(); return; @@ -670,53 +722,57 @@ void ParametersToolBox::changeParameter(const int & value) paramChanged.append(Settings::kNearestNeighbor_2Distance_type()); } } + } - // Distance issue when using nearest neighbor strategy using CV_32F type, though Lsh support all type (doesn't crash at least) - if(nnStrategyChanged || - comboBox->objectName().compare(Settings::kNearestNeighbor_1Strategy()) == 0 || - comboBox->objectName().compare(Settings::kNearestNeighbor_2Distance_type()) == 0) + // Distance issue when using nearest neighbor strategy using CV_32F type, though Lsh support all type (doesn't crash at least) + if(nnStrategyChanged || + (comboBox && (comboBox->objectName().compare(Settings::kNearestNeighbor_1Strategy()) == 0 || + comboBox->objectName().compare(Settings::kNearestNeighbor_2Distance_type()) == 0))) + { + QComboBox * nnBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_1Strategy()); + QComboBox * distBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_2Distance_type()); + if(nnBox->currentText().compare("BruteForce") != 0 && nnBox->currentText().compare("Lsh") != 0 && distBox->currentIndex() > 1) { - QComboBox * nnBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_1Strategy()); - QComboBox * distBox = (QComboBox*)this->getParameterWidget(Settings::kNearestNeighbor_2Distance_type()); - if(nnBox->currentText().compare("BruteForce") != 0 && nnBox->currentText().compare("Lsh") != 0 && distBox->currentIndex() > 1) + QMessageBox::warning(this, + tr("Warning"), + tr("Current selected nearest neighbor strategy type (\"%1\") cannot handle distance strategy (\"%2\").\n" + "Falling back to \"EUCLIDEAN_L2\" distance strategy (by default).") + .arg(nnBox->currentText()) + .arg(distBox->currentText())); + QString tmp = Settings::getNearestNeighbor_2Distance_type(); + *tmp.begin() = '0'; // set index + Settings::setNearestNeighbor_2Distance_type(tmp); + distBox->blockSignals(true); + this->updateParameter(Settings::kNearestNeighbor_2Distance_type()); + distBox->blockSignals(false); + if(sender == distBox) { - QMessageBox::warning(this, - tr("Warning"), - tr("Current selected nearest neighbor strategy type (\"%1\") cannot handle distance strategy (\"%2\").\n" - "Falling back to \"EUCLIDEAN_L2\" distance strategy (by default).") - .arg(nnBox->currentText()) - .arg(distBox->currentText())); - QString tmp = Settings::getNearestNeighbor_2Distance_type(); - *tmp.begin() = '0'; // set index - Settings::setNearestNeighbor_2Distance_type(tmp); - distBox->blockSignals(true); - this->updateParameter(Settings::kNearestNeighbor_2Distance_type()); - distBox->blockSignals(false); - if(sender() == distBox) - { - this->updateParametersVisibility(); - return; - } - paramChanged.append(Settings::kNearestNeighbor_2Distance_type()); + this->updateParametersVisibility(); + return; } + paramChanged.append(Settings::kNearestNeighbor_2Distance_type()); } + } + if(comboBox) + { QStringList items; for(int i=0; icount(); ++i) { items.append(comboBox->itemText(i)); } QString merged = QString::number(value) + QString(":") + items.join(";"); - Settings::setParameter(sender()->objectName(), merged); - - this->updateParametersVisibility(); + Settings::setParameter(sender->objectName(), merged); } - else if(checkBox) + + if(checkBox) { - Settings::setParameter(sender()->objectName(), value==Qt::Checked?true:false); + Settings::setParameter(sender->objectName(), value==Qt::Checked?true:false); } - paramChanged.append(sender()->objectName()); + this->updateParametersVisibility(); + + paramChanged.append(sender->objectName()); Q_EMIT parametersChanged(paramChanged); } } diff --git a/src/ParametersToolBox.h b/src/ParametersToolBox.h index ca6a44f3..4cd85283 100644 --- a/src/ParametersToolBox.h +++ b/src/ParametersToolBox.h @@ -55,13 +55,16 @@ private: void addParameter(QVBoxLayout * layout, const QString & key, const bool & value); void addParameter(QVBoxLayout * layout, const QString & name, QWidget * widget); + void changeParameter(QObject * sender, int value); + Q_SIGNALS: void parametersChanged(const QStringList & name); private Q_SLOTS: void changeParameter(); void changeParameter(const QString & value); - void changeParameter(const int & value); + void changeParameter(bool value); + void changeParameter(int value); void resetCurrentPage(); void resetAllPages(); diff --git a/src/Vocabulary.cpp b/src/Vocabulary.cpp index 6d5ac5a8..62234225 100644 --- a/src/Vocabulary.cpp +++ b/src/Vocabulary.cpp @@ -144,14 +144,24 @@ bool Vocabulary::load(const QString & filename) return false; } -QMultiMap Vocabulary::addWords(const cv::Mat & descriptors, int objectId) +QMultiMap Vocabulary::addWords(const cv::Mat & descriptorsIn, int objectId) { QMultiMap words; - if (descriptors.empty()) + if (descriptorsIn.empty()) { return words; } + cv::Mat descriptors; + if(descriptorsIn.type() == CV_8U && Settings::getNearestNeighbor_7ConvertBinToFloat()) + { + descriptorsIn.convertTo(descriptors, CV_32F); + } + else + { + descriptors = descriptorsIn; + } + if(Settings::getGeneral_vocabularyIncremental() || Settings::getGeneral_vocabularyFixed()) { int k = 2; @@ -350,10 +360,20 @@ void Vocabulary::update() } } -void Vocabulary::search(const cv::Mat & descriptors, cv::Mat & results, cv::Mat & dists, int k) +void Vocabulary::search(const cv::Mat & descriptorsIn, cv::Mat & results, cv::Mat & dists, int k) { if(!indexedDescriptors_.empty()) { + cv::Mat descriptors; + if(descriptorsIn.type() == CV_8U && Settings::getNearestNeighbor_7ConvertBinToFloat()) + { + descriptorsIn.convertTo(descriptors, CV_32F); + } + else + { + descriptors = descriptorsIn; + } + UASSERT(descriptors.type() == indexedDescriptors_.type() && descriptors.cols == indexedDescriptors_.cols); if(Settings::isBruteForceNearestNeighbor())