MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Harris.cpp
Go to the documentation of this file.
1#include "Harris.hpp"
2
3#include "Gradient.hpp"
4#include "ImageFilter.hpp"
5
6#include <execution>
7
8#ifdef MAYAFLUX_ARCH_X64
9#include <immintrin.h>
10#endif
11#ifdef MAYAFLUX_ARCH_ARM64
12#include <arm_neon.h>
13#endif
14
16
17namespace {
18
19 void normalize_response(std::span<float> response)
20 {
21 if (response.empty())
22 return;
23
24 const float peak = *std::ranges::max_element(response);
25 if (peak > 1e-6F) {
26 std::transform(std::execution::par_unseq,
27 response.begin(), response.end(), response.begin(),
28 [peak](float v) { return std::clamp(v / peak, 0.0F, 1.0F); });
29 } else {
30 std::ranges::fill(response, 0.0F);
31 }
32 }
33
34}
35
36std::vector<float> harris_response(
37 std::span<const float> gray, uint32_t w, uint32_t h,
38 float k, float sigma)
39{
40 const size_t n = static_cast<size_t>(w) * h;
41
42 auto grad = sobel(gray, w, h);
43
44 std::vector<float> ixx(n), iyy(n), ixy(n);
45
46 std::for_each(std::execution::par_unseq,
47 std::views::iota(size_t { 0 }, n).begin(),
48 std::views::iota(size_t { 0 }, n).end(),
49 [&](size_t i) {
50 const float dx = grad.dx[i];
51 const float dy = grad.dy[i];
52 ixx[i] = dx * dx;
53 iyy[i] = dy * dy;
54 ixy[i] = dx * dy;
55 });
56
57 auto sxx = gaussian_blur(ixx, w, h, sigma);
58 auto syy = gaussian_blur(iyy, w, h, sigma);
59 auto sxy = gaussian_blur(ixy, w, h, sigma);
60
61 std::vector<float> response(n);
62
63 std::for_each(std::execution::par_unseq,
64 std::views::iota(size_t { 0 }, n).begin(),
65 std::views::iota(size_t { 0 }, n).end(),
66 [&](size_t i) {
67 const float det = sxx[i] * syy[i] - sxy[i] * sxy[i];
68 const float trace = sxx[i] + syy[i];
69 response[i] = std::max(0.0F, det - k * trace * trace);
70 });
71
72 normalize_response(response);
73
74 return response;
75}
76
78 std::span<const float> gray,
79 std::span<float> dx, std::span<float> dy, std::span<float> tmp,
80 std::span<float> ixx, std::span<float> iyy, std::span<float> ixy,
81 std::span<float> sxx, std::span<float> syy, std::span<float> sxy,
82 std::span<float> dst,
83 uint32_t w, uint32_t h,
84 float k,
85 std::span<const float> kern)
86{
87 const size_t n = static_cast<size_t>(w) * h;
88
89 sobel(gray, dx, dy, tmp, w, h);
90
91 {
92 const float* dx_ptr = dx.data();
93 const float* dy_ptr = dy.data();
94 float* ixx_ptr = ixx.data();
95 float* iyy_ptr = iyy.data();
96 float* ixy_ptr = ixy.data();
97
98 std::for_each(std::execution::par_unseq,
99 std::views::iota(size_t { 0 }, n).begin(),
100 std::views::iota(size_t { 0 }, n).end(),
101 [&](size_t i) {
102 const float gx = dx_ptr[i];
103 const float gy = dy_ptr[i];
104 ixx_ptr[i] = gx * gx;
105 iyy_ptr[i] = gy * gy;
106 ixy_ptr[i] = gx * gy;
107 });
108 }
109
110 const float* h_src[3] = { ixx.data(), iyy.data(), ixy.data() };
111 float* h_dst[3] = { dx.data(), dy.data(), tmp.data() };
112 filter_horizontal_planes({ h_src, 3 }, { h_dst, 3 }, w, h, kern);
113
114 const float* v_src[3] = { dx.data(), dy.data(), tmp.data() };
115 float* v_dst[3] = { sxx.data(), syy.data(), sxy.data() };
116 filter_vertical_planes({ v_src, 3 }, { v_dst, 3 }, w, h, kern);
117
118 {
119 const float* sxx_ptr = sxx.data();
120 const float* syy_ptr = syy.data();
121 const float* sxy_ptr = sxy.data();
122 float* dst_ptr = dst.data();
123
124#ifdef MAYAFLUX_ARCH_X64
125 const __m256 k_vec = _mm256_set1_ps(k);
126 const __m256 zero = _mm256_setzero_ps();
127
128 size_t i = 0;
129 for (; i + 8 <= n; i += 8) {
130 const __m256 s0 = _mm256_loadu_ps(sxx_ptr + i);
131 const __m256 s1 = _mm256_loadu_ps(syy_ptr + i);
132 const __m256 s01 = _mm256_loadu_ps(sxy_ptr + i);
133
134 const __m256 det = _mm256_sub_ps(
135 _mm256_mul_ps(s0, s1),
136 _mm256_mul_ps(s01, s01));
137 const __m256 trace = _mm256_add_ps(s0, s1);
138 const __m256 resp = _mm256_max_ps(zero,
139 _mm256_fnmadd_ps(k_vec,
140 _mm256_mul_ps(trace, trace),
141 det));
142
143 _mm256_storeu_ps(dst_ptr + i, resp);
144 }
145 for (; i < n; ++i) {
146 const float s0 = sxx_ptr[i];
147 const float s1 = syy_ptr[i];
148 const float s01 = sxy_ptr[i];
149 const float det = s0 * s1 - s01 * s01;
150 const float trace = s0 + s1;
151 dst_ptr[i] = std::max(0.0F, det - k * trace * trace);
152 }
153
154#elif defined(MAYAFLUX_ARCH_ARM64)
155 const float32x4_t k_vec = vdupq_n_f32(k);
156 const float32x4_t zero = vdupq_n_f32(0.0F);
157
158 size_t i = 0;
159 for (; i + 4 <= n; i += 4) {
160 const float32x4_t s0 = vld1q_f32(sxx_ptr + i);
161 const float32x4_t s1 = vld1q_f32(syy_ptr + i);
162 const float32x4_t s01 = vld1q_f32(sxy_ptr + i);
163
164 const float32x4_t det = vsubq_f32(
165 vmulq_f32(s0, s1),
166 vmulq_f32(s01, s01));
167 const float32x4_t trace = vaddq_f32(s0, s1);
168 const float32x4_t resp = vmaxq_f32(zero,
169 vmlsq_f32(det, k_vec, vmulq_f32(trace, trace)));
170
171 vst1q_f32(dst_ptr + i, resp);
172 }
173 for (; i < n; ++i) {
174 const float s0 = sxx_ptr[i];
175 const float s1 = syy_ptr[i];
176 const float s01 = sxy_ptr[i];
177 const float det = s0 * s1 - s01 * s01;
178 const float trace = s0 + s1;
179 dst_ptr[i] = std::max(0.0F, det - k * trace * trace);
180 }
181
182#else
183 for (size_t i = 0; i < n; ++i) {
184 const float s0 = sxx_ptr[i];
185 const float s1 = syy_ptr[i];
186 const float s01 = sxy_ptr[i];
187 const float det = s0 * s1 - s01 * s01;
188 const float trace = s0 + s1;
189 dst_ptr[i] = std::max(0.0F, det - k * trace * trace);
190 }
191#endif
192 }
193
194 normalize_response(dst.first(n));
195}
196
197std::vector<Keypoint> extract_peaks(
198 std::span<const float> response, uint32_t w, uint32_t h,
199 float threshold, uint32_t nms_radius)
200{
201 const auto n = static_cast<size_t>(w) * h;
202 const auto r = static_cast<int32_t>(nms_radius);
203
204 std::vector<Keypoint> keypoints;
205
206 for (uint32_t py = 0; py < h; ++py) {
207 for (uint32_t px = 0; px < w; ++px) {
208 const size_t idx = static_cast<size_t>(py) * w + px;
209 const float val = response[idx];
210
211 if (val < threshold)
212 continue;
213
214 bool is_max = true;
215 for (int32_t dy = -r; dy <= r && is_max; ++dy) {
216 const int32_t ny = static_cast<int32_t>(py) + dy;
217 if (ny < 0 || ny >= static_cast<int32_t>(h))
218 continue;
219 for (int32_t dx = -r; dx <= r && is_max; ++dx) {
220 if (dx == 0 && dy == 0)
221 continue;
222 const int32_t nx = static_cast<int32_t>(px) + dx;
223 if (nx < 0 || nx >= static_cast<int32_t>(w))
224 continue;
225 const size_t ni = static_cast<size_t>(ny) * w + nx;
226 if (response[ni] >= val)
227 is_max = false;
228 }
229 }
230
231 if (!is_max)
232 continue;
233
234 keypoints.push_back({
235 .position = {
236 (static_cast<float>(px) + 0.5F) / static_cast<float>(w),
237 (static_cast<float>(py) + 0.5F) / static_cast<float>(h) },
238 .response = val,
239 .scale = 1.0F,
240 .angle = 0.0F,
241 });
242 }
243 }
244
245 std::ranges::sort(keypoints, [](const Keypoint& a, const Keypoint& b) {
246 return a.response > b.response;
247 });
248
249 return keypoints;
250}
251
252} // namespace MayaFlux::Kinesis::Vision
Gradient and edge detection on normalised float image spans.
Harris corner detection and non-maximum suppression.
2D spatial filtering on normalised float image spans.
uint32_t h
Definition InkPress.cpp:29
uint32_t * dst
size_t a
size_t b
uint32_t py
uint32_t px
float threshold
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.
Definition Analysis.cpp:51
std::vector< Keypoint > extract_peaks(std::span< const float > response, uint32_t w, uint32_t h, float threshold, uint32_t nms_radius)
Extract peaks from a response map via non-maximum suppression.
Definition Harris.cpp:197
std::vector< float > harris_response(std::span< const float > gray, uint32_t w, uint32_t h, float k, float sigma)
Compute the Harris corner response map.
Definition Harris.cpp:36
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.
Definition Gradient.cpp:64
void filter_horizontal_planes(std::span< const float *const > src, std::span< float *const > dst, uint32_t w, uint32_t h, std::span< const float > kernel)
Fused horizontal separable filter over N planes.
std::vector< float > gaussian_blur(std::span< const float > src, uint32_t w, uint32_t h, float sigma)
Separable Gaussian blur.
void filter_vertical_planes(std::span< const float *const > src, std::span< float *const > dst, uint32_t w, uint32_t h, std::span< const float > kernel)
Fused vertical separable filter over N planes.
Single interest point produced by a corner or blob detector.
Definition Features.hpp:77