[英]Why the corners detected by Fast algorithm in OPENCV and my own implementation are not same?
I have implemented a naive FAST corner detector using C++ boost algorithm.我已经使用 C++ 增强算法实现了一个简单的 FAST 角检测器。 Currently it does not have non max suppression.
目前它没有非最大抑制。 I ran my implementation and the OPENCV implementation on this photo below
我在下面这张照片上运行了我的实现和 OPENCV 实现
The output of OPENCV is OPENCV 的 output 是
The output from my implementation is我的实现中的 output 是
Here is my code这是我的代码
#include <bits/stdc++.h>
#include <boost/gil.hpp>
#include <boost/gil/extension/io/jpeg.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <boost/gil/image_processing/circle.hpp>
namespace gil = boost::gil;
template <typename T, typename U>
bool FastCornerDetector(const T& buffer, int r, int c, std::vector<U> points,
int t = 20) {
int valid_points_count = 16;
for (auto& u :
points) // checking whether all the 16 points lie within the image
{
if (u[1] + r >= buffer.height() || u[0] + c >= buffer.width()) {
return false;
} else if (u[1] + r < 0 || u[0] + c < 0) {
return false;
} else {
u += gil::point_t(c, r);
}
}
int threshold_indicator[16], index = -1;
memset(threshold_indicator, -1, sizeof(threshold_indicator));
// marking the pixel value of every pixel as >I_p+t or <I_p-t or between
// these two
for (const auto& point : points) {
if (buffer(point) < buffer(gil::point_t(c, r)) - t) {
threshold_indicator[++index] = -1;
} else if (buffer(point) > buffer(gil::point_t(c, r)) + t) {
threshold_indicator[++index] = 1;
} else {
threshold_indicator[++index] = 0;
}
}
bool is_feature_point = false;
// threshold check for n=9 consecutive points. I am not doing the speed test
std::ptrdiff_t count = 0;
for (int i = 0; i < 25; i++) {
if (threshold_indicator[i] == -1) {
if (++count > 8) {
is_feature_point = true;
break;
}
} else
count = 0;
}
if (!is_feature_point) {
count = 0;
for (int i = 0; i < 25; i++) {
if (threshold_indicator[i] == 1) {
if (++count > 8) {
is_feature_point = true;
break;
}
} else
count = 0;
}
}
return is_feature_point;
}
int main() {
const std::ptrdiff_t radius = 3;
const auto rasterizer = gil::midpoint_circle_rasterizer{};
int number_of_points = rasterizer.point_count(radius);
std::vector<gil::point_t> circle_points(number_of_points);
rasterizer(radius, {0, 0}, circle_points.begin());
auto cmp = [](const auto& a, const auto& b) {
return a[0] < b[0] || a[1] < b[1];
};
std::set<gil::point_t, decltype(cmp)> s(cmp);
for (const auto& point : circle_points) {
s.insert(point);
}
// std::cout<<s.size()<<std::endl;
std::vector<gil::point_t> points_clockwise[4];
for (const auto& point : s) {
if (point[1] > 0 && point[0] >= 0) {
points_clockwise[0].push_back(point);
} else if (point[1] <= 0 && point[0] > 0) {
points_clockwise[1].push_back(point);
} else if (point[1] < 0 && point[0] <= 0) {
points_clockwise[2].push_back(point);
} else if (point[1] >= 0 && point[0] < 0) {
points_clockwise[3].push_back(point);
}
}
std::sort(points_clockwise[0].begin(), points_clockwise[0].end(),
[](const auto& a, const auto& b) -> bool {
if (a[1] == b[1])
return a[0] < b[0];
return a[1] > b[1];
});
std::sort(points_clockwise[1].begin(), points_clockwise[1].end(),
[](const auto& a, const auto& b) -> bool {
if (a[1] == b[1])
return a[0] > b[0];
return a[1] > b[1];
});
std::sort(points_clockwise[2].begin(), points_clockwise[2].end(),
[](const auto& a, const auto& b) -> bool {
if (a[1] == b[1])
return a[0] > b[0];
return a[1] < b[1];
});
std::sort(points_clockwise[3].begin(), points_clockwise[3].end(),
[](const auto& a, const auto& b) -> bool {
if (a[1] == b[1])
return a[0] < b[0];
return a[1] < b[1];
});
std::vector<gil::point_t> final_points_clockwise;
// all the 16 points on the circumference of the circle are present in
// clockwise format in final_points_clockwise
final_points_clockwise.insert(final_points_clockwise.end(),
points_clockwise[0].begin(),
points_clockwise[0].end());
final_points_clockwise.insert(final_points_clockwise.end(),
points_clockwise[1].begin(),
points_clockwise[1].end());
final_points_clockwise.insert(final_points_clockwise.end(),
points_clockwise[2].begin(),
points_clockwise[2].end());
final_points_clockwise.insert(final_points_clockwise.end(),
points_clockwise[3].begin(),
points_clockwise[3].end());
gil::gray8_image_t input_image;
gil::rgb8_image_t input_color_image;
gil::rgb8_view_t input_color_image_view;
gil::read_image("box_image.png", input_image, gil::png_tag{});
gil::read_image("box.jpg", input_color_image, gil::jpeg_tag{});
input_color_image_view = gil::view(input_color_image);
auto input_image_view =
gil::color_converted_view<gil::gray8_pixel_t>(gil::view(input_image));
s.clear();
for (int i = 0; i < input_image_view.height(); i++) {
for (int j = 0; j < input_image_view.width(); j++) {
if (FastCornerDetector(input_image_view, i, j,
final_points_clockwise)) {
// if it is a corner draw a circle against it
for (auto u : final_points_clockwise) {
input_color_image_view(u + gil::point_t(j, i)) =
gil::rgb8_pixel_t(0, 255, 0);
}
}
}
}
gil::write_view("Fast_Output.jpeg", input_color_image_view,
gil::jpeg_tag{});
}
I have not used speed test and non max suppression but I do not think that is the reason behind the corners not getting detected.我没有使用速度测试和非最大抑制,但我认为这不是拐角处未被检测到的原因。 The coordinates for the bresenham circle of radius 3 is correct calculated in my code as I verified.
正如我验证的那样,半径为 3 的布雷森汉姆圆的坐标在我的代码中是正确计算的。 I am unable to find the reason then why is the output differing.
我无法找到 output 不同的原因。 Why less corners are detected in my output?
为什么在我的 output 中检测到的角较少? The threshold in both the cases have been set to 20. Please, if anyone can kindly help me...
这两种情况的阈值都设置为 20。如果有人可以帮助我,请...
I can't compile your code.我无法编译你的代码。 I can only spot a few random things, that might help you fix things:
我只能发现一些随机的东西,这可能会帮助您解决问题:
Your threshold_indicator array is sixteen elements, but your loops go to index 24 This will lead to Undefined Behaviour (unless the loop incidentally break
s before that point)您的 threshold_indicator 数组是十六个元素,但是您的循环 go 到索引 24 这将导致未定义的行为(除非循环在该点之前偶然
break
)
valid_points_count
is unused, yet it is being initialized to a magic number valid_points_count
未使用,但正在初始化为幻数
You can reduce a lot of code by not repeating yourself quite as much.您可以通过不重复自己来减少大量代码。 Eg your code for the following bit took 45 lines of code.
例如,以下位的代码需要 45 行代码。 That's >4x as much.
那是 > 4 倍。
std::vector<gil::point_t> final_points_clockwise; for (auto& pc: points_clockwise) { std::sort(pc.begin(), pc.end(), [](const auto& a, const auto& b) -> bool { if (a[1] == b[1]) return a[0] < b[0]; return a[1] > b[1]; }); final_points_clockwise.insert(final_points_clockwise.end(), pc.begin(), pc.end()); } // all the 16 points on the circumference of the circle are present in // clockwise format in final_points_clockwise
In fact, further down it becomes just
事实上,再往下它就变成了
std::vector<gil::point_t> final_cw; for (auto& pc: points_clockwise) { std::sort(pc.begin(), pc.end(), ClockwiseCmp{}); final_cw.insert(final_cw.end(), pc.begin(), pc.end()); }
Similarly, all of同样,所有
std::ptrdiff_t count = 0; for (int i = 0; i < 25; i++) { if (threshold_indicator[i] == -1) { if (++count > 8) { is_feature_point = true; break; } } else count = 0; } if (;is_feature_point) { count = 0; for (int i = 0; i < 25; i++) { if (threshold_indicator[i] == 1) { if (++count > 8) { is_feature_point = true; break; } } else count = 0; } } return is_feature_point;
Can be replaced by可以换成
bool is_feature_point = indicator.end():= std:.search_n(indicator,begin(). indicator,end(), 8. -1) || indicator:end():= std.,search_n(indicator.begin(), indicator,end(); 8, 1);
This at once removes the need for initializing the indicator array and the problem that the loop index goes beyond the indicator size.这立即消除了初始化指标数组的需要以及循环索引超出指标大小的问题。
In fact I'd probably write the entire FastCornerDetector
as事实上,我可能会将整个
FastCornerDetector
写为
template <typename T, typename U> bool FastCornerDetector(T const& buffer, int r, int c, std::vector<U> points, int t = 20) { assert(points.size() == 16); if (std::any_of(points.begin(), points.end(), [&](auto& u) { u += gil::point_t(c, r); return (u[1] >= buffer.height() || u[0] >= buffer.width()) || (u[1] < 0 || u[0] < 0); })) { return false; } // marking every pixel as >I_p+t or <I_p-t auto const I_p = buffer(gil::point_t(c, r)); std::vector<int> indicator; std::transform( points.begin(), points.end(), back_inserter(indicator), [&buffer, low = I_p - t, hi = I_p + t](auto const& point) { if (buffer(point) < low) return -1; else if (buffer(point) > hi) return 1; else return 0; }); bool is_feature_point = indicator.end():= std:.search_n(indicator,begin(). indicator,end(), 8. -1) || indicator:end():= std.,search_n(indicator.begin(), indicator,end(); 8; 1); return is_feature_point; }
That's那是
This comparison looks broken:这个比较看起来很糟糕:
auto cmp = [](const auto& a, const auto& b) { return a[0] < b[0] || a[1] < b[1]; };
I'd expect我期待
auto cmp = [](const auto& a, const auto& b) { if (a[0] < b[0]) return true; if (a[0] == b[0]) return a[1] < b[1]; return false; };
Or, you know, less error prone:或者,你知道,更不容易出错:
auto cmp = [](const auto& a, const auto& b) { return std::tie(a[0], a[1]) < std::tie(b[0], b[1]); };
In fact I'd probably define it out-of-line:事实上,我可能会离线定义它:
And since you don't need to know the number of rasterized points beforehand, and don't ever use the circle_points
vector except to fill the set
, why not just而且由于您不需要事先知道光栅化点的数量,并且除了填充
set
之外不要使用circle_points
向量,为什么不只是
constexpr std::ptrdiff_t radius = 3; auto const rasterizer = gil::midpoint_circle_rasterizer{}; std::set<gil::point_t, PointCmp> s; rasterizer(radius, {0, 0}, inserter(s, s.end()));
Trying to simplify the point separation:试图简化点分离:
std::vector<gil::point_t> points_clockwise[4]; for (const auto& point: s) { auto index = 0; if (false); else if (point[1] > 0 && point[0] >= 0) { index = 0; } else if (point[1] <= 0 && point[0] > 0) { index = 1; } else if (point[1] < 0 && point[0] <= 0) { index = 2; } else if (point[1] >= 0 && point[0] < 0) { index = 3; } points_clockwise[index].push_back(point); }
Makes me question whether the inconsistent(?) boundary handling is on purpose.让我质疑不一致(?)边界处理是否是故意的。 I'm assuming it might be.
我假设它可能是。 My sense of geometry is not very good.
我的几何感不是很好。
The (counter)clockwise sort looks interesting. (逆)顺时针排序看起来很有趣。 How does it work?
它是如何工作的? I've not found any code on the internet that doesn't look at the angles to achieve this.
我没有在互联网上找到任何不考虑实现这一目标的角度的代码。 Regardless, you can at least refactor the code into an equivalent comparator again using the trick from above ( proof of quivalence ):
无论如何,您至少可以使用上面的技巧(等价证明)再次将代码重构为等效的比较器:
struct ClockwiseCmp { bool operator()(gil::point_t const& a, gil::point_t const& b) const { return std::make_tuple(-a[1], a[0]) < std::make_tuple(-b[1], b[0]); } }; std::vector<gil::point_t> final_cw; for (auto& pc: points_clockwise) { std::sort(pc.begin(), pc.end(), ClockwiseCmp{}); final_cw.insert(final_cw.end(), pc.begin(), pc.end()); }
Fully reviewed/refactored listing.全面审查/重构清单。 Note that I wasn't able to run any of the code because of the missing GIL headers.
请注意,由于缺少 GIL 标头,我无法运行任何代码。
"Live" On Compiler Explorer编译器资源管理器上的“实时”
#include <boost/gil.hpp>
#include <boost/gil/extension/io/jpeg.hpp>
#include <boost/gil/extension/io/png.hpp>
#include <set>
#if 0
#include <boost/gil/image_processing/circle.hpp>
#else
namespace boost::gil { // mockups to satisfy the compiler
struct midpoint_circle_rasterizer{
void operator()(ptrdiff_t, gil::point_t, auto iterator) const {}
};
}
#endif
namespace gil = boost::gil;
namespace /*file static*/ {
template <typename T, typename U>
bool FastCornerDetector(T const& buffer, int r, int c,
std::vector<U> points, int t = 20)
{
assert(points.size() == 16);
if (std::any_of(points.begin(), points.end(), [&](auto& u) {
u += gil::point_t(c, r);
return (u[1] >= buffer.height() || u[0] >= buffer.width()) ||
(u[1] < 0 || u[0] < 0);
})) {
return false;
}
// marking every pixel as >I_p+t or <I_p-t
auto const I_p = buffer(gil::point_t(c, r));
std::vector<int> indicator;
std::transform(
points.begin(), points.end(), back_inserter(indicator),
[&buffer, low = I_p - t, hi = I_p + t](auto const& point) {
if (buffer(point) < low) return -1;
else if (buffer(point) > hi) return 1;
else return 0;
});
bool is_feature_point =
indicator.end() != std::search_n(indicator.begin(), indicator.end(), 8, -1) ||
indicator.end() != std::search_n(indicator.begin(), indicator.end(), 8, 1);
return is_feature_point;
}
struct PointCmp {
bool operator()(const gil::point_t& a, const gil::point_t& b) const {
return std::tie(a[0], a[1]) < std::tie(b[0], b[1]);
}
};
struct ClockwiseCmp {
bool operator()(gil::point_t const& a, gil::point_t const& b) const {
return std::make_tuple(-a[1], a[0]) < std::make_tuple(-b[1], b[0]);
}
};
} // namespace
int main() {
constexpr std::ptrdiff_t radius = 3;
auto const rasterizer = gil::midpoint_circle_rasterizer{};
std::set<gil::point_t, PointCmp> s;
rasterizer(radius, {0, 0}, inserter(s, s.end()));
// std::cout<<s.size()<<std::endl;
std::vector<gil::point_t> points_clockwise[4];
#if 0 // old boundary spellings:
for (const auto& point : s) {
auto index = 0;
if (false);
else if (point[1] > 0 && point[0] >= 0) { index = 0; }
else if (point[1] <= 0 && point[0] > 0) { index = 1; }
else if (point[1] < 0 && point[0] <= 0) { index = 2; }
else if (point[1] >= 0 && point[0] < 0) { index = 3; }
points_clockwise[index].push_back(point);
}
#else // unified version (UNTESTED):
for (const auto& point : s) {
bool const a = point[0] < 0;
bool const b = point[1] > 0;
auto index = 0;
if (!a && b) { index = 0; }
else if (!a && !b) { index = 1; }
else if ( a && !b) { index = 2; }
else if ( a && b) { index = 3; }
points_clockwise[index].push_back(point);
}
#endif
std::vector<gil::point_t> final_cw;
for (auto& pc : points_clockwise) {
std::sort(pc.begin(), pc.end(), ClockwiseCmp{});
final_cw.insert(final_cw.end(), pc.begin(), pc.end());
}
// all the 16 points on the circumference of the circle are present in
// clockwise format in final_cw
gil::gray8_image_t input_image;
gil::rgb8_image_t input_color_image;
gil::rgb8_view_t input_color_image_view;
gil::read_image("box_image.png", input_image, gil::png_tag{});
gil::read_image("box.jpg", input_color_image, gil::jpeg_tag{});
input_color_image_view = gil::view(input_color_image);
auto input_image_view =
gil::color_converted_view<gil::gray8_pixel_t>(gil::view(input_image));
for (int i = 0; i < input_image_view.height(); i++) {
for (int j = 0; j < input_image_view.width(); j++) {
if (FastCornerDetector(input_image_view, i, j, final_cw)) {
// if it is a corner draw a circle against it
for (auto& u : final_cw) {
input_color_image_view(u + gil::point_t(j, i)) =
gil::rgb8_pixel_t(0, 255, 0);
}
}
}
}
gil::write_view("Fast_Output.jpeg", input_color_image_view,
gil::jpeg_tag{});
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.