diff --git a/Filter.cpp b/Filter.cpp new file mode 100644 index 0000000..1edea07 --- /dev/null +++ b/Filter.cpp @@ -0,0 +1,52 @@ + +#include "Filter.h" +#include "MatchKD.h" +#include + +/* +* Filters the matches by the distances (->Ratio-test), and by using the A->B algorithm. +* +* @param pairMatches1 - The vector which presents pair of matches between image A to image B. +* @param pairMatches2 - The vector which presents pair of matches between image B to image A. +* +* return the vector of filtered matches according to the ratio-test, +* or empty; if pairMatches was empty or if filtered-matches were not found. +*/ + +vector> filterMatches(vector> pairMatches1, vector> pairMatches2) { + + float matchConf = 0.3f; + vector> matches; + set> tempmatch; + + // Finding filtered matches from A to B + for (size_t i = 0; i < pairMatches1.size(); ++i) + { + // Checking if there are less than 2 matches + if (pairMatches1[i].size() < 2) + continue; + const MatchKD& m0 = pairMatches1[i][0]; + const MatchKD& m1 = pairMatches1[i][1]; + // Ratio-test + if (m0.distance < (1.f - matchConf) * m1.distance) + { + matches.push_back(m0); + tempmatch.insert(std::make_pair(m0.queryIdx, m0.trainIdx)); + } + } + + // Finding filtered matches B to A + for (size_t i = 0; i < pairMatches2.size(); ++i) + { + // Checking if there are less than 2 matches + if (pairMatches2[i].size() < 2) + continue; + const MatchKD& m0 = pairMatches2[i][0]; + const MatchKD& m1 = pairMatches2[i][1]; + // Ratio-test + if (m0.distance < (1.f - matchConf) * m1.distance) + if (tempmatch.find(std::make_pair(m0.trainIdx, m0.queryIdx)) == tempmatch.end()) + matches.push_back(MatchKD(m0.trainIdx, m0.queryIdx, m0.distance)); + } + return matches; +} \ No newline at end of file diff --git a/Filter.h b/Filter.h new file mode 100644 index 0000000..9d34067 --- /dev/null +++ b/Filter.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include "MatchKD.h" + +using namespace std; + +vector> filterMatches(vector> pairMatches1, vector> pairMatches2); \ No newline at end of file diff --git a/FilterTest.cpp b/FilterTest.cpp new file mode 100644 index 0000000..56731a0 --- /dev/null +++ b/FilterTest.cpp @@ -0,0 +1,82 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include "doctest.h" +#include "Filter.h" +#include +#include + +bool areVectorsEqual(const std::vector& vec1, const std::vector& vec2) { + if (vec1.size() != vec2.size()) { + return false; + } + for (size_t i = 0; i < vec1.size(); ++i) { + if (!(vec1[i] == vec2[i])) { + return false; + } + } + return true; +} + +void printMatches(const std::vector& matches) { + for (const auto& match : matches) { + std::cout << "QueryIdx: " << match.queryIdx << ", TrainIdx: " << match.trainIdx << ", Distance: " << match.distance << std::endl; + } +} + +TEST_CASE("testing the filterMatches function when there is a match") { + + vector> pairMatches1 = { + {MatchKD(0, 191, 77.0f), MatchKD(0, 27, 82.0f)}, + {MatchKD(1, 18, 46.0f), MatchKD(1, 46, 69.0f)}, + {MatchKD(2, 163, 98.0f), MatchKD(2, 139, 102.0f)}, + {MatchKD(3, 89, 64.0f), MatchKD(3, 220, 73.0f)}, + }; + + vector> pairMatches2 = { + {MatchKD(0, 321, 70.0f), MatchKD(0, 164, 78.0f)}, + {MatchKD(1, 50, 65.0f), MatchKD(1, 166, 76.0f)}, + {MatchKD(2, 360, 86.0f), MatchKD(2, 48, 88.0f)}, + {MatchKD(3, 107, 79.0f), MatchKD(3, 196, 85.0f)}, + }; + + vector expectedMatches = { + MatchKD(1, 18, 46.0f) + }; + + vector actualMatches = filterMatches(pairMatches1, pairMatches2); + + std::cout << "Tesing filterMatches when matches found: " << actualMatches.size() << " matches" << std::endl; + printMatches(actualMatches); + CHECK(areVectorsEqual(actualMatches, expectedMatches)); + +} + +TEST_CASE("Testing empty inputs") { + vector> empty1, empty2; + vector expectedMatches; + vector actualMatches = filterMatches(empty1, empty2); + + std::cout << "Testing empty inputs - Expected: 0 matches, Actual: " << actualMatches.size() << " matches" << std::endl; + CHECK(areVectorsEqual(actualMatches, expectedMatches)); +} + +TEST_CASE("Tesing filterMatches when filtered not found") +{ + std::vector> pairMatches1 = { + {MatchKD(0, 191, 77.0f), MatchKD(0, 27, 82.0f)}, + {MatchKD(2, 163, 98.0f), MatchKD(2, 139, 102.0f)}, + {MatchKD(3, 89, 64.0f), MatchKD(3, 220, 73.0f)}, + }; + + std::vector> pairMatches2 = { + {MatchKD(0, 191, 77.0f), MatchKD(0, 27, 82.0f)}, + {MatchKD(2, 163, 98.0f), MatchKD(2, 139, 102.0f)}, + {MatchKD(3, 89, 64.0f), MatchKD(3, 220, 73.0f)}, + }; + + vector actualMatches = filterMatches(pairMatches1, pairMatches2); + vector expectedMatches; + + std::cout << "Tesing filterMatches when filtered not found: " << actualMatches.size() << " matches" << std::endl; + CHECK(areVectorsEqual(actualMatches, expectedMatches)); +} + diff --git a/MatchKD.cpp b/MatchKD.cpp new file mode 100644 index 0000000..5cf547f --- /dev/null +++ b/MatchKD.cpp @@ -0,0 +1 @@ +#include "MatchKD.h" diff --git a/MatchKD.h b/MatchKD.h new file mode 100644 index 0000000..2a95267 --- /dev/null +++ b/MatchKD.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +using namespace std; + +//Class MatchKD to create an object that represents a match between 2 features of 2 images +class MatchKD { +public: + int queryIdx; + int trainIdx; + float distance; + + MatchKD() : queryIdx(-1), trainIdx(-1), distance(FLT_MAX) {} + + MatchKD(int qIdx, int tIdx, float dist) : queryIdx(qIdx), trainIdx(tIdx), distance(dist) {} + + bool operator==(const MatchKD& other) const { + return queryIdx == other.queryIdx && trainIdx == other.trainIdx && distance == other.distance; + } + +}; \ No newline at end of file