diff --git a/AlgorithmEstimation.cpp b/AlgorithmEstimation.cpp index 40b1e6e..eb02b51 100644 --- a/AlgorithmEstimation.cpp +++ b/AlgorithmEstimation.cpp @@ -2,128 +2,135 @@ bool computeMatchesDistanceStatistics(const Matches& matches, float& meanDistance, float& stdDev) { - if (matches.empty()) + if (matches.empty()) + return false; + + std::vector distances(matches.size()); + for (size_t i=0; i(mean.val[0]); + stdDev = static_cast(dev.val[0]); + return false; - - std::vector distances(matches.size()); - for (size_t i=0; i(mean.val[0]); - stdDev = static_cast(dev.val[0]); - - return false; } void ratioTest(const std::vector& knMatches, float maxRatio, Matches& goodMatches) { - goodMatches.clear(); - - for (size_t i=0; i< knMatches.size(); i++) - { - const cv::DMatch& best = knMatches[i][0]; - const cv::DMatch& good = knMatches[i][1]; - - assert(best.distance <= good.distance); - float ratio = (best.distance / good.distance); - - if (ratio <= maxRatio) + goodMatches.clear(); + + for (size_t i=0; i< knMatches.size(); i++) { - goodMatches.push_back(best); + const cv::DMatch& best = knMatches[i][0]; + const cv::DMatch& good = knMatches[i][1]; + + assert(best.distance <= good.distance); + float ratio = (best.distance / good.distance); + + if (ratio <= maxRatio) + { + goodMatches.push_back(best); + } } - } } bool performEstimation ( - const FeatureAlgorithm& alg, - const ImageTransformation& transformation, - const cv::Mat& sourceImage, - std::vector& stat -) + const FeatureAlgorithm& alg, + const ImageTransformation& transformation, + const cv::Mat& sourceImage, + std::vector& stat + ) { - Keypoints sourceKp; - Descriptors sourceDesc; - - cv::Mat gray; - if (sourceImage.channels() == 3) - cv::cvtColor(sourceImage, gray, CV_BGR2GRAY); - else if (sourceImage.channels() == 4) - cv::cvtColor(sourceImage, gray, CV_BGRA2GRAY); - else if(sourceImage.channels() == 1) - gray = sourceImage; - - if (!alg.extractFeatures(gray, sourceKp, sourceDesc)) - return false; - - std::vector x = transformation.getX(); - stat.resize(x.size()); - - const int count = x.size(); - - cv::Mat transformedImage; - Keypoints resKpReal; - Descriptors resDesc; - Matches matches; - - // To convert ticks to milliseconds - const double toMsMul = 1000. / cv::getTickFrequency(); - + Keypoints sourceKp; + Descriptors sourceDesc; + + cv::Mat gray; + if (sourceImage.channels() == 3) + cv::cvtColor(sourceImage, gray, CV_BGR2GRAY); + else if (sourceImage.channels() == 4) + cv::cvtColor(sourceImage, gray, CV_BGRA2GRAY); + else if(sourceImage.channels() == 1) + gray = sourceImage; + + if (!alg.extractFeatures(gray, sourceKp, sourceDesc)) + return false; + + std::vector x = transformation.getX(); + stat.resize(x.size()); + + const int count = x.size(); + + cv::Mat transformedImage; + Keypoints resKpReal; + Descriptors resDesc; + Matches matches; + + // To convert ticks to milliseconds + const double toMsMul = 1000. / cv::getTickFrequency(); + #pragma omp parallel for private(transformedImage, resKpReal, resDesc, matches) - for (int i = 0; i < count; i++) - { - float arg = x[i]; - FrameMatchingStatistics& s = stat[i]; - - transformation.transform(arg, gray, transformedImage); - - int64 start = cv::getTickCount(); - - alg.extractFeatures(transformedImage, resKpReal, resDesc); - - // Initialize required fields - s.isValid = resKpReal.size() > 0; - s.argumentValue = arg; - - if (!s.isValid) - continue; - - if (alg.knMatchSupported) + for (int i = 0; i < count; i++) { - std::vector knMatches; - alg.matchFeatures(sourceDesc, resDesc, 2, knMatches); - ratioTest(knMatches, 0.75, matches); - - // Compute percent of false matches that were rejected by ratio test - s.ratioTestFalseLevel = (float)(knMatches.size() - matches.size()) / (float) knMatches.size(); + float arg = x[i]; + FrameMatchingStatistics& s = stat[i]; + + transformation.transform(arg, gray, transformedImage); + cv::Mat expectedHomography = transformation.getHomography(arg, gray); + + + int64 start = cv::getTickCount(); + + alg.extractFeatures(transformedImage, resKpReal, resDesc); + + // Initialize required fields + s.isValid = resKpReal.size() > 0; + s.argumentValue = arg; + + if (!s.isValid) + continue; + + if (alg.knMatchSupported) + { + std::vector knMatches; + alg.matchFeatures(sourceDesc, resDesc, 2, knMatches); + ratioTest(knMatches, 0.75, matches); + + // Compute percent of false matches that were rejected by ratio test + s.ratioTestFalseLevel = (float)(knMatches.size() - matches.size()) / (float) knMatches.size(); + } + else + { + alg.matchFeatures(sourceDesc, resDesc, matches); + } + + int64 end = cv::getTickCount(); + + Matches correctMatches; + cv::Mat homography; + bool homographyFound = ImageTransformation::findHomography(sourceKp, resKpReal, matches, correctMatches, homography); + + // Some simple stat: + s.isValid = homographyFound; + s.totalKeypoints = resKpReal.size(); + s.consumedTimeMs = (end - start) * toMsMul; + + // Compute overall percent of matched keypoints + s.percentOfMatches = (float) matches.size() / (float)(std::min(sourceKp.size(), resKpReal.size())); + s.correctMatchesPercent = (float) correctMatches.size() / (float)matches.size(); + + // Compute matching statistics + computeMatchesDistanceStatistics(correctMatches, s.meanDistance, s.stdDevDistance); + + if (homographyFound) + { + s.homographyError = cv::norm(expectedHomography * homography.inv(), cv::NORM_INF); + } } - else - { - alg.matchFeatures(sourceDesc, resDesc, matches); - } - - int64 end = cv::getTickCount(); - - Matches correctMatches; - cv::Mat homography; - bool homographyFound = ImageTransformation::findHomography(sourceKp, resKpReal, matches, correctMatches, homography); - - // Some simple stat: - s.isValid = homographyFound; - s.totalKeypoints = resKpReal.size(); - s.consumedTimeMs = (end - start) * toMsMul; - - // Compute overall percent of matched keypoints - s.percentOfMatches = (float) matches.size() / (float)(std::min(sourceKp.size(), resKpReal.size())); - s.correctMatchesPercent = (float) correctMatches.size() / (float)matches.size(); - - // Compute matching statistics - computeMatchesDistanceStatistics(correctMatches, s.meanDistance, s.stdDevDistance); - } - - return true; + + return true; } diff --git a/CollectedStatistics.cpp b/CollectedStatistics.cpp index 6fc46f6..14caf0b 100644 --- a/CollectedStatistics.cpp +++ b/CollectedStatistics.cpp @@ -33,6 +33,7 @@ FrameMatchingStatistics::FrameMatchingStatistics() stdDevDistance = 0; correctMatchesPercent = 0; consumedTimeMs = 0; + homographyError = std::numeric_limits::max(); isValid = false; } @@ -42,36 +43,39 @@ std::ostream& FrameMatchingStatistics::writeElement(std::ostream& str, Statistic { switch(elem) { - case StatisticsElementPercentOfCorrectMatches: - str << correctMatchesPercent * 100 << tab; - break; - - case StatisticsElementPercentOfMatches: - str << percentOfMatches * 100 << tab; - break; - - case StatisticsElementPointsCount: - str << totalKeypoints << tab; - break; - - case StatisticsElementMeanDistance: - str << meanDistance << tab; - break; - - case StatisticsElementMatchingRatio: - str << (correctMatchesPercent * percentOfMatches * 100) << tab; - break; - - default: - str << null << tab; - break; + case StatisticsElementPercentOfCorrectMatches: + str << correctMatchesPercent * 100 << tab; + break; + + case StatisticsElementPercentOfMatches: + str << percentOfMatches * 100 << tab; + break; + + case StatisticsElementPointsCount: + str << totalKeypoints << tab; + break; + + case StatisticsElementMeanDistance: + str << meanDistance << tab; + break; + + case StatisticsElementMatchingRatio: + str << (correctMatchesPercent * percentOfMatches * 100) << tab; + break; + + case StatisticsElementHomographyError: + str << homographyError << tab; + + default: + str << null << tab; + break; }; } else { str << null << tab; } - + return str; } @@ -84,99 +88,99 @@ SingleRunStatistics& CollectedStatistics::getStatistics(std::string algorithmNam CollectedStatistics::OuterGroup CollectedStatistics::groupByAlgorithmThenByTransformation() const { OuterGroup result; - + for (std::map::const_iterator i = m_allStats.begin(); i != m_allStats.end(); ++i) { result[i->first.first][i->first.second] = &(i->second); } - + return result; } CollectedStatistics::OuterGroupLine CollectedStatistics::groupByTransformationThenByAlgorithm() const { OuterGroup result; - + for (std::map::const_iterator i = m_allStats.begin(); i != m_allStats.end(); ++i) { result[i->first.second][i->first.first] = &(i->second); } - + OuterGroupLine line; - + for (OuterGroup::const_iterator tIter = result.begin(); tIter != result.end(); ++tIter) { std::string transformationName = tIter->first; const CollectedStatistics::InnerGroup& inner = tIter->second; - + GroupedByArgument& lineStat = line[transformationName]; - + std::vector statitics; - + for (CollectedStatistics::InnerGroup::const_iterator algIter = inner.begin(); algIter != inner.end(); ++algIter) { std::string algName = algIter->first; - + lineStat.algorithms.push_back(algName); statitics.push_back(algIter->second); } - + const SingleRunStatistics& firstStat = *statitics.front(); int argumentsCount = firstStat.size(); - + for (int i=0; i < argumentsCount; i++) { Line l; l.argument = firstStat[i].argumentValue; - + for (int algIndex = 0; algIndex < statitics.size(); algIndex++) { const SingleRunStatistics& s = *statitics[algIndex]; - + l.stats.push_back(&s[i]); } - + lineStat.lines.push_back(l); } } - - + + return line; } std::ostream& CollectedStatistics::printStatistics(std::ostream& str, StatisticElement elem) const { CollectedStatistics::OuterGroupLine report = groupByTransformationThenByAlgorithm(); - + for (CollectedStatistics::OuterGroupLine::const_iterator tIter = report.begin(); tIter != report.end(); ++tIter) { std::string transformationName = tIter->first; str << quote(transformationName) << std::endl; - + const GroupedByArgument& inner = tIter->second; - + str << "Argument" << tab; for (size_t i=0; i timePerFrames; std::vector timePerKeyPoint; - + for (CollectedStatistics::InnerGroup::const_iterator tIter = alg->second.begin(); tIter != alg->second.end(); ++tIter) { const SingleRunStatistics& runStatistics = *tIter->second; @@ -206,14 +210,14 @@ std::ostream& CollectedStatistics::printPerformanceStatistics(std::ostream& str) } } } - + double avgPerFrame = std::accumulate(timePerFrames.begin(), timePerFrames.end(), 0.0) / timePerFrames.size(); double avgPerKeyPoint = std::accumulate(timePerKeyPoint.begin(), timePerKeyPoint.end(), 0.0) / timePerKeyPoint.size(); - + str << quote(alg->first) << tab - << avgPerFrame << tab - << avgPerKeyPoint << std::endl; + << avgPerFrame << tab + << avgPerKeyPoint << std::endl; } - + return str << std::endl; } diff --git a/CollectedStatistics.hpp b/CollectedStatistics.hpp index af33020..79dad2e 100644 --- a/CollectedStatistics.hpp +++ b/CollectedStatistics.hpp @@ -13,6 +13,7 @@ typedef enum StatisticsElementPercentOfMatches, StatisticsElementMeanDistance, StatisticsElementMatchingRatio, + StatisticsElementHomographyError, } StatisticElement; @@ -28,6 +29,7 @@ struct FrameMatchingStatistics float meanDistance; float stdDevDistance; float correctMatchesPercent; + float homographyError; double consumedTimeMs; bool isValid; diff --git a/ImageTransformation.cpp b/ImageTransformation.cpp index a20de1b..ade8c08 100644 --- a/ImageTransformation.cpp +++ b/ImageTransformation.cpp @@ -9,9 +9,14 @@ bool ImageTransformation::canTransformKeypoints() const void ImageTransformation::transform(float t, const Keypoints& source, Keypoints& result) const { - } +cv::Mat ImageTransformation::getHomography(float t, const cv::Mat& source) const +{ + return cv::Mat::eye(3, 3, CV_64FC1); +} + + ImageTransformation::~ImageTransformation() { } @@ -20,19 +25,19 @@ bool ImageTransformation::findHomography( const Keypoints& source, const Keypoin { if (input.size() < 8) return false; - + std::vector srcPoints, dstPoints; const int pointsCount = input.size(); - + for (int i=0; i status; - cv::findHomography(srcPoints, dstPoints, CV_FM_RANSAC, 3, status); - + homography = cv::findHomography(srcPoints, dstPoints, CV_FM_RANSAC, 3, status); + inliers.clear(); for (int i=0; i ImageScalingTransformation::getX() const { - return m_args; + return m_args; } void ImageScalingTransformation::transform(float t, const cv::Mat& source, cv::Mat& result)const diff --git a/ImageTransformation.hpp b/ImageTransformation.hpp index 81e8a1f..2598f44 100644 --- a/ImageTransformation.hpp +++ b/ImageTransformation.hpp @@ -22,6 +22,9 @@ class ImageTransformation virtual ~ImageTransformation(); static bool findHomography( const Keypoints& source, const Keypoints& result, const Matches& input, Matches& inliers, cv::Mat& homography); + + virtual cv::Mat getHomography(float t, const cv::Mat& source) const; + protected: ImageTransformation(const std::string& transformationName) @@ -40,6 +43,8 @@ class ImageRotationTransformation : public ImageTransformation virtual void transform(float t, const cv::Mat& source, cv::Mat& result)const ; + virtual cv::Mat getHomography(float t, const cv::Mat& source) const; + private: float m_startAngleInDeg; float m_endAngleInDeg; diff --git a/main.cpp b/main.cpp index 313c4d0..363b402 100644 --- a/main.cpp +++ b/main.cpp @@ -15,14 +15,16 @@ const bool USE_VERBOSE_TRANSFORMATIONS = false; int main(int argc, const char* argv[]) { // Print OpenCV build info: - std::cout << cv::getBuildInformation() << std::endl; - return 0; + //std::cout << cv::getBuildInformation() << std::endl; + //return 0; std::vector algorithms; std::vector > transformations; bool useCrossCheck = true; // Initialize list of algorithm tuples: + + /* algorithms.push_back(FeatureAlgorithm("BRISK/BRISK/BF", new cv::BriskFeatureDetector(60,4), new cv::BriskDescriptorExtractor(), @@ -32,42 +34,57 @@ int main(int argc, const char* argv[]) new cv::ORB(), new cv::ORB(), new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + */ - algorithms.push_back(FeatureAlgorithm("SURF/BRISK/BF", + algorithms.push_back(FeatureAlgorithm("SURF/BRISK(1)/BF", new cv::SurfFeatureDetector(), new cv::BriskDescriptorExtractor(), new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + + algorithms.push_back(FeatureAlgorithm("SURF/BRISK(2)/BF", + new cv::SurfFeatureDetector(), + new cv::BriskDescriptorExtractor(false, false), + new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + /* algorithms.push_back(FeatureAlgorithm("SURF/FREAK/BF", new cv::SurfFeatureDetector(), new cv::FREAK(), new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + + algorithms.push_back(FeatureAlgorithm("ORB/FREAK/BF", + new cv::OrbFeatureDetector(), + new cv::FREAK(), + new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + algorithms.push_back(FeatureAlgorithm("ORB3/ORB3/BF", new cv::ORB(500, 1.2f, 8,31, 0, 3), new cv::ORB(500, 1.2f, 8,31, 0, 3), new cv::BFMatcher(cv::NORM_HAMMING2, useCrossCheck))); - + */ + + /* algorithms.push_back(FeatureAlgorithm("ORB4/ORB4/BF", new cv::ORB(500, 1.2f, 8, 31, 0, 4), new cv::ORB(500, 1.2f, 8, 31, 0, 4), new cv::BFMatcher(cv::NORM_HAMMING2, useCrossCheck))); - + */ + + /* algorithms.push_back(FeatureAlgorithm("FAST/BRIEF/BF", new cv::FastFeatureDetector(50), new cv::BriefDescriptorExtractor(), new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); + */ - algorithms.push_back(FeatureAlgorithm("ORB/FREAK/BF", - new cv::OrbFeatureDetector(), - new cv::FREAK(), - new cv::BFMatcher(cv::NORM_HAMMING, useCrossCheck))); - + /* algorithms.push_back(FeatureAlgorithm("SURF/SURF/BF", new cv::SurfFeatureDetector(), new cv::SurfDescriptorExtractor(), new cv::BFMatcher(cv::NORM_L2, useCrossCheck))); - + */ + algorithms.push_back(FeatureAlgorithm("SURF/SURF/FLANN", new cv::SurfFeatureDetector(), new cv::SurfDescriptorExtractor(), @@ -124,11 +141,23 @@ int main(int argc, const char* argv[]) std::cout << "done." << std::endl; } - fullStat.printPerformanceStatistics(std::ofstream("Performance.txt")); - fullStat.printStatistics(std::ofstream("MatchingRatio.txt"), StatisticsElementMatchingRatio); - fullStat.printStatistics(std::ofstream("PercentOfMatches.txt"), StatisticsElementPercentOfMatches); - fullStat.printStatistics(std::ofstream("PercentOfCorrectMatches.txt"), StatisticsElementPercentOfCorrectMatches); - fullStat.printStatistics(std::ofstream("MeanDistance.txt"), StatisticsElementMeanDistance); + std::ofstream performanceStr("Performance.txt"); + fullStat.printPerformanceStatistics(performanceStr); + + std::ofstream matchingRatioStr("MatchingRatio.txt"); + fullStat.printStatistics(matchingRatioStr, StatisticsElementMatchingRatio); + + std::ofstream percentOfMatchesStr("PercentOfMatches.txt") ; + fullStat.printStatistics(percentOfMatchesStr, StatisticsElementPercentOfMatches); + + std::ofstream percentOfCorrectMatchesStr("PercentOfCorrectMatches.txt"); + fullStat.printStatistics(percentOfCorrectMatchesStr, StatisticsElementPercentOfCorrectMatches); + + std::ofstream meanDistanceStr("MeanDistance.txt"); + fullStat.printStatistics(meanDistanceStr, StatisticsElementMeanDistance); + + std::ofstream homographyErrorStr("HomographyError.txt"); + fullStat.printStatistics(homographyErrorStr, StatisticsElementHomographyError); } return 0;