MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionOp.hpp
Go to the documentation of this file.
1#pragma once
2
4
5/**
6 * @file VisionOp.hpp
7 * @brief Declarative description of a Kinesis::Vision processing sequence.
8 *
9 * VisionStep names one operation and carries the parameters needed to invoke
10 * it. VisionSequence is an ordered list of steps. Both are pure value types
11 * with no MayaFlux dependencies.
12 *
13 * Processors (VisionDataProcessor, VisionBufferProcessor) accept a
14 * VisionSequence at construction and execute it each cycle without knowing
15 * which specific algorithms are involved. No per-algorithm processor subclass
16 * is needed.
17 */
18
20
21/**
22 * @enum VisionOp
23 * @brief Named operations available in a VisionSequence.
24 *
25 * Each enumerator maps 1:1 to a function in Kinesis::Vision.
26 * The processor dispatches on this enum; parameters are carried
27 * by the corresponding VisionStep variant field.
28 */
64
65// ============================================================================
66// Per-op parameter structs
67// ============================================================================
68
70 float value;
71};
73 uint32_t block_size;
74 float offset;
75};
77 float lo;
78 float hi;
79};
81 float sigma;
82};
83
85 std::vector<float> kernel_x;
86 std::vector<float> kernel_y;
87};
88
90 float sigma;
93};
94
96 uint32_t radius;
97};
98
100 float k = 0.04F;
101 float sigma = 1.0F;
102};
103
106 uint32_t nms_radius;
107};
108
110 uint32_t window_radius = 7;
111 uint32_t max_iterations = 20;
112 float eigen_threshold = 1e-4F;
113 float error_threshold = 0.3F;
114};
115
117 bool export_labels { false };
118 bool with_colors { false };
119};
120
122 float min_area { 0.0F };
123 uint32_t max_contours { 0 };
125 bool as_image { false };
126};
127
128/**
129 * @brief Parameter variant covering all ops that carry parameters.
130 *
131 * Ops with no parameters (RgbaToGray, Sobel, Scharr, ThresholdOtsu,
132 * NormalizeInplace, GrayToRgba, RgbaToHsv, MorphGradient, Erode, Dilate,
133 * Open, Close) use std::monostate.
134 */
135using VisionParams = std::variant<
136 std::monostate,
149
150/**
151 * @brief One step in a VisionSequence: an op and its parameters.
152 */
155 VisionParams params { std::monostate {} };
156};
157
158/**
159 * @brief Ordered sequence of VisionSteps describing a complete vision pipeline.
160 *
161 * Constructed via the fluent VisionSequence::Builder.
162 */
164 std::vector<VisionStep> steps;
165 bool tracks_keypoints { false };
166 bool track_follows_peaks { false };
167 bool contours_follow_cc { false };
168
169 /**
170 * @brief Fluent builder for VisionSequence.
171 *
172 * Each method appends one step and returns *this for chaining.
173 * Call build() to produce the final VisionSequence.
174 *
175 * @code
176 * auto seq = VisionSequence::Builder{}
177 * .rgba_to_gray()
178 * .gaussian_blur(1.5f)
179 * .threshold(0.4f)
180 * .build();
181 * @endcode
182 */
183 class Builder {
184 public:
186 {
188 }
189
191 {
193 }
194
196 {
198 }
199
204
206 {
208 }
209
215
220
225
227 {
229 NormalizeRangeParams { .lo = lo, .hi = hi });
230 }
231
236
238 std::vector<float> kx, std::vector<float> ky)
239 {
241 FilterSeparableParams { .kernel_x = std::move(kx), .kernel_y = std::move(ky) });
242 }
243
245 {
246 return push(VisionOp::Sobel);
247 }
248
250 {
251 return push(VisionOp::Scharr);
252 }
253
254 Builder& canny(float sigma, float lo, float hi)
255 {
256 return push(VisionOp::Canny, CannyParams { .sigma = sigma, .low_threshold = lo, .high_threshold = hi });
257 }
258
260 {
262 }
263
265 {
267 }
268
269 Builder& open(uint32_t radius)
270 {
272 }
273
275 {
277 }
278
283
284 Builder& harris_response(float k = 0.04F, float sigma = 1.0F)
285 {
286 return push(VisionOp::HarrisResponse, HarrisParams { .k = k, .sigma = sigma });
287 }
288
290 {
292 ExtractPeaksParams { .threshold = threshold, .nms_radius = nms_radius });
293 }
294
295 Builder& connected_components(bool export_labels = false, bool with_colors = false)
296 {
298 ConnectedComponentsParams { .export_labels = export_labels, .with_colors = with_colors });
299 }
300
302 uint32_t window_radius = 7,
303 uint32_t max_iterations = 20,
304 float eigen_threshold = 1e-4F,
305 float error_threshold = 0.3F)
306 {
309 .window_radius = window_radius, .max_iterations = max_iterations, .eigen_threshold = eigen_threshold, .error_threshold = error_threshold });
310 }
311
312 Builder& find_contours(float min_area = 0.0F, uint32_t max_contours = 0, uint32_t max_points_per_contour = 0, bool as_image = false)
313 {
315 FindContoursParams { .min_area = min_area, .max_contours = max_contours, .max_points_per_contour = max_points_per_contour, .as_image = as_image });
316 }
317
319 {
320 return push(VisionOp::Snapshot);
321 }
322
323 [[nodiscard]] VisionSequence build()
324 {
325 VisionSequence seq { .steps = std::move(m_steps) };
326 for (size_t i = 0; i < seq.steps.size(); ++i) {
327 if (seq.steps[i].op == VisionOp::TrackKeypoints)
328 seq.tracks_keypoints = true;
329 if (i + 1 < seq.steps.size()
330 && seq.steps[i].op == VisionOp::ExtractPeaks
331 && seq.steps[i + 1].op == VisionOp::TrackKeypoints)
332 seq.track_follows_peaks = true;
333 if (i + 1 < seq.steps.size()
334 && seq.steps[i].op == VisionOp::ConnectedComponents
335 && seq.steps[i + 1].op == VisionOp::FindContours) {
336 seq.contours_follow_cc = true;
337 std::get<ConnectedComponentsParams>(seq.steps[i].params).export_labels = true;
338 }
339 }
340 return seq;
341 }
342
343 private:
344 std::vector<VisionStep> m_steps;
345
346 Builder& push(VisionOp op, VisionParams p = std::monostate {})
347 {
348 m_steps.push_back({ .op = op, .params = std::move(p) });
349 return *this;
350 }
351 };
352};
353
354/**
355 * @brief Combine a hash into an existing seed, FNV-style.
356 */
357inline void hash_combine(size_t& seed, size_t value)
358{
359 seed ^= value + 0x9e3779b9U + (seed << 6) + (seed >> 2);
360}
361
362/**
363 * @brief Hash a VisionStep's op and parameters together.
364 *
365 * Used to key GPU dispatch memoization within a single VisionGpuExecutor::run()
366 * call, so an op run earlier in a sequence with identical parameters can be
367 * reused rather than redispatched (e.g. Canny reusing an earlier Sobel step).
368 */
369inline size_t hash_vision_step(VisionOp op, const VisionParams& params)
370{
371 size_t seed = std::hash<std::string_view> {}(Reflect::enum_to_string(op));
372
373 std::visit([&seed](const auto& p) {
374 using T = std::decay_t<decltype(p)>;
375 if constexpr (std::is_same_v<T, std::monostate>) {
376 } else if constexpr (std::is_same_v<T, ThresholdParams>) {
377 hash_combine(seed, std::hash<float> {}(p.value));
378 } else if constexpr (std::is_same_v<T, ThresholdAdaptiveParams>) {
379 hash_combine(seed, std::hash<uint32_t> {}(p.block_size));
380 hash_combine(seed, std::hash<float> {}(p.offset));
381 } else if constexpr (std::is_same_v<T, NormalizeRangeParams>) {
382 hash_combine(seed, std::hash<float> {}(p.lo));
383 hash_combine(seed, std::hash<float> {}(p.hi));
384 } else if constexpr (std::is_same_v<T, GaussianBlurParams>) {
385 hash_combine(seed, std::hash<float> {}(p.sigma));
386 } else if constexpr (std::is_same_v<T, FilterSeparableParams>) {
387 for (float v : p.kernel_x)
388 hash_combine(seed, std::hash<float> {}(v));
389 for (float v : p.kernel_y)
390 hash_combine(seed, std::hash<float> {}(v));
391 } else if constexpr (std::is_same_v<T, CannyParams>) {
392 hash_combine(seed, std::hash<float> {}(p.sigma));
393 hash_combine(seed, std::hash<float> {}(p.low_threshold));
394 hash_combine(seed, std::hash<float> {}(p.high_threshold));
395 } else if constexpr (std::is_same_v<T, MorphParams>) {
396 hash_combine(seed, std::hash<uint32_t> {}(p.radius));
397 } else if constexpr (std::is_same_v<T, HarrisParams>) {
398 hash_combine(seed, std::hash<float> {}(p.k));
399 hash_combine(seed, std::hash<float> {}(p.sigma));
400 } else if constexpr (std::is_same_v<T, ExtractPeaksParams>) {
401 hash_combine(seed, std::hash<float> {}(p.threshold));
402 hash_combine(seed, std::hash<uint32_t> {}(p.nms_radius));
403 } else if constexpr (std::is_same_v<T, TrackKeypointsParams>) {
404 hash_combine(seed, std::hash<uint32_t> {}(p.window_radius));
405 hash_combine(seed, std::hash<uint32_t> {}(p.max_iterations));
406 hash_combine(seed, std::hash<float> {}(p.eigen_threshold));
407 hash_combine(seed, std::hash<float> {}(p.error_threshold));
408 } else if constexpr (std::is_same_v<T, FindContoursParams>) {
409 hash_combine(seed, std::hash<float> {}(p.min_area));
410 hash_combine(seed, std::hash<uint32_t> {}(p.max_contours));
411 }
412 },
413 params);
414
415 return seed;
416}
417
418} // namespace MayaFlux::Kinesis::Vision
float value
float lo
float threshold
float offset
uint32_t nms_radius
uint32_t radius
uint32_t max_points_per_contour
uint32_t block_size
float k
float min_area
uint32_t export_labels
float sigma
uint32_t max_contours
float hi
Builder & threshold_adaptive(uint32_t block_size, float offset)
Definition VisionOp.hpp:210
Builder & normalize_range(float lo, float hi)
Definition VisionOp.hpp:226
Builder & track_keypoints(uint32_t window_radius=7, uint32_t max_iterations=20, float eigen_threshold=1e-4F, float error_threshold=0.3F)
Definition VisionOp.hpp:301
Builder & connected_components(bool export_labels=false, bool with_colors=false)
Definition VisionOp.hpp:295
Builder & extract_peaks(float threshold, uint32_t nms_radius)
Definition VisionOp.hpp:289
Builder & canny(float sigma, float lo, float hi)
Definition VisionOp.hpp:254
Builder & push(VisionOp op, VisionParams p=std::monostate {})
Definition VisionOp.hpp:346
Builder & harris_response(float k=0.04F, float sigma=1.0F)
Definition VisionOp.hpp:284
Builder & filter_separable(std::vector< float > kx, std::vector< float > ky)
Definition VisionOp.hpp:237
Builder & find_contours(float min_area=0.0F, uint32_t max_contours=0, uint32_t max_points_per_contour=0, bool as_image=false)
Definition VisionOp.hpp:312
Fluent builder for VisionSequence.
Definition VisionOp.hpp:183
void hash_combine(size_t &seed, size_t value)
Combine a hash into an existing seed, FNV-style.
Definition VisionOp.hpp:357
size_t hash_vision_step(VisionOp op, const VisionParams &params)
Hash a VisionStep's op and parameters together.
Definition VisionOp.hpp:369
std::variant< std::monostate, ThresholdParams, ThresholdAdaptiveParams, NormalizeRangeParams, GaussianBlurParams, FilterSeparableParams, CannyParams, MorphParams, HarrisParams, ExtractPeaksParams, TrackKeypointsParams, ConnectedComponentsParams, FindContoursParams > VisionParams
Parameter variant covering all ops that carry parameters.
Definition VisionOp.hpp:148
VisionOp
Named operations available in a VisionSequence.
Definition VisionOp.hpp:29
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)
Ordered sequence of VisionSteps describing a complete vision pipeline.
Definition VisionOp.hpp:163
One step in a VisionSequence: an op and its parameters.
Definition VisionOp.hpp:153