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 *
153 * A deferred step submits its GPU work without waiting on the fence. The
154 * run that submits it returns SUSPENDED at that step index; a subsequent
155 * run polls the fence, and once signalled resumes the sequence from the
156 * following step. How many calls that takes is not the executor's concern.
157 */
160 VisionParams params { std::monostate {} };
161 bool deferred { false };
162};
163
164/**
165 * @brief Ordered sequence of VisionSteps describing a complete vision pipeline.
166 *
167 * Constructed via the fluent VisionSequence::Builder.
168 */
170 std::vector<VisionStep> steps;
171
172 /**
173 * @brief Fluent builder for VisionSequence.
174 *
175 * Each method appends one step and returns *this for chaining.
176 * Call build() to produce the final VisionSequence.
177 *
178 * @code
179 * auto seq = VisionSequence::Builder{}
180 * .rgba_to_gray()
181 * .gaussian_blur(1.5f)
182 * .threshold(0.4f)
183 * .build();
184 * @endcode
185 */
186 class Builder {
187 public:
189 {
191 }
192
194 {
196 }
197
199 {
201 }
202
207
209 {
211 }
212
218
223
228
230 {
232 NormalizeRangeParams { .lo = lo, .hi = hi });
233 }
234
239
241 std::vector<float> kx, std::vector<float> ky)
242 {
244 FilterSeparableParams { .kernel_x = std::move(kx), .kernel_y = std::move(ky) });
245 }
246
248 {
249 return push(VisionOp::Sobel);
250 }
251
253 {
254 return push(VisionOp::Scharr);
255 }
256
257 Builder& canny(float sigma, float lo, float hi)
258 {
259 return push(VisionOp::Canny, CannyParams { .sigma = sigma, .low_threshold = lo, .high_threshold = hi });
260 }
261
263 {
265 }
266
268 {
270 }
271
272 Builder& open(uint32_t radius)
273 {
275 }
276
278 {
280 }
281
286
287 Builder& harris_response(float k = 0.04F, float sigma = 1.0F)
288 {
289 return push(VisionOp::HarrisResponse, HarrisParams { .k = k, .sigma = sigma });
290 }
291
293 {
295 ExtractPeaksParams { .threshold = threshold, .nms_radius = nms_radius });
296 }
297
298 Builder& connected_components(bool export_labels = false, bool with_colors = false)
299 {
301 ConnectedComponentsParams { .export_labels = export_labels, .with_colors = with_colors });
302 }
303
305 uint32_t window_radius = 7,
306 uint32_t max_iterations = 20,
307 float eigen_threshold = 1e-4F,
308 float error_threshold = 0.3F)
309 {
312 .window_radius = window_radius, .max_iterations = max_iterations, .eigen_threshold = eigen_threshold, .error_threshold = error_threshold });
313 }
314
315 Builder& find_contours(float min_area = 0.0F, uint32_t max_contours = 0, uint32_t max_points_per_contour = 0, bool as_image = false)
316 {
318 FindContoursParams { .min_area = min_area, .max_contours = max_contours, .max_points_per_contour = max_points_per_contour, .as_image = as_image });
319 }
320
322 {
323 return push(VisionOp::Snapshot);
324 }
325
326 [[nodiscard]] VisionSequence build()
327 {
328 return VisionSequence { .steps = std::move(m_steps) };
329 }
330
331 /**
332 * @brief Mark the most recently pushed step deferred.
333 */
335 {
336 if (!m_steps.empty())
337 m_steps.back().deferred = true;
338 return *this;
339 }
340
341 private:
342 std::vector<VisionStep> m_steps;
343
344 Builder& push(VisionOp op, VisionParams p = std::monostate {})
345 {
346 m_steps.push_back({ .op = op, .params = std::move(p) });
347 return *this;
348 }
349 };
350};
351
352/**
353 * @brief Combine a hash into an existing seed, FNV-style.
354 */
355inline void hash_combine(size_t& seed, size_t value)
356{
357 seed ^= value + 0x9e3779b9U + (seed << 6) + (seed >> 2);
358}
359
360/**
361 * @brief Hash a VisionStep's op and parameters together.
362 *
363 * Keys GPU dispatch memoization on VisionPass::completed, which spans one
364 * walk including any suspensions. Two steps hashing equal are treated as
365 * interchangeable, so every field that changes the output must be hashed.
366 */
367inline size_t hash_vision_step(VisionOp op, const VisionParams& params)
368{
369 size_t seed = std::hash<std::string_view> {}(Reflect::enum_to_string(op));
370
371 std::visit([&seed](const auto& p) {
372 using T = std::decay_t<decltype(p)>;
373 if constexpr (std::is_same_v<T, std::monostate>) {
374 } else if constexpr (std::is_same_v<T, ThresholdParams>) {
375 hash_combine(seed, std::hash<float> {}(p.value));
376 } else if constexpr (std::is_same_v<T, ThresholdAdaptiveParams>) {
377 hash_combine(seed, std::hash<uint32_t> {}(p.block_size));
378 hash_combine(seed, std::hash<float> {}(p.offset));
379 } else if constexpr (std::is_same_v<T, NormalizeRangeParams>) {
380 hash_combine(seed, std::hash<float> {}(p.lo));
381 hash_combine(seed, std::hash<float> {}(p.hi));
382 } else if constexpr (std::is_same_v<T, GaussianBlurParams>) {
383 hash_combine(seed, std::hash<float> {}(p.sigma));
384 } else if constexpr (std::is_same_v<T, FilterSeparableParams>) {
385 for (float v : p.kernel_x)
386 hash_combine(seed, std::hash<float> {}(v));
387 for (float v : p.kernel_y)
388 hash_combine(seed, std::hash<float> {}(v));
389 } else if constexpr (std::is_same_v<T, CannyParams>) {
390 hash_combine(seed, std::hash<float> {}(p.sigma));
391 hash_combine(seed, std::hash<float> {}(p.low_threshold));
392 hash_combine(seed, std::hash<float> {}(p.high_threshold));
393 } else if constexpr (std::is_same_v<T, MorphParams>) {
394 hash_combine(seed, std::hash<uint32_t> {}(p.radius));
395 } else if constexpr (std::is_same_v<T, HarrisParams>) {
396 hash_combine(seed, std::hash<float> {}(p.k));
397 hash_combine(seed, std::hash<float> {}(p.sigma));
398 } else if constexpr (std::is_same_v<T, ExtractPeaksParams>) {
399 hash_combine(seed, std::hash<float> {}(p.threshold));
400 hash_combine(seed, std::hash<uint32_t> {}(p.nms_radius));
401 } else if constexpr (std::is_same_v<T, TrackKeypointsParams>) {
402 hash_combine(seed, std::hash<uint32_t> {}(p.window_radius));
403 hash_combine(seed, std::hash<uint32_t> {}(p.max_iterations));
404 hash_combine(seed, std::hash<float> {}(p.eigen_threshold));
405 hash_combine(seed, std::hash<float> {}(p.error_threshold));
406 } else if constexpr (std::is_same_v<T, FindContoursParams>) {
407 hash_combine(seed, std::hash<float> {}(p.min_area));
408 hash_combine(seed, std::hash<uint32_t> {}(p.max_contours));
409 hash_combine(seed, std::hash<uint32_t> {}(p.max_points_per_contour));
410 hash_combine(seed, std::hash<bool> {}(p.as_image));
411 } else if constexpr (std::is_same_v<T, ConnectedComponentsParams>) {
412 hash_combine(seed, std::hash<bool> {}(p.export_labels));
413 hash_combine(seed, std::hash<bool> {}(p.with_colors));
414 }
415 },
416 params);
417
418 return seed;
419}
420
421/**
422 * @brief True when any step tracks keypoints.
423 *
424 * Whole-sequence, not index-local: the gray-frame capture this gates happens
425 * at RgbaToGray, arbitrarily far ahead of the TrackKeypoints step that needs
426 * it. Evaluate once per run; sequences are short.
427 */
428[[nodiscard]] inline bool tracks_keypoints(const VisionSequence& seq)
429{
430 return std::ranges::any_of(seq.steps,
431 [](const VisionStep& s) { return s.op == VisionOp::TrackKeypoints; });
432}
433
434/**
435 * @brief True when an ExtractPeaks step is immediately followed by TrackKeypoints.
436 *
437 * Whole-sequence for the same reason: the capture gate at RgbaToGray needs the
438 * answer before either step is reached. Ops sitting at the pair itself should
439 * use VisionPass::ahead() instead.
440 */
441[[nodiscard]] inline bool track_follows_peaks(const VisionSequence& seq)
442{
443 for (size_t i = 0; i + 1 < seq.steps.size(); ++i) {
444 if (seq.steps[i].op == VisionOp::ExtractPeaks
445 && seq.steps[i + 1].op == VisionOp::TrackKeypoints)
446 return true;
447 }
448 return false;
449}
450
451} // namespace MayaFlux::Kinesis::Vision
float radius
float value
float lo
float threshold
float offset
uint32_t nms_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:213
Builder & normalize_range(float lo, float hi)
Definition VisionOp.hpp:229
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:304
Builder & connected_components(bool export_labels=false, bool with_colors=false)
Definition VisionOp.hpp:298
Builder & extract_peaks(float threshold, uint32_t nms_radius)
Definition VisionOp.hpp:292
Builder & defer()
Mark the most recently pushed step deferred.
Definition VisionOp.hpp:334
Builder & canny(float sigma, float lo, float hi)
Definition VisionOp.hpp:257
Builder & push(VisionOp op, VisionParams p=std::monostate {})
Definition VisionOp.hpp:344
Builder & harris_response(float k=0.04F, float sigma=1.0F)
Definition VisionOp.hpp:287
Builder & filter_separable(std::vector< float > kx, std::vector< float > ky)
Definition VisionOp.hpp:240
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:315
Fluent builder for VisionSequence.
Definition VisionOp.hpp:186
bool track_follows_peaks(const VisionSequence &seq)
True when an ExtractPeaks step is immediately followed by TrackKeypoints.
Definition VisionOp.hpp:441
void hash_combine(size_t &seed, size_t value)
Combine a hash into an existing seed, FNV-style.
Definition VisionOp.hpp:355
size_t hash_vision_step(VisionOp op, const VisionParams &params)
Hash a VisionStep's op and parameters together.
Definition VisionOp.hpp:367
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
bool tracks_keypoints(const VisionSequence &seq)
True when any step tracks keypoints.
Definition VisionOp.hpp:428
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:169
One step in a VisionSequence: an op and its parameters.
Definition VisionOp.hpp:158