37 #include <QtCore/QThread> 38 #include <QtCore/QFileInfo> 39 #include <QtCore/QStringList> 40 #include <QtCore/QTime> 41 #include <QtCore/QDir> 42 #include <QGraphicsRectItem> 50 detector_(
Settings::createKeypointDetector()),
51 extractor_(
Settings::createDescriptorExtractor()),
52 sessionModified_(false),
53 keepImagesInRAM_(keepImagesInRAM)
55 qRegisterMetaType<find_object::DetectionInfo>(
"find_object::DetectionInfo");
58 if(Settings::getGeneral_debug())
79 if(QFile::exists(path) && !path.isEmpty() && QFileInfo(path).suffix().compare(
"bin") == 0)
82 file.open(QIODevice::ReadOnly);
83 QDataStream
in(&file);
89 for(QMap<QString, QVariant>::iterator iter=parameters.begin(); iter!=parameters.end(); ++iter)
108 UERROR(
"Failed to load and object!");
114 if(!Settings::getGeneral_invertedSearch())
124 UERROR(
"Invalid session file (should be *.bin): \"%s\"", path.toStdString().c_str());
131 if(!path.isEmpty() && QFileInfo(path).suffix().compare(
"bin") == 0)
134 file.open(QIODevice::WriteOnly);
135 QDataStream out(&file);
144 for(QMultiMap<int, ObjSignature*>::const_iterator iter=
objects_.constBegin(); iter!=
objects_.constEnd(); ++iter)
146 iter.value()->save(out);
153 UERROR(
"Path \"%s\" not valid (should be *.bin)", path.toStdString().c_str());
164 if(!Settings::getGeneral_vocabularyFixed() || !Settings::getGeneral_invertedSearch())
166 UWARN(
"Doesn't make sense to load a vocabulary if \"General/vocabularyFixed\" and \"General/invertedSearch\" are not enabled! It will " 167 "be cleared at the time the objects are updated.");
182 QString formats = Settings::getGeneral_imageFormats().remove(
'*').remove(
'.');
185 paths.append(dirPath);
187 QList<int> idsLoaded;
190 QString currentDir = paths.front();
191 UDirectory dir(currentDir.toStdString(), formats.toStdString());
194 const std::list<std::string> & names = dir.
getFileNames();
195 for(std::list<std::string>::const_iterator iter=names.begin(); iter!=names.end(); ++iter)
200 idsLoaded.push_back(s->
id());
210 QStringList subDirs = d.entryList(QDir::AllDirs|QDir::NoDotAndDotDot, QDir::Name);
211 for(
int i=subDirs.size()-1; i>=0; --i)
213 paths.prepend(currentDir+QDir::separator()+subDirs[i]);
224 return idsLoaded.size();
229 if(!filePath.isNull())
231 cv::Mat img = cv::imread(filePath.toStdString().c_str(), cv::IMREAD_GRAYSCALE);
235 QFileInfo file(filePath);
236 QStringList list = file.fileName().split(
'.');
240 id = list.front().toInt(&ok);
245 UWARN(
"Object %d already added, a new ID will be generated (new id=%d).",
id, Settings::getGeneral_nextObjID());
256 UERROR(
"File name doesn't contain \".\" (\"%s\")", filePath.toStdString().c_str());
262 UINFO(
"Added object %d (%s)", s->id(), filePath.toStdString().c_str());
268 UERROR(
"Could not read image \"%s\"", filePath.toStdString().c_str());
273 UERROR(
"File path is null!?");
295 UERROR(
"object with id %d already added!", obj->
id());
298 else if(obj->
id() == 0)
300 obj->
setId(Settings::getGeneral_nextObjID());
303 Settings::setGeneral_nextObjID(obj->
id()+1);
333 ids.push_back(s->
id());
358 std::vector<cv::KeyPoint>
limitKeypoints(
const std::vector<cv::KeyPoint> & keypoints,
int maxKeypoints)
360 std::vector<cv::KeyPoint> kptsKept;
361 if(maxKeypoints > 0 && (
int)keypoints.size() > maxKeypoints)
364 std::multimap<float, int> reponseMap;
365 for(
unsigned int i = 0; i <keypoints.size(); ++i)
368 reponseMap.insert(std::pair<float, int>(fabs(keypoints[i].response), i));
372 std::multimap<float, int>::reverse_iterator iter = reponseMap.rbegin();
373 kptsKept.resize(maxKeypoints);
374 for(
unsigned int k=0; k < kptsKept.size() && iter!=reponseMap.rend(); ++k, ++iter)
376 kptsKept[k] = keypoints[iter->second];
381 kptsKept = keypoints;
386 void limitKeypoints(std::vector<cv::KeyPoint> & keypoints, cv::Mat & descriptors,
int maxKeypoints)
388 UASSERT((
int)keypoints.size() == descriptors.rows);
389 std::vector<cv::KeyPoint> kptsKept;
390 cv::Mat descriptorsKept;
391 if(maxKeypoints > 0 && (
int)keypoints.size() > maxKeypoints)
393 descriptorsKept = cv::Mat(0, descriptors.cols, descriptors.type());
396 std::multimap<float, int> reponseMap;
397 for(
unsigned int i = 0; i <keypoints.size(); ++i)
400 reponseMap.insert(std::pair<float, int>(fabs(keypoints[i].response), i));
404 std::multimap<float, int>::reverse_iterator iter = reponseMap.rbegin();
405 kptsKept.resize(maxKeypoints);
406 descriptorsKept.reserve(maxKeypoints);
407 for(
unsigned int k=0; k < kptsKept.size() && iter!=reponseMap.rend(); ++k, ++iter)
409 kptsKept[k] = keypoints[iter->second];
410 descriptorsKept.push_back(descriptors.row(iter->second));
413 keypoints = kptsKept;
414 descriptors = descriptorsKept;
415 UASSERT_MSG((
int)keypoints.size() == descriptors.rows,
uFormat(
"%d vs %d", (
int)keypoints.size(), descriptors.rows).c_str());
421 const cv::Mat & image,
422 const cv::Mat & mask,
423 std::vector<cv::KeyPoint> & keypoints,
424 cv::Mat & descriptors,
426 int & timeExtraction)
431 descriptors = cv::Mat();
433 int maxFeatures = Settings::getFeature2D_3MaxFeatures();
437 UASSERT_MSG((
int)keypoints.size() == descriptors.rows,
uFormat(
"%d vs %d", (
int)keypoints.size(), descriptors.rows).c_str());
438 if(maxFeatures > 0 && (
int)keypoints.size() > maxFeatures)
442 timeDetection=timeStep.restart();
447 detector->
detect(image, keypoints, mask);
448 if(maxFeatures > 0 && (
int)keypoints.size() > maxFeatures)
452 timeDetection=timeStep.restart();
457 extractor->
compute(image, keypoints, descriptors);
458 UASSERT_MSG((
int)keypoints.size() == descriptors.rows,
uFormat(
"%d vs %d", (
int)keypoints.size(), descriptors.rows).c_str());
460 catch(cv::Exception & e)
462 UERROR(
"Descriptor exception: %s. Maybe some keypoints are invalid " 463 "for the selected descriptor extractor.", e.what());
464 descriptors = cv::Mat();
467 catch (
const std::exception& e )
470 UERROR(
"Descriptor exception: %s. Maybe some keypoints are invalid " 471 "for the selected descriptor extractor.", e.what());
472 descriptors = cv::Mat();
475 timeExtraction+=timeStep.restart();
478 if( Settings::getFeature2D_SIFT_rootSIFT() &&
480 !descriptors.empty())
482 UINFO(
"Performing RootSIFT...");
486 for(
int i=0; i<descriptors.rows; ++i)
488 descriptors.row(i) = descriptors.row(i) / cv::sum(descriptors.row(i))[0];
489 cv::sqrt(descriptors.row(i), descriptors.row(i));
505 const cv::Mat & image,
510 float h = image.rows;
511 float w = image.cols;
512 cv::Mat A = cv::Mat::zeros(2,3,CV_32FC1);
513 A.at<
float>(0,0) = A.at<
float>(1,1) = 1;
514 skewMask = cv::Mat::ones(h, w, CV_8U) * 255;
517 phi = phi*CV_PI/180.0f;
518 float s = std::sin(phi);
519 float c = std::cos(phi);
520 cv::Mat A22 = (cv::Mat_<float>(2, 2) <<
523 cv::Mat cornersIn = (cv::Mat_<float>(4, 2) <<
528 cv::Mat cornersOut = cornersIn * A22.t();
529 cv::Rect rect = cv::boundingRect(cornersOut.reshape(2,4));
530 A = (cv::Mat_<float>(2, 3) <<
533 cv::warpAffine(image, skewImage, A, cv::Size(rect.width, rect.height), cv::INTER_LINEAR, cv::BORDER_REPLICATE);
541 float s = 0.8*std::sqrt(tilt*tilt-1);
543 cv::GaussianBlur(skewImage, out, cv::Size(0, 0), s, 0.01);
544 cv::resize(out, out2, cv::Size(0, 0), 1.0/tilt, 1.0, cv::INTER_NEAREST);
548 if(phi != 0.0 || tilt != 1.0)
550 cv::Mat mask = skewMask;
551 cv::warpAffine(mask, skewMask, A, skewImage.size(), cv::INTER_NEAREST);
553 cv::invertAffineTransform(A, Ai);
562 const cv::Mat & image,
575 UASSERT(detector && extractor);
577 const cv::Mat &
image()
const {
return image_;}
578 const std::vector<cv::KeyPoint> &
keypoints()
const {
return keypoints_;}
591 cv::Mat skewImage, skewMask, Ai;
593 timeSkewAffine_=timeStep.restart();
608 for(
unsigned int i=0; i<keypoints_.size(); ++i)
610 cv::Mat p = (cv::Mat_<float>(3, 1) << keypoints_[i].pt.x, keypoints_[i].pt.y, 1);
612 keypoints_[i].pt.x = pa.at<
float>(0,0);
613 keypoints_[i].pt.y = pa.at<
float>(1,0);
616 if(keypoints_.size() && Settings::getFeature2D_6SubPix())
619 std::vector<cv::Point2f> corners;
620 cv::KeyPoint::convert(keypoints_, corners);
621 cv::cornerSubPix(image_,
623 cv::Size(Settings::getFeature2D_7SubPixWinSize(), Settings::getFeature2D_7SubPixWinSize()),
625 cv::TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, Settings::getFeature2D_8SubPixIterations(), Settings::getFeature2D_9SubPixEps() ));
626 UASSERT(corners.size() == keypoints_.size());
627 for(
unsigned int i=0; i<corners.size(); ++i)
629 keypoints_[i].pt = corners[i];
631 timeSubPix_ +=timeStep.restart();
656 const cv::Mat & image) :
666 UASSERT(detector && extractor);
667 UASSERT_MSG(!image.empty() && image.type() == CV_8UC1,
668 uFormat(
"Image of object %d is null or not type CV_8UC1!?!? (cols=%d, rows=%d, type=%d)",
669 objectId, image.cols, image.rows, image.type()).c_str());
672 const cv::Mat &
image()
const {
return image_;}
673 const std::vector<cv::KeyPoint> &
keypoints()
const {
return keypoints_;}
686 UINFO(
"Extracting descriptors from object %d...", objectId_);
691 if(!Settings::getFeature2D_4Affine())
704 if(keypoints_.size())
706 UDEBUG(
"Detected %d features from object %d...", (
int)keypoints_.size(), objectId_);
707 if(Settings::getFeature2D_6SubPix())
710 std::vector<cv::Point2f> corners;
711 cv::KeyPoint::convert(keypoints_, corners);
712 cv::cornerSubPix(image_,
714 cv::Size(Settings::getFeature2D_7SubPixWinSize(), Settings::getFeature2D_7SubPixWinSize()),
716 cv::TermCriteria( CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, Settings::getFeature2D_8SubPixIterations(), Settings::getFeature2D_9SubPixEps() ));
717 UASSERT(corners.size() == keypoints_.size());
718 for(
unsigned int i=0; i<corners.size(); ++i)
720 keypoints_[i].pt = corners[i];
722 timeSubPix_ +=timeStep.restart();
727 UWARN(
"no features detected in object %d !?!", objectId_);
733 std::vector<float> tilts;
734 std::vector<float> phis;
735 tilts.push_back(1.0
f);
736 phis.push_back(0.0
f);
737 int nTilt = Settings::getFeature2D_5AffineCount();
738 for(
int t=1; t<nTilt; ++t)
740 float tilt = std::pow(2.0
f, 0.5
f*
float(t));
741 float inc = 72.0f / float(tilt);
742 for(
float phi=0.0
f; phi<180.0f; phi+=inc)
744 tilts.push_back(tilt);
750 unsigned int threadCounts = Settings::getGeneral_threads();
751 if(threadCounts == 0)
753 threadCounts = (
unsigned int)tilts.size();
756 for(
unsigned int i=0; i<tilts.size(); i+=threadCounts)
758 QVector<AffineExtractionThread*> threads;
760 for(
unsigned int k=i; k<i+threadCounts && k<tilts.size(); ++k)
763 threads.back()->start();
766 for(
int k=0; k<threads.size(); ++k)
770 keypoints_.insert(keypoints_.end(), threads[k]->keypoints().begin(), threads[k]->keypoints().end());
771 descriptors_.push_back(threads[k]->descriptors());
773 timeSkewAffine_ += threads[k]->timeSkewAffine();
774 timeDetection_ += threads[k]->timeDetection();
775 timeExtraction_ += threads[k]->timeExtraction();
776 timeSubPix_ += threads[k]->timeSubPix();
781 UINFO(
"%d descriptors extracted from object %d (in %d ms)", descriptors_.rows, objectId_, time.elapsed());
799 QList<ObjSignature*> objectsList;
802 for(
int i=0; i<ids.size(); ++i)
806 objectsList.push_back(
objects_[ids[i]]);
810 UERROR(
"Not found object %d!", ids[i]);
819 if(objectsList.size())
822 int threadCounts = Settings::getGeneral_threads();
823 if(threadCounts == 0)
825 threadCounts = objectsList.size();
831 if(objectsList.size())
833 UINFO(
"Features extraction from %d objects...", objectsList.size());
834 for(
int i=0; i<objectsList.size(); i+=threadCounts)
836 QVector<ExtractFeaturesThread*> threads;
837 for(
int k=i; k<i+threadCounts && k<objectsList.size(); ++k)
839 if(!objectsList.at(k)->image().empty())
842 threads.back()->start();
846 objects_.value(objectsList.at(k)->id())->setData(std::vector<cv::KeyPoint>(), cv::Mat());
849 UERROR(
"Empty image detected for object %d!? No features can be detected.", objectsList.at(k)->id());
854 UWARN(
"Empty image detected for object %d! No features can be detected. Note that images are in not kept in RAM.", objectsList.at(k)->id());
859 for(
int j=0; j<threads.size(); ++j)
863 int id = threads[j]->objectId();
865 objects_.value(
id)->setData(threads[j]->keypoints(), threads[j]->descriptors());
873 UINFO(
"Features extraction from %d objects... done! (%d ms)", objectsList.size(), time.elapsed());
878 UINFO(
"No objects to update...");
894 QList<ObjSignature*> objectsList;
897 for(
int i=0; i<ids.size(); ++i)
901 objectsList.push_back(
objects_[ids[i]]);
905 UERROR(
"Not found object %d!", ids[i]);
921 for(
int i=0; i<objectsList.size(); ++i)
923 if(!objectsList.at(i)->descriptors().empty())
925 if(dim >= 0 && objectsList.at(i)->descriptors().cols != dim)
927 UERROR(
"Descriptors of the objects are not all the same size! Objects " 928 "opened must have all the same size (and from the same descriptor extractor).");
931 dim = objectsList.at(i)->descriptors().cols;
932 if(type >= 0 && objectsList.at(i)->descriptors().type() != type)
934 UERROR(
"Descriptors of the objects are not all the same type! Objects opened " 935 "must have been processed by the same descriptor extractor.");
938 type = objectsList.at(i)->descriptors().type();
939 count += objectsList.at(i)->descriptors().rows;
946 UINFO(
"Updating global descriptors matrix: Objects=%d, total descriptors=%d, dim=%d, type=%d",
947 (
int)
objects_.size(), count, dim, type);
948 if(!Settings::getGeneral_invertedSearch())
950 if(Settings::getGeneral_threads() == 1)
964 for(
int i=0; i<objectsList.size(); ++i)
966 objectsList[i]->setWords(QMultiMap<int,int>());
967 if(objectsList.at(i)->descriptors().rows)
971 cv::Mat dest(
objectsDescriptors_.begin().value(), cv::Range(row, row+objectsList.at(i)->descriptors().rows));
972 objectsList.at(i)->descriptors().copyTo(dest);
982 row += objectsList.at(i)->descriptors().rows;
986 if(objectsList.at(i)->descriptors().rows)
988 dataRange_.insert(row-1, objectsList.at(i)->id());
995 for(
int i=0; i<objectsList.size(); ++i)
997 objectsList[i]->setWords(QMultiMap<int,int>());
1008 bool incremental = Settings::getGeneral_vocabularyIncremental() && !Settings::getGeneral_vocabularyFixed();
1011 UINFO(
"Creating incremental vocabulary...");
1013 else if(Settings::getGeneral_vocabularyFixed())
1015 UINFO(
"Updating vocabulary correspondences only (vocabulary is fixed)...");
1019 UINFO(
"Creating vocabulary...");
1023 int updateVocabularyMinWords = Settings::getGeneral_vocabularyUpdateMinWords();
1025 for(
int i=0; i<objectsList.size(); ++i)
1027 UASSERT(objectsList[i]->descriptors().rows == (
int)objectsList[i]->keypoints().size());
1028 QMultiMap<int, int> words =
vocabulary_->
addWords(objectsList[i]->descriptors(), objectsList.at(i)->id());
1029 objectsList[i]->setWords(words);
1030 addedWords += words.uniqueKeys().size();
1031 bool updated =
false;
1032 if(incremental && addedWords && addedWords >= updateVocabularyMinWords)
1038 UINFO(
"Object %d, %d words from %d descriptors (%d words, %d ms) %s",
1039 objectsList[i]->
id(),
1040 words.uniqueKeys().size(),
1041 objectsList[i]->descriptors().rows,
1043 localTime.restart(),
1044 updated?
"updated":
"");
1046 if(addedWords && !Settings::getGeneral_vocabularyFixed())
1053 UINFO(
"Creating incremental vocabulary... done! size=%d (%d ms)",
vocabulary_->
size(), time.elapsed());
1055 else if(Settings::getGeneral_vocabularyFixed())
1057 UINFO(
"Updating vocabulary correspondences only (vocabulary is fixed)... done! size=%d (%d ms)",
vocabulary_->
size(), time.elapsed());
1072 objectId_(objectId),
1073 descriptors_(descriptors),
1074 sceneWords_(sceneWords),
1075 minMatchedDistance_(-1.0
f),
1076 maxMatchedDistance_(-1.0
f)
1085 const QMultiMap<int, int> &
getMatches()
const {
return matches_;}
1097 int k = Settings::getNearestNeighbor_3nndrRatioUsed()?2:1;
1098 results = cv::Mat(descriptors_->rows, k, CV_32SC1);
1099 dists = cv::Mat(descriptors_->rows, k, CV_32FC1);
1104 for(
int i=0; i<dists.rows; ++i)
1107 bool matched =
false;
1109 if(Settings::getNearestNeighbor_3nndrRatioUsed() &&
1110 dists.at<
float>(i,0) <= Settings::getNearestNeighbor_4nndrRatio() * dists.at<
float>(i,1))
1114 if((matched || !Settings::getNearestNeighbor_3nndrRatioUsed()) &&
1115 Settings::getNearestNeighbor_5minDistanceUsed())
1117 if(dists.at<
float>(i,0) <= Settings::getNearestNeighbor_6minDistance())
1126 if(!matched && !Settings::getNearestNeighbor_3nndrRatioUsed() && !Settings::getNearestNeighbor_5minDistanceUsed())
1130 if(minMatchedDistance_ == -1 || minMatchedDistance_ > dists.at<
float>(i,0))
1132 minMatchedDistance_ = dists.at<
float>(i,0);
1134 if(maxMatchedDistance_ == -1 || maxMatchedDistance_ < dists.at<
float>(i,0))
1136 maxMatchedDistance_ = dists.at<
float>(i,0);
1139 int wordId = results.at<
int>(i,0);
1140 if(matched && sceneWords_->count(wordId) == 1)
1142 matches_.insert(i, sceneWords_->value(wordId));
1143 matches_.insert(i, results.at<
int>(i,0));
1164 const QMultiMap<int, int> * matches,
1166 const std::vector<cv::KeyPoint> * kptsA,
1167 const std::vector<cv::KeyPoint> * kptsB,
1168 const cv::Mat & imageA,
1169 const cv::Mat & imageB) :
1171 objectId_(objectId),
1178 UASSERT(matches && kptsA && kptsB);
1197 std::vector<cv::Point2f> mpts_1(matches_->size());
1198 std::vector<cv::Point2f> mpts_2(matches_->size());
1199 indexesA_.resize(matches_->size());
1200 indexesB_.resize(matches_->size());
1202 UDEBUG(
"Fill matches...");
1204 for(QMultiMap<int, int>::const_iterator iter = matches_->begin(); iter!=matches_->end(); ++iter)
1206 UASSERT_MSG(iter.key() < (int)kptsA_->size(),
uFormat(
"key=%d size=%d", iter.key(),(int)kptsA_->size()).c_str());
1207 UASSERT_MSG(iter.value() < (int)kptsB_->size(),
uFormat(
"key=%d size=%d", iter.value(),(int)kptsB_->size()).c_str());
1208 mpts_1[j] = kptsA_->at(iter.key()).pt;
1209 indexesA_[j] = iter.key();
1210 mpts_2[j] = kptsB_->at(iter.value()).pt;
1211 indexesB_[j] = iter.value();
1215 if((
int)mpts_1.size() >= Settings::getHomography_minimumInliers())
1217 if(Settings::getHomography_opticalFlow())
1219 UASSERT(!imageA_.empty() && !imageB_.empty());
1221 cv::Mat imageA = imageA_;
1222 cv::Mat imageB = imageB_;
1223 if(imageA_.cols < imageB_.cols && imageA_.rows < imageB_.rows)
1226 imageA = cv::Mat::zeros(imageB_.size(), imageA_.type());
1227 imageA_.copyTo(imageA(cv::Rect(0,0,imageA_.cols, imageA_.rows)));
1229 if(imageA.size() == imageB.size())
1231 UDEBUG(
"Optical flow...");
1233 std::vector<unsigned char> status;
1234 std::vector<float> err;
1235 cv::calcOpticalFlowPyrLK(
1242 cv::Size(Settings::getHomography_opticalFlowWinSize(), Settings::getHomography_opticalFlowWinSize()),
1243 Settings::getHomography_opticalFlowMaxLevel(),
1244 cv::TermCriteria(cv::TermCriteria::COUNT+cv::TermCriteria::EPS, Settings::getHomography_opticalFlowIterations(), Settings::getHomography_opticalFlowEps()),
1245 cv::OPTFLOW_LK_GET_MIN_EIGENVALS | cv::OPTFLOW_USE_INITIAL_FLOW, 1e-4);
1249 UERROR(
"Object's image should be less/equal size of the scene image to use Optical Flow.");
1253 UDEBUG(
"Find homography... begin");
1254 #if CV_MAJOR_VERSION < 3 1255 h_ = findHomography(mpts_1,
1258 Settings::getHomography_ransacReprojThr(),
1261 h_ = findHomography(mpts_1,
1264 Settings::getHomography_ransacReprojThr(),
1266 Settings::getHomography_maxIterations(),
1267 Settings::getHomography_confidence());
1269 UDEBUG(
"Find homography... end");
1271 UASSERT(outlierMask_.size() == 0 || outlierMask_.size() == mpts_1.size());
1272 for(
unsigned int k=0; k<mpts_1.size();++k)
1274 if(outlierMask_.size() && outlierMask_.at(k))
1276 inliers_.insert(indexesA_[k], indexesB_[k]);
1280 outliers_.insert(indexesA_[k], indexesB_[k]);
1284 if(inliers_.size() == (int)outlierMask_.size() && !h_.empty())
1286 if(Settings::getHomography_ignoreWhenAllInliers() || cv::countNonZero(h_) < 1)
1323 this->
detect(image, info);
1327 UINFO(
"(%s) %d objects detected! (%d ms)",
1328 QTime::currentTime().toString(
"HH:mm:ss.zzz").toStdString().c_str(),
1334 UINFO(
"(%s) Object %d detected! (%d ms)",
1335 QTime::currentTime().toString(
"HH:mm:ss.zzz").toStdString().c_str(),
1339 else if(Settings::getGeneral_sendNoObjDetectedEvents())
1341 UINFO(
"(%s) No objects detected. (%d ms)",
1342 QTime::currentTime().toString(
"HH:mm:ss.zzz").toStdString().c_str(),
1346 if(info.
objDetected_.size() > 0 || Settings::getGeneral_sendNoObjDetectedEvents())
1360 bool success =
false;
1364 cv::Mat grayscaleImg;
1365 if(image.channels() != 1 || image.depth() != CV_8U)
1367 cv::cvtColor(image, grayscaleImg, cv::COLOR_BGR2GRAY);
1371 grayscaleImg = image;
1375 UDEBUG(
"DETECT FEATURES AND EXTRACT DESCRIPTORS FROM THE SCENE");
1377 extractThread.start();
1378 extractThread.wait();
1390 bool descriptorsValid = !Settings::getGeneral_invertedSearch() &&
1395 bool vocabularyValid = Settings::getGeneral_invertedSearch() &&
1404 if((descriptorsValid || vocabularyValid) &&
1412 QMultiMap<int, int> words;
1414 if(!Settings::getGeneral_invertedSearch())
1418 UDEBUG(
"CREATE INDEX FOR THE SCENE");
1425 for(QMap<int, ObjSignature*>::const_iterator iter=
objects_.begin(); iter!=
objects_.end(); ++iter)
1427 info.
matches_.insert(iter.key(), QMultiMap<int, int>());
1430 if(Settings::getGeneral_invertedSearch() || Settings::getGeneral_threads() == 1)
1435 UDEBUG(
"DO NEAREST NEIGHBOR");
1436 int k = Settings::getNearestNeighbor_3nndrRatioUsed()?2:1;
1437 if(!Settings::getGeneral_invertedSearch())
1453 UDEBUG(
"PROCESS RESULTS");
1455 for(
int i=0; i<dists.rows; ++i)
1458 bool matched =
false;
1460 if(Settings::getNearestNeighbor_3nndrRatioUsed() &&
1461 dists.at<
float>(i,0) <= Settings::getNearestNeighbor_4nndrRatio() * dists.at<
float>(i,1))
1465 if((matched || !Settings::getNearestNeighbor_3nndrRatioUsed()) &&
1466 Settings::getNearestNeighbor_5minDistanceUsed())
1468 if(dists.at<
float>(i,0) <= Settings::getNearestNeighbor_6minDistance())
1478 !Settings::getNearestNeighbor_3nndrRatioUsed() &&
1479 !Settings::getNearestNeighbor_5minDistanceUsed() &&
1480 dists.at<
float>(i,0) >= 0.0f)
1495 int wordId = results.at<
int>(i,0);
1496 if(Settings::getGeneral_invertedSearch())
1500 for(
int j=0; j<objIds.size(); ++j)
1505 info.
matches_.find(objIds[j]).value().insert(
objects_.value(objIds[j])->words().value(wordId), i);
1511 QMap<int, int>::const_iterator iter =
dataRange_.lowerBound(i);
1512 int objectId = iter.value();
1513 int fisrtObjectDescriptorIndex = (iter ==
dataRange_.begin())?0:(--iter).key()+1;
1514 int objectDescriptorIndex = i - fisrtObjectDescriptorIndex;
1516 if(words.count(wordId) == 1)
1518 info.
matches_.find(objectId).value().insert(objectDescriptorIndex, words.value(wordId));
1527 UDEBUG(
"MULTI-THREADED, MATCH OBJECTS TO SCENE");
1528 int threadCounts = Settings::getGeneral_threads();
1529 if(threadCounts == 0)
1536 for(
int j=0; j<objectsDescriptorsMat.size(); j+=threadCounts)
1538 QVector<SearchThread*> threads;
1540 for(
int k=j; k<j+threadCounts && k<objectsDescriptorsMat.size(); ++k)
1543 threads.back()->start();
1546 for(
int k=0; k<threads.size(); ++k)
1549 info.
matches_[threads[k]->getObjectId()] = threads[k]->getMatches();
1568 if(Settings::getHomography_homographyComputed())
1571 UDEBUG(
"COMPUTE HOMOGRAPHY");
1572 int threadCounts = Settings::getGeneral_threads();
1573 if(threadCounts == 0)
1575 threadCounts = info.
matches_.size();
1577 QList<int> matchesId = info.
matches_.keys();
1578 QList<QMultiMap<int, int> > matchesList = info.
matches_.values();
1579 for(
int i=0; i<matchesList.size(); i+=threadCounts)
1581 UDEBUG(
"Processing matches %d/%d", i+1, matchesList.size());
1583 QVector<HomographyThread*> threads;
1585 UDEBUG(
"Creating/Starting homography threads (%d)...", threadCounts);
1586 for(
int k=i; k<i+threadCounts && k<matchesList.size(); ++k)
1588 int objectId = matchesId[k];
1593 &
objects_.value(objectId)->keypoints(),
1597 threads.back()->start();
1599 UDEBUG(
"Started homography threads");
1601 for(
int j=0; j<threads.size(); ++j)
1604 UDEBUG(
"Processing results of homography thread %d", j);
1606 int id = threads[j]->getObjectId();
1607 QTransform hTransform;
1609 if(threads[j]->getHomography().empty())
1611 code = threads[j]->rejectedCode();
1614 threads[j]->getInliers().size() < Settings::getHomography_minimumInliers() )
1620 const cv::Mat & H = threads[j]->getHomography();
1621 UASSERT(H.cols == 3 && H.rows == 3 && H.type()==CV_64FC1);
1622 hTransform = QTransform(
1623 H.at<
double>(0,0), H.at<
double>(1,0), H.at<
double>(2,0),
1624 H.at<
double>(0,1), H.at<
double>(1,1), H.at<
double>(2,1),
1625 H.at<
double>(0,2), H.at<
double>(1,2), H.at<
double>(2,2));
1632 QRectF objectRect =
objects_.value(
id)->rect();
1633 QGraphicsRectItem item(objectRect);
1634 item.setTransform(hTransform);
1635 QPolygonF rectH = item.mapToScene(item.rect());
1638 for(
int p=0; p<rectH.size(); ++p)
1640 if((rectH.at(p).x() < -image.cols && rectH.at(p).x() < -objectRect.width()) ||
1641 (rectH.at(p).x() > image.cols*2 && rectH.at(p).x() > objectRect.width()*2) ||
1642 (rectH.at(p).y() < -image.rows && rectH.at(p).x() < -objectRect.height()) ||
1643 (rectH.at(p).y() > image.rows*2 && rectH.at(p).x() > objectRect.height()*2))
1652 Settings::getHomography_minAngle() > 0)
1654 for(
int a=0; a<rectH.size(); ++a)
1657 QLineF ab(rectH.at(a).x(), rectH.at(a).y(), rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y());
1658 QLineF cb(rectH.at((a+1)%4).x(), rectH.at((a+1)%4).y(), rectH.at((a+2)%4).x(), rectH.at((a+2)%4).y());
1659 float angle = ab.angle(cb);
1660 float minAngle = (float)Settings::getHomography_minAngle();
1661 if(angle < minAngle ||
1662 angle > 180.0-minAngle)
1672 Settings::getGeneral_multiDetection())
1674 int distance = Settings::getGeneral_multiDetectionRadius();
1676 matchesList.push_back(threads[j]->getOutliers());
1677 matchesId.push_back(
id);
1680 QMultiMap<int, QTransform>::iterator objIter = info.
objDetected_.find(
id);
1681 for(;objIter!=info.
objDetected_.end() && objIter.key() == id; ++objIter)
1683 qreal dx = objIter.value().m31() - hTransform.m31();
1684 qreal dy = objIter.value().m32() - hTransform.m32();
1685 int d = (int)sqrt(dx*dx + dy*dy);
1692 if(distance < Settings::getGeneral_multiDetectionRadius())
1700 Settings::getHomography_allCornersVisible())
1703 QRectF sceneRect(0,0,image.cols, image.rows);
1704 for(
int p=0; p<rectH.size(); ++p)
1706 if(!sceneRect.contains(QPointF(rectH.at(p).x(), rectH.at(p).y())))
1734 UDEBUG(
"Processed matches %d", i+1);
1739 else if((descriptorsValid || vocabularyValid) && info.
sceneKeypoints_.size())
1741 UWARN(
"Cannot search, objects must be updated");
1746 UWARN(
"No features detected in the scene!?!");
static void affineSkew(float tilt, float phi, const cv::Mat &image, cv::Mat &skewImage, cv::Mat &skewMask, cv::Mat &Ai)
static int getHomographyMethod()
std::string uFormat(const char *fmt,...)
static Feature2D * createKeypointDetector()
QMultiMap< int, int > matches_
void save(QDataStream &streamSessionPtr) const
const std::list< std::string > & getFileNames() const
QMultiMap< int, QString > objDetectedFilePaths_
virtual void detectAndCompute(const cv::Mat &image, std::vector< cv::KeyPoint > &keypoints, cv::Mat &descriptors, const cv::Mat &mask=cv::Mat())
void load(QDataStream &streamSessionPtr)
QMap< int, cv::Mat > objectsDescriptors_
const cv::Mat & indexedDescriptors() const
std::vector< cv::KeyPoint > sceneKeypoints_
FindObject(bool keepImagesInRAM_=true, QObject *parent=0)
DetectionInfo::RejectedCode code_
QMultiMap< int, QSize > objDetectedSizes_
virtual ~HomographyThread()
const QMultiMap< int, int > * sceneWords_
std::vector< cv::KeyPoint > limitKeypoints(const std::vector< cv::KeyPoint > &keypoints, int maxKeypoints)
const std::vector< int > & getIndexesA() const
Some conversion functions.
QMultiMap< int, RejectedCode > rejectedCodes_
TFSIMD_FORCE_INLINE tfScalar distance(const Vector3 &v) const
QMap< int, QMultiMap< int, int > > matches_
std::vector< int > indexesB_
cv::Mat sceneDescriptors_
static void setLevel(ULogger::Level level)
void addObjectAndUpdate(const cv::Mat &image, int id=0, const QString &filePath=QString())
void objectsFound(const find_object::DetectionInfo &)
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
const QMultiMap< int, int > & getMatches() const
#define UASSERT(condition)
static QString currentDetectorType()
static const ParametersMap & getParameters()
QMultiMap< int, int > addWords(const cv::Mat &descriptors, int objectId)
void search(const cv::Mat &descriptors, cv::Mat &results, cv::Mat &dists, int k)
void updateVocabulary(const QList< int > &ids=QList< int >())
const std::vector< uchar > & getOutlierMask() const
QMultiMap< int, int > objDetectedInliersCount_
float getMinMatchedDistance() const
#define UASSERT_MSG(condition, msg_str)
int loadObjects(const QString &dirPath, bool recursive=false)
QMap< TimeStamp, float > timeStamps_
void updateDetectorExtractor()
virtual void detect(const cv::Mat &image, std::vector< cv::KeyPoint > &keypoints, const cv::Mat &mask=cv::Mat())
bool loadVocabulary(const QString &filePath)
QMap< int, ObjSignature * > objects_
HomographyThread(const QMultiMap< int, int > *matches, int objectId, const std::vector< cv::KeyPoint > *kptsA, const std::vector< cv::KeyPoint > *kptsB, const cv::Mat &imageA, const cv::Mat &imageB)
QMultiMap< int, int > getOutliers() const
QMultiMap< int, QTransform > objDetected_
const std::vector< int > & getIndexesB() const
void updateObjects(const QList< int > &ids=QList< int >())
DetectionInfo::RejectedCode rejectedCode() const
static void setParameter(const QString &key, const QVariant &value)
float minMatchedDistance_
const std::vector< cv::KeyPoint > * kptsA_
QMap< QString, QVariant > ParametersMap
static QString currentDescriptorType()
QMultiMap< int, QMultiMap< int, int > > rejectedInliers_
static void setPrintWhere(bool printWhere)
std::vector< uchar > outlierMask_
QMultiMap< int, QMultiMap< int, int > > objDetectedInliers_
void computeFeatures(Feature2D *detector, Feature2D *extractor, const cv::Mat &image, const cv::Mat &mask, std::vector< cv::KeyPoint > &keypoints, cv::Mat &descriptors, int &timeDetection, int &timeExtraction)
QMultiMap< int, int > getInliers() const
QMultiMap< int, int > objDetectedOutliersCount_
TFSIMD_FORCE_INLINE const tfScalar & w() const
void load(QDataStream &streamPtr, bool ignoreImage)
virtual void compute(const cv::Mat &image, std::vector< cv::KeyPoint > &keypoints, cv::Mat &descriptors)
void removeObject(int id)
static bool in(Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4)
float getMaxMatchedDistance() const
const Vocabulary * vocabulary() const
bool loadSession(const QString &path)
bool saveSession(const QString &path)
const QMultiMap< int, int > * matches_
QMultiMap< int, int > inliers_
QMultiMap< int, int > sceneWords_
ULogger class and convenient macros.
const std::vector< cv::KeyPoint > * kptsB_
QMultiMap< int, QMultiMap< int, int > > rejectedOutliers_
float maxMatchedDistance_
static Feature2D * createDescriptorExtractor()
float maxMatchedDistance_
SearchThread(Vocabulary *vocabulary, int objectId, const cv::Mat *descriptors, const QMultiMap< int, int > *sceneWords)
const cv::Mat * descriptors_
float minMatchedDistance_
std::vector< int > indexesA_
const QMultiMap< int, int > & wordToObjects() const
QMultiMap< int, QMultiMap< int, int > > objDetectedOutliers_
QMultiMap< int, int > outliers_
const ObjSignature * addObject(const QString &filePath)
QMap< int, int > dataRange_
void removeObjectAndUpdate(int id)
const cv::Mat & getHomography() const
bool detect(const cv::Mat &image, find_object::DetectionInfo &info) const
bool saveVocabulary(const QString &filePath) const