16 GradientResult gradient_from_kernels(
17 std::span<const float> gray, uint32_t w, uint32_t
h,
18 std::span<const float> kx, std::span<const float> ky)
20 const size_t n =
static_cast<size_t>(w) *
h;
22 static constexpr float smooth[] = { 1.0F, 2.0F, 1.0F };
26 std::vector<float> mag(n);
27 std::vector<float> ang(n);
31 std::for_each(std::execution::par_unseq,
32 std::views::iota(
size_t { 0 }, n).begin(),
33 std::views::iota(
size_t { 0 }, n).end(),
35 mag[i] = std::sqrt(dx[i] * dx[i] + dy[i] * dy[i]);
36 ang[i] = std::atan2(dy[i], dx[i]);
39 const float peak = *std::ranges::max_element(mag);
41 const float inv = 1.0F /
peak;
42 std::transform(std::execution::par_unseq, mag.begin(), mag.end(), mag.begin(),
43 [inv](
float v) { return v * inv; });
46 return { .dx = std::move(dx), .dy = std::move(dy), .magnitude = std::move(mag), .angle = std::move(ang) };
49 void gradient_into_buffers(
50 std::span<const float> gray,
54 uint32_t w, uint32_t
h,
55 std::span<const float> kx,
56 std::span<const float> ky)
65 std::span<const float> gray,
66 std::span<float> dx, std::span<float> dy, std::span<float> tmp,
67 uint32_t w, uint32_t
h)
74 const size_t n =
static_cast<size_t>(w) *
h;
78 std::vector<float> tmp(n);
81 const auto en =
static_cast<Eigen::Index
>(n);
82 auto dx = Eigen::Map<const Eigen::ArrayXf>(r.
dx.data(), en);
83 auto dy = Eigen::Map<const Eigen::ArrayXf>(r.
dy.data(), en);
87 Eigen::Map<Eigen::ArrayXf>(r.
magnitude.data(), en) = (dx.square() + dy.square()).sqrt();
88 const float peak = Eigen::Map<const Eigen::ArrayXf>(r.
magnitude.data(), en).maxCoeff();
90 Eigen::Map<Eigen::ArrayXf>(r.
magnitude.data(), en) /= peak;
92 std::transform(std::execution::par_unseq, r.
dx.begin(), r.
dx.end(), r.
dy.begin(), r.
angle.begin(),
93 [](
float gx,
float gy) { return std::atan2(gy, gx); });
98 std::span<const float> gray,
99 std::span<float> dx, std::span<float> dy, std::span<float> tmp,
100 uint32_t w, uint32_t
h)
107 const size_t n =
static_cast<size_t>(w) *
h;
111 std::vector<float> tmp(n);
114 const auto en =
static_cast<Eigen::Index
>(n);
115 auto dx = Eigen::Map<const Eigen::ArrayXf>(r.
dx.data(), en);
116 auto dy = Eigen::Map<const Eigen::ArrayXf>(r.
dy.data(), en);
120 Eigen::Map<Eigen::ArrayXf>(r.
magnitude.data(), en) = (dx.square() + dy.square()).sqrt();
121 const float peak = Eigen::Map<const Eigen::ArrayXf>(r.
magnitude.data(), en).maxCoeff();
123 Eigen::Map<Eigen::ArrayXf>(r.
magnitude.data(), en) /= peak;
125 std::transform(std::execution::par_unseq, r.
dx.begin(), r.
dx.end(), r.
dy.begin(), r.
angle.begin(),
126 [](
float gx,
float gy) { return std::atan2(gy, gx); });
131 std::span<const float> gray, uint32_t w, uint32_t
h,
132 float sigma,
float low_threshold,
float high_threshold)
134 const size_t n =
static_cast<size_t>(w) *
h;
136 std::vector<float> blurred;
137 std::span<const float>
src = gray;
145 std::vector<float> nms(n, 0.0F);
146 std::for_each(std::execution::par_unseq,
147 std::views::iota(
size_t { 0 }, n).begin(),
148 std::views::iota(
size_t { 0 }, n).end(),
150 const auto px =
static_cast<int32_t
>(idx % w);
151 const auto py =
static_cast<int32_t
>(idx / w);
153 if (
px == 0 ||
py == 0
154 ||
px ==
static_cast<int32_t
>(w) - 1
155 ||
py ==
static_cast<int32_t
>(
h) - 1)
158 const float angle = grad.angle[idx];
159 const float pi = std::numbers::pi_v<float>;
161 float norm_angle = angle < 0.0F ? angle + pi : angle;
162 norm_angle = norm_angle / pi * 4.0F;
164 int32_t ox = 0, oy = 0;
165 if (norm_angle < 0.5F || norm_angle >= 3.5F) {
167 }
else if (norm_angle < 1.5F) {
170 }
else if (norm_angle < 2.5F) {
177 const float m = grad.magnitude[idx];
178 const float m_pos = grad.magnitude[
static_cast<size_t>(
py + oy) * w +
static_cast<size_t>(
px + ox)];
179 const float m_neg = grad.magnitude[
static_cast<size_t>(
py - oy) * w +
static_cast<size_t>(
px - ox)];
181 if (m >= m_pos && m >= m_neg)
185 std::vector<float>
out(n, 0.0F);
186 std::vector<uint8_t> strong(n, 0);
187 std::vector<uint8_t> weak(n, 0);
189 for (
size_t i = 0; i < n; ++i) {
190 if (nms[i] >= high_threshold) {
192 }
else if (nms[i] >= low_threshold) {
197 std::vector<size_t>
stack;
198 for (
size_t i = 0; i < n; ++i) {
203 while (!
stack.empty()) {
204 const size_t idx =
stack.back();
208 const auto px =
static_cast<int32_t
>(idx % w);
209 const auto py =
static_cast<int32_t
>(idx / w);
211 for (int32_t dy = -1; dy <= 1; ++dy) {
212 for (int32_t dx = -1; dx <= 1; ++dx) {
213 if (dx == 0 && dy == 0)
215 const int32_t nx =
px + dx;
216 const int32_t ny =
py + dy;
218 || nx >=
static_cast<int32_t
>(w)
219 || ny >=
static_cast<int32_t
>(
h))
221 const size_t ni =
static_cast<size_t>(ny) * w + nx;
222 if (weak[ni] &&
out[ni] == 0.0F)
232 std::span<const float> gray,
233 std::span<float>
dst,
234 uint32_t w, uint32_t
h,
235 float sigma,
float low_threshold,
float high_threshold)
237 auto result =
canny(gray, w,
h, sigma, low_threshold, high_threshold);
238 std::ranges::copy(result,
dst.begin());
std::shared_ptr< PlacementStack > stack
Gradient and edge detection on normalised float image spans.
2D spatial filtering on normalised float image spans.
Compile-time separable filter kernel definition and named kernel bank.
std::vector< float > * out
std::vector< double > peak(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Peak amplitude per window.
constexpr auto sobel_smooth
std::vector< float > filter_separable(std::span< const float > src, uint32_t w, uint32_t h, std::span< const float > kernel_x, std::span< const float > kernel_y)
Apply a separable 2D filter via two 1D passes.
std::vector< float > canny(std::span< const float > gray, uint32_t w, uint32_t h, float sigma, float low_threshold, float high_threshold)
Canny edge detector.
void sobel(std::span< const float > gray, std::span< float > dx, std::span< float > dy, std::span< float > tmp, uint32_t w, uint32_t h)
Sobel gradient writing dx and dy into caller-supplied buffers.
void scharr(std::span< const float > gray, std::span< float > dx, std::span< float > dy, std::span< float > tmp, uint32_t w, uint32_t h)
Scharr gradient writing dx and dy into caller-supplied buffers.
std::vector< float > gaussian_blur(std::span< const float > src, uint32_t w, uint32_t h, float sigma)
Separable Gaussian blur.
std::vector< float > angle
Gradient angle in radians, [-pi, pi].
std::vector< float > magnitude
Gradient magnitude, normalised to [0, 1].
std::vector< float > dx
Horizontal gradient component.
std::vector< float > dy
Vertical gradient component.
Gradient maps produced by Sobel and Scharr operators.