Skip to content

Commit

Permalink
selectable match confidence for detection/stitching (#66)
Browse files Browse the repository at this point in the history
relation to distance ratio from Lowe's paper: match confidence = 1 - (distance ratio)
  • Loading branch information
krupkat authored Mar 28, 2023
1 parent e5cc14a commit b4cf021
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 8 deletions.
9 changes: 6 additions & 3 deletions xpano/algorithm/algorithm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <opencv2/features2d.hpp>
#include <opencv2/photo.hpp>
#include <opencv2/stitching.hpp>
#include <opencv2/stitching/detail/matchers.hpp>

#include "xpano/algorithm/auto_crop.h"
#include "xpano/algorithm/image.h"
Expand Down Expand Up @@ -85,7 +86,8 @@ std::optional<cv::RotateFlags> GetRotationFlags(ProjectionOptions options) {

} // namespace

std::vector<cv::DMatch> MatchImages(const Image& img1, const Image& img2) {
std::vector<cv::DMatch> MatchImages(const Image& img1, const Image& img2,
float match_conf) {
if (img1.GetKeypoints().empty() || img2.GetKeypoints().empty()) {
return {};
}
Expand All @@ -98,8 +100,7 @@ std::vector<cv::DMatch> MatchImages(const Image& img1, const Image& img2) {
// FILTER BY FIRST/SECOND RATIO
std::vector<cv::DMatch> good_matches;
for (const auto& match : matches) {
double ratio = match[0].distance / match[1].distance;
if (ratio < 0.8) {
if (match[0].distance < (1.0f - match_conf) * match[1].distance) {
good_matches.push_back(match[0]);
}
}
Expand Down Expand Up @@ -187,6 +188,8 @@ StitchResult Stitch(const std::vector<cv::Mat>& images, StitchOptions options,
stitcher->setFeaturesFinder(cv::ORB::create());
break;
}
stitcher->setFeaturesMatcher(cv::makePtr<cv::detail::BestOf2NearestMatcher>(
false, options.match_conf));

cv::Mat pano;
auto status = stitcher->stitch(images, pano);
Expand Down
4 changes: 3 additions & 1 deletion xpano/algorithm/algorithm.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ struct Match {
std::vector<cv::DMatch> matches;
};

std::vector<cv::DMatch> MatchImages(const Image& img1, const Image& img2);
std::vector<cv::DMatch> MatchImages(const Image& img1, const Image& img2,
float match_conf);

std::vector<Pano> FindPanos(const std::vector<Match>& matches,
int match_threshold);
Expand Down Expand Up @@ -76,6 +77,7 @@ struct ProjectionOptions {
struct StitchOptions {
ProjectionOptions projection;
FeatureType feature = FeatureType::kSift;
float match_conf = kDefaultMatchConf;
};

struct StitchResult {
Expand Down
3 changes: 3 additions & 0 deletions xpano/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ constexpr int kStepPreviewLongerSide = 256;

constexpr float kDefaultPaniniA = 2.0f;
constexpr float kDefaultPaniniB = 1.0f;
constexpr float kDefaultMatchConf = 0.25f;
constexpr float kMinMatchConf = 0.1f;
constexpr float kMaxMatchConf = 0.4f;

const std::string kConfigFilename = "config.txt";

Expand Down
37 changes: 35 additions & 2 deletions xpano/gui/panels/sidebar.cc
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,30 @@ void DrawLoadingOptionsMenu(pipeline::LoadingOptions* loading_options) {
}
}

void DrawMatchingOptionsMenu(pipeline::MatchingOptions* matching_options) {
bool DrawMatchConf(float* match_conf) {
bool value_changed = false;
if (ImGui::InputFloat("Match confidence", match_conf, 0.01f, 0.01f, "%.2f")) {
value_changed = true;

if (*match_conf < kMinMatchConf) {
*match_conf = kMinMatchConf;
}

if (*match_conf > kMaxMatchConf) {
*match_conf = kMaxMatchConf;
}
}
ImGui::SameLine();
utils::imgui::InfoMarker(
"(?)",
"Increasing this value will get less matches, but with higher quality."
"\nChanging both ways can be useful in case a panorama fails to detect "
"or stitch.");
return value_changed;
}

void DrawMatchingOptionsMenu(pipeline::MatchingOptions* matching_options,
bool debug_enabled) {
if (ImGui::BeginMenu("Panorama detection")) {
ImGui::Text(
"Experiment with this if the app cannot find the panoramas you "
Expand All @@ -121,6 +144,10 @@ void DrawMatchingOptionsMenu(pipeline::MatchingOptions* matching_options) {
"(?)",
"Number of keypoints that need to match in order to include the two "
"images in a panorama.");
if (debug_enabled) {
ImGui::Text("[debug options]");
DrawMatchConf(&matching_options->match_conf);
}
ImGui::EndMenu();
}
}
Expand Down Expand Up @@ -181,7 +208,12 @@ Action DrawStitchOptionsMenu(pipeline::StitchAlgorithmOptions* stitch_options,
action |= DrawProjectionOptions(stitch_options);

if (debug_enabled) {
ImGui::Text("[debug options]");
action |= DrawFeatureMatchingOptions(stitch_options);

if (DrawMatchConf(&stitch_options->match_conf)) {
action |= {ActionType::kRecomputePano};
}
}
ImGui::EndMenu();
}
Expand All @@ -190,6 +222,7 @@ Action DrawStitchOptionsMenu(pipeline::StitchAlgorithmOptions* stitch_options,

void DrawAutofillOptionsMenu(pipeline::InpaintingOptions* inpaint_options) {
if (ImGui::BeginMenu("Auto fill")) {
ImGui::Text("[debug options]");
ImGui::Text("Algorithm:");
ImGui::Spacing();
if (ImGui::BeginCombo("##inpaint_type", Label(inpaint_options->method))) {
Expand Down Expand Up @@ -224,7 +257,7 @@ Action DrawOptionsMenu(pipeline::CompressionOptions* compression_options,
if (ImGui::BeginMenu("Options")) {
DrawCompressionOptionsMenu(compression_options);
DrawLoadingOptionsMenu(loading_options);
DrawMatchingOptionsMenu(matching_options);
DrawMatchingOptionsMenu(matching_options, debug_enabled);
action |= DrawStitchOptionsMenu(stitch_options, debug_enabled);
if (debug_enabled) {
DrawAutofillOptionsMenu(inpaint_options);
Expand Down
6 changes: 4 additions & 2 deletions xpano/pipeline/stitcher_pipeline.cc
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,10 @@ StitcherData StitcherPipeline::RunMatchingPipeline(
for (int j = 0; j < images.size(); j++) {
for (int i = std::max(0, j - num_neighbors); i < j; i++) {
matches_future.push_back(
pool_.submit([this, i, j, left = images[i], right = images[j]]() {
auto match = algorithm::Match{i, j, MatchImages(left, right)};
pool_.submit([this, i, j, left = images[i], right = images[j],
match_conf = options.match_conf]() {
auto match =
algorithm::Match{i, j, MatchImages(left, right, match_conf)};
progress_.NotifyTaskDone();
return match;
}));
Expand Down
1 change: 1 addition & 0 deletions xpano/pipeline/stitcher_pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct CompressionOptions {
struct MatchingOptions {
int neighborhood_search_size = kDefaultNeighborhoodSearchSize;
int match_threshold = kDefaultMatchThreshold;
float match_conf = kDefaultMatchConf;
};

struct LoadingOptions {
Expand Down

0 comments on commit b4cf021

Please sign in to comment.