MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionOp.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Features.hpp"
5
6/**
7 * @file VisionOp.hpp
8 * @brief Declarative description of a Kinesis::Vision processing sequence.
9 *
10 * VisionStep names one operation and carries the parameters needed to invoke
11 * it. VisionSequence is an ordered list of steps. Both are pure value types
12 * with no MayaFlux dependencies.
13 *
14 * Processors (VisionDataProcessor, VisionBufferProcessor) accept a
15 * VisionSequence at construction and execute it each cycle without knowing
16 * which specific algorithms are involved. No per-algorithm processor subclass
17 * is needed.
18 */
19
21
22/**
23 * @enum VisionOp
24 * @brief Named operations available in a VisionSequence.
25 *
26 * Each enumerator maps 1:1 to a function in Kinesis::Vision.
27 * The processor dispatches on this enum; parameters are carried
28 * by the corresponding VisionStep variant field.
29 */
69
70/**
71 * @brief Selects where a VisionSequence executes.
72 *
73 * GPU uses MayaFlux::Yantra::VisionGpuExecutor, the Vulkan implementation in
74 * Yantra. It accepts a GPU image, runs the vision steps through GPU dispatches,
75 * and can keep image results on the device. This is the default because it
76 * suits GPU image pipelines and avoids transferring intermediate images to
77 * the host. Host input may still require an upload, and requesting host data
78 * from a GPU result may require a readback and synchronization. GPU resource
79 * setup and dispatch also have costs, particularly for small workloads.
80 *
81 * CPU uses MayaFlux::Kinesis::Vision::VisionExecutor, the host implementation
82 * in Kinesis. It accepts normalized floating-point pixels in host memory and
83 * produces host-side results. Choose it when the input and desired output are
84 * already on the host, when a GPU is unavailable, or when a sequence needs an
85 * operation implemented only by the CPU executor. Supplying a GPU image to a
86 * CPU pipeline requires a transfer to host memory.
87 *
88 * The selected backend applies to the whole sequence. VisionProcessor and
89 * ImageCVProcessor do not switch backend for individual steps or fall back to
90 * the other executor when an operation is unsupported. The default expresses
91 * the preferred path for GPU image processing, not a guarantee that it will
92 * be faster for every input size or transfer pattern.
93 */
94enum class VisionBackend : uint8_t {
95 GPU, ///< Execute the sequence with the Yantra Vulkan executor.
96 CPU, ///< Execute the sequence with the Kinesis host executor.
97};
98
99// ============================================================================
100// Per-op parameter structs
101// ============================================================================
102
103/**
104 * @brief Selects channels of the current image for a multichannel threshold.
105 *
106 * Ch0, Ch1 and Ch2 are the first three channels as stored: red, green, blue
107 * on an RGBA image, hue, saturation, value after RgbaToHsv. NONE selects the
108 * legacy single-channel path, which reads channel 0 only. Multichannel
109 * selection is implemented by the GPU backend only.
110 */
111enum class ChannelMask : uint8_t {
112 NONE = 0U,
113 Ch0 = 1U << 0U,
114 Ch1 = 1U << 1U,
115 Ch2 = 1U << 2U,
116};
117MF_BITMASK_OPERATORS(ChannelMask)
118
119/**
120 * @brief Inclusive accepted range of one channel. lo greater than hi wraps
121 * around, so a hue band may straddle 0.
122 */
124 float lo { 0.0F };
125 float hi { 1.0F };
126};
127
128/**
129 * @brief Fixed threshold.
130 *
131 * With channels NONE the pixel passes when channel 0 is at least value. With
132 * any channel selected, value is ignored and the pixel passes only when every
133 * selected channel lies inside its band.
134 */
136 float value;
138 std::array<ChannelBand, 3> bands {};
139};
140
141/**
142 * @brief Adaptive threshold. With channels NONE only channel 0 is tested;
143 * otherwise the pixel passes when every selected channel passes its
144 * own neighbourhood test.
145 */
151
152/**
153 * @brief Otsu threshold. With channels NONE only channel 0 is thresholded;
154 * otherwise each selected channel gets its own Otsu threshold and the
155 * pixel passes when every selected channel does.
156 */
161 float lo;
162 float hi;
163};
165 float sigma;
166};
167
169 std::vector<float> kernel_x;
170 std::vector<float> kernel_y;
171};
172
174 float sigma = 1.4F;
175 float low_threshold = 0.1F;
176 float high_threshold = 0.3F;
177};
178
180 uint32_t radius;
181};
182
183/**
184 * @brief Harris response parameters.
185 *
186 * region, a normalised rectangle, zeroes the response outside it, which also
187 * makes the response's peak normalisation relative to the region. Coordinates
188 * and image size stay full-frame. Empty is the whole frame. GPU backend only.
189 */
191 float k = 0.04F;
192 float sigma = 1.0F;
193 std::optional<BoundingBox> region;
194};
195
196/**
197 * @brief Peak extraction parameters.
198 *
199 * region, a normalised rectangle, restricts detection to it while keypoint
200 * coordinates stay full-frame. Empty is the whole frame. GPU backend only.
201 */
204 uint32_t nms_radius;
205 bool export_keypoints { false };
206 std::optional<BoundingBox> region;
207};
208
209/**
210 * @brief Parameters for TrackKeypoints.
211 *
212 * eigen_threshold and error_threshold are normalised by the window area, so
213 * they do not depend on window_radius. levels, max_points and min_distance
214 * apply to the persistent GPU tracker: levels is the pyramid depth including
215 * full resolution, max_points caps live tracks, and min_distance is the grid
216 * cell size in pixels that admits at most one new track. A positive
217 * forward_backward_threshold rejects a track when its backward estimate
218 * misses the source point by more than that many pixels; zero disables it.
219 * host_tracks false skips the host readback entirely (structured stays
220 * empty) when export_tracks is set; it has no effect on the CPU executor,
221 * which always produces the host result.
222 */
224 uint32_t window_radius = 7;
225 uint32_t max_iterations = 20;
226 float eigen_threshold = 1e-4F;
227 float error_threshold = 0.3F;
228 uint32_t levels = 4;
229 uint32_t max_points = 512;
230 float min_distance = 8.0F;
232 bool export_tracks = false;
233 bool host_tracks = true;
234};
235
236/**
237 * @brief Parameters for OpticalFlowDense.
238 *
239 * Dense coarse-to-fine Lucas-Kanade. window_radius is the half size of the
240 * Gaussian weighted window in pixels of the level being solved, iterations the
241 * refinement steps per level, and levels the pyramid depth including full
242 * resolution (lowered for small images). eigen_threshold damps the solve so
243 * untextured pixels take small steps, and max_step caps one increment in
244 * pixels. With visualize set, a hue and brightness rendering of the flow is
245 * delivered in VisionResult::images.flow_visualization, at full brightness
246 * from visual_range pixels. visual_min_motion keeps smaller displacements dark in
247 * the visualization, in pixels of the flow image.
248 */
250 uint32_t window_radius = 5;
251 uint32_t iterations = 3;
252 uint32_t levels = 4;
253 float eigen_threshold = 1e-3F;
254 float max_step = 4.0F;
255 bool visualize = false;
256 float visual_range = 2.0F;
257 float visual_min_motion = 0.0F;
258};
259
260/**
261 * @brief Parameters for Confine.
262 *
263 * bounds is a normalised [0, 1] rectangle anywhere in the current image,
264 * not limited to a found region. resize_to_source false (the default)
265 * leaves the working image at bounds' own pixel size; true nearest-resamples
266 * the confined region back to the frame's own size before this step, e.g.
267 * to keep a fixed working resolution for whatever follows.
268 *
269 * Every later step, and every result coordinate, is in the confined image's
270 * space; the caller remaps to the full frame. In a sequence with
271 * TrackKeypoints or OpticalFlowDense, place Confine before RgbaToGray: the
272 * flow pyramid is built from the gray frame at that point, so a later Confine
273 * is rejected, or with resize_to_source silently mismatches the pyramid. Flow
274 * state resets when the confined size changes, and bounds that move at a
275 * constant size compare frames of different content.
276 */
281
282/**
283 * @brief Connected-component label export and color output.
284 *
285 * CPU always returns component counts and bounding boxes. export_labels also
286 * retains the per-pixel host label_map; otherwise labels remain internal when
287 * the next step extracts contours. with_colors produces a pixel-precision,
288 * opaque RGBA host image in pixel_image, with black background and one color
289 * per foreground label. Subsequent steps may replace that image.
290 *
291 * GPU export_labels requests a device label map and with_colors selects the
292 * device color image in VisionResult::images.component_colors. When
293 * FindContours follows in the same sequence, the box readback is skipped
294 * unless export_boxes or export_label_buffer asks for it, since FindContours
295 * needs neither.
296 *
297 * export_boxes populates VisionResult::component_boxes even when FindContours
298 * follows, for joining a Contour back to its box via Contour::label_id.
299 * export_label_buffer's label map is consumed via
300 * VisionGpuExecutor::select_label(), not decoded to a host list.
301 */
303 bool export_labels { false };
304 bool with_colors { false };
305 bool export_boxes { false };
306 bool export_label_buffer { false };
307};
308
309/**
310 * @brief Contour filtering, trace limits, and output selection.
311 *
312 * FindContours requires ConnectedComponents as the immediately preceding
313 * sequence step on both backends.
314 *
315 * min_area is pixel coverage normalised by image area. A positive
316 * max_contours selects contours by descending area, counting both outer
317 * boundaries and holes. Zero imposes no caller count limit.
318 * max_points_per_contour bounds each trace; zero imposes no caller point
319 * limit. The GPU additionally applies its storage capacities.
320 *
321 * as_image replaces structured contours with white closed boundaries on an
322 * opaque black background. CPU returns host RGBA pixels in pixel_image with
323 * their dimensions; GPU returns a device image in
324 * VisionResult::images.contour_image.
325 */
327 float min_area { 0.0F };
328 uint32_t max_contours { 0 };
330 bool as_image { false };
331
333};
334
335/**
336 * @brief Parameter variant covering all ops that carry parameters.
337 *
338 * Ops with no parameters (RgbaToGray, Sobel, Scharr,
339 * NormalizeInplace, GrayToRgba, RgbaToHsv, MorphGradient, Erode, Dilate,
340 * Open, Close) use std::monostate.
341 */
342using VisionParams = std::variant<
343 std::monostate,
359
360/**
361 * @brief One step in a VisionSequence: an op and its parameters.
362 *
363 * A deferred step submits its GPU work without waiting on the fence. The
364 * run that submits it returns SUSPENDED at that step index; a subsequent
365 * run polls the fence, and once signalled resumes the sequence from the
366 * following step. How many calls that takes is not the executor's concern.
367 */
370 VisionParams params { std::monostate {} };
371 bool deferred { false };
372};
373
374/**
375 * @brief Ordered VisionSteps and the backend chosen to execute them.
376 *
377 * Construct with the fluent VisionSequence::Builder. The backend belongs to
378 * the sequence, so a processor uses the same executor for every step. Directly
379 * constructed sequences default to VisionBackend::GPU.
380 */
382 std::vector<VisionStep> steps;
383 VisionBackend backend { VisionBackend::GPU }; ///< Backend used for every step.
384
385 /**
386 * @brief Fluent builder for VisionSequence.
387 *
388 * Each method appends one step and returns *this for chaining.
389 * Call build() to produce the final VisionSequence.
390 * Parameterized operations accept their parameter struct,
391 * allowing designated initializers in member declaration order. Omitted
392 * settings use the struct's defaults. Operations whose settings have no
393 * defaults require an explicit parameter struct.
394 *
395 * @code
396 * auto seq = VisionSequence::Builder{}
397 * .rgba_to_gray()
398 * .gaussian_blur({ .sigma = 1.5F })
399 * .threshold({ .value = 0.4F })
400 * .build();
401 * auto features = VisionSequence::Builder{}
402 * .rgba_to_gray()
403 * .harris_response({ .sigma = 1.5F })
404 * .extract_peaks({ .threshold = 0.05F, .nms_radius = 6 })
405 * .track_keypoints({ .error_threshold = 0.08F })
406 * .build();
407 * @endcode
408 */
409 class Builder {
410 public:
412 {
414 }
415
417 {
419 }
420
422 {
424 }
425
430
432 {
433 return push(VisionOp::Threshold, params);
434 }
435
440
442 {
443 return push(VisionOp::ThresholdOtsu, params);
444 }
445
450
455
457 {
458 return push(VisionOp::GaussianBlur, params);
459 }
460
462 {
463 return push(VisionOp::FilterSeparable, std::move(params));
464 }
465
467 {
468 return push(VisionOp::Sobel);
469 }
470
472 {
473 return push(VisionOp::Scharr);
474 }
475
477 {
478 return push(VisionOp::Canny, params);
479 }
480
482 {
483 return push(VisionOp::Erode, params);
484 }
485
487 {
488 return push(VisionOp::Dilate, params);
489 }
490
492 {
493 return push(VisionOp::Open, params);
494 }
495
497 {
498 return push(VisionOp::Close, params);
499 }
500
502 {
503 return push(VisionOp::MorphGradient, params);
504 }
505
507 {
508 return push(VisionOp::HarrisResponse, params);
509 }
510
512 {
513 return push(VisionOp::ExtractPeaks, params);
514 }
515
520
522 {
523 return push(VisionOp::TrackKeypoints, params);
524 }
525
527 {
528 return push(VisionOp::FindContours, params);
529 }
530
532 {
533 return push(VisionOp::Snapshot);
534 }
535
540
542 {
543 return push(VisionOp::Confine, params);
544 }
545
546 /**
547 * @brief Finish the sequence and select its execution backend.
548 *
549 * Calling build() selects VisionBackend::GPU for GPU image pipelines.
550 * Pass VisionBackend::CPU to run the entire sequence through the
551 * Kinesis host executor instead. The choice is stored in the returned
552 * VisionSequence and used by VisionProcessor or ImageCVProcessor when
553 * that sequence is processed.
554 *
555 * @code
556 * auto host_sequence = VisionSequence::Builder{}
557 * .rgba_to_gray()
558 * .build(VisionBackend::CPU);
559 * @endcode
560 *
561 * @param backend Backend for all steps. Defaults to the Yantra GPU
562 * executor; no per-step fallback is performed.
563 * @return The completed sequence with its selected backend.
564 */
566 {
567 return VisionSequence { .steps = std::move(m_steps), .backend = backend };
568 }
569
570 /**
571 * @brief Mark the most recently pushed step deferred.
572 */
574 {
575 if (!m_steps.empty())
576 m_steps.back().deferred = true;
577 return *this;
578 }
579
580 private:
581 std::vector<VisionStep> m_steps;
582
583 Builder& push(VisionOp op, VisionParams p = std::monostate {})
584 {
585 m_steps.push_back({ .op = op, .params = std::move(p) });
586 return *this;
587 }
588 };
589};
590
591/**
592 * @brief Combine a hash into an existing seed, FNV-style.
593 */
594inline void hash_combine(size_t& seed, size_t value)
595{
596 seed ^= value + 0x9e3779b9U + (seed << 6) + (seed >> 2);
597}
598
599/**
600 * @brief Combine an optional region into a hash: absence and presence hash
601 * differently, presence by its four bounds.
602 */
603inline void hash_region(size_t& seed, const std::optional<BoundingBox>& region)
604{
605 hash_combine(seed, std::hash<bool> {}(region.has_value()));
606 if (!region)
607 return;
608 hash_combine(seed, std::hash<float> {}(region->x));
609 hash_combine(seed, std::hash<float> {}(region->y));
610 hash_combine(seed, std::hash<float> {}(region->w));
611 hash_combine(seed, std::hash<float> {}(region->h));
612}
613
614/**
615 * @brief Hash a VisionStep's op and parameters together.
616 *
617 * Keys GPU dispatch memoization on VisionPass::completed, which spans one
618 * walk including any suspensions. Two steps hashing equal are treated as
619 * interchangeable, so every field that changes the output must be hashed.
620 */
621inline size_t hash_vision_step(VisionOp op, const VisionParams& params)
622{
623 size_t seed = std::hash<std::string_view> {}(Reflect::enum_to_string(op));
624
625 std::visit([&seed](const auto& p) {
626 using T = std::decay_t<decltype(p)>;
627 if constexpr (std::is_same_v<T, std::monostate>) {
628 } else if constexpr (std::is_same_v<T, ThresholdParams>) {
629 hash_combine(seed, std::hash<float> {}(p.value));
630 hash_combine(seed, std::hash<uint8_t> {}(static_cast<uint8_t>(p.channels)));
631 for (const auto& band : p.bands) {
632 hash_combine(seed, std::hash<float> {}(band.lo));
633 hash_combine(seed, std::hash<float> {}(band.hi));
634 }
635 } else if constexpr (std::is_same_v<T, ThresholdAdaptiveParams>) {
636 hash_combine(seed, std::hash<uint32_t> {}(p.block_size));
637 hash_combine(seed, std::hash<float> {}(p.offset));
638 hash_combine(seed, std::hash<uint8_t> {}(static_cast<uint8_t>(p.channels)));
639 } else if constexpr (std::is_same_v<T, OtsuParams>) {
640 hash_combine(seed, std::hash<uint8_t> {}(static_cast<uint8_t>(p.channels)));
641 } else if constexpr (std::is_same_v<T, NormalizeRangeParams>) {
642 hash_combine(seed, std::hash<float> {}(p.lo));
643 hash_combine(seed, std::hash<float> {}(p.hi));
644 } else if constexpr (std::is_same_v<T, GaussianBlurParams>) {
645 hash_combine(seed, std::hash<float> {}(p.sigma));
646 } else if constexpr (std::is_same_v<T, FilterSeparableParams>) {
647 for (float v : p.kernel_x)
648 hash_combine(seed, std::hash<float> {}(v));
649 for (float v : p.kernel_y)
650 hash_combine(seed, std::hash<float> {}(v));
651 } else if constexpr (std::is_same_v<T, CannyParams>) {
652 hash_combine(seed, std::hash<float> {}(p.sigma));
653 hash_combine(seed, std::hash<float> {}(p.low_threshold));
654 hash_combine(seed, std::hash<float> {}(p.high_threshold));
655 } else if constexpr (std::is_same_v<T, MorphParams>) {
656 hash_combine(seed, std::hash<uint32_t> {}(p.radius));
657 } else if constexpr (std::is_same_v<T, HarrisParams>) {
658 hash_combine(seed, std::hash<float> {}(p.k));
659 hash_combine(seed, std::hash<float> {}(p.sigma));
660 hash_region(seed, p.region);
661 } else if constexpr (std::is_same_v<T, ExtractPeaksParams>) {
662 hash_combine(seed, std::hash<float> {}(p.threshold));
663 hash_combine(seed, std::hash<uint32_t> {}(p.nms_radius));
664 hash_combine(seed, std::hash<bool> {}(p.export_keypoints));
665 hash_region(seed, p.region);
666 } else if constexpr (std::is_same_v<T, TrackKeypointsParams>) {
667 hash_combine(seed, std::hash<uint32_t> {}(p.window_radius));
668 hash_combine(seed, std::hash<uint32_t> {}(p.max_iterations));
669 hash_combine(seed, std::hash<float> {}(p.eigen_threshold));
670 hash_combine(seed, std::hash<float> {}(p.error_threshold));
671 hash_combine(seed, std::hash<uint32_t> {}(p.levels));
672 hash_combine(seed, std::hash<uint32_t> {}(p.max_points));
673 hash_combine(seed, std::hash<float> {}(p.min_distance));
674 hash_combine(seed, std::hash<float> {}(p.forward_backward_threshold));
675 hash_combine(seed, std::hash<bool> {}(p.export_tracks));
676 hash_combine(seed, std::hash<bool> {}(p.host_tracks));
677 } else if constexpr (std::is_same_v<T, FindContoursParams>) {
678 hash_combine(seed, std::hash<float> {}(p.min_area));
679 hash_combine(seed, std::hash<uint32_t> {}(p.max_contours));
680 hash_combine(seed, std::hash<uint32_t> {}(p.max_points_per_contour));
681 hash_combine(seed, std::hash<bool> {}(p.as_image));
682 hash_combine(seed, std::hash<bool> {}(p.export_contours_buffer));
683 } else if constexpr (std::is_same_v<T, ConnectedComponentsParams>) {
684 hash_combine(seed, std::hash<bool> {}(p.export_labels));
685 hash_combine(seed, std::hash<bool> {}(p.with_colors));
686 hash_combine(seed, std::hash<bool> {}(p.export_boxes));
687 hash_combine(seed, std::hash<bool> {}(p.export_label_buffer));
688 } else if constexpr (std::is_same_v<T, OpticalFlowDenseParams>) {
689 hash_combine(seed, std::hash<uint32_t> {}(p.window_radius));
690 hash_combine(seed, std::hash<uint32_t> {}(p.iterations));
691 hash_combine(seed, std::hash<uint32_t> {}(p.levels));
692 hash_combine(seed, std::hash<float> {}(p.eigen_threshold));
693 hash_combine(seed, std::hash<float> {}(p.max_step));
694 hash_combine(seed, std::hash<bool> {}(p.visualize));
695 hash_combine(seed, std::hash<float> {}(p.visual_range));
696 hash_combine(seed, std::hash<float> {}(p.visual_min_motion));
697 } else if constexpr (std::is_same_v<T, ConfineParams>) {
698 hash_combine(seed, std::hash<float> {}(p.bounds.x));
699 hash_combine(seed, std::hash<float> {}(p.bounds.y));
700 hash_combine(seed, std::hash<float> {}(p.bounds.w));
701 hash_combine(seed, std::hash<float> {}(p.bounds.h));
702 hash_combine(seed, std::hash<bool> {}(p.resize_to_source));
703 }
704 },
705 params);
706
707 return seed;
708}
709
710/**
711 * @brief True when any step tracks keypoints.
712 *
713 * Whole-sequence, not index-local: the gray-frame capture this gates happens
714 * at RgbaToGray, arbitrarily far ahead of the TrackKeypoints step that needs
715 * it. Evaluate once per run; sequences are short.
716 */
717[[nodiscard]] inline bool tracks_keypoints(const VisionSequence& seq)
718{
719 return std::ranges::any_of(seq.steps,
720 [](const VisionStep& s) { return s.op == VisionOp::TrackKeypoints; });
721}
722
723/**
724 * @brief True when an ExtractPeaks step is immediately followed by TrackKeypoints.
725 *
726 * Whole-sequence for the same reason: the capture gate at RgbaToGray needs the
727 * answer before either step is reached. Ops sitting at the pair itself should
728 * use VisionPass::ahead() instead.
729 */
730[[nodiscard]] inline bool track_follows_peaks(const VisionSequence& seq)
731{
732 for (size_t i = 0; i + 1 < seq.steps.size(); ++i) {
733 if (seq.steps[i].op == VisionOp::ExtractPeaks
734 && seq.steps[i + 1].op == VisionOp::TrackKeypoints)
735 return true;
736 }
737 return false;
738}
739
740} // namespace MayaFlux::Kinesis::Vision
glm::vec3 value
Builder & extract_peaks(ExtractPeaksParams params)
Definition VisionOp.hpp:511
Builder & connected_components(ConnectedComponentsParams params={})
Definition VisionOp.hpp:516
Builder & threshold(ThresholdParams params)
Definition VisionOp.hpp:431
Builder & harris_response(HarrisParams params={})
Definition VisionOp.hpp:506
Builder & optical_flow_dense(OpticalFlowDenseParams params={})
Definition VisionOp.hpp:536
Builder & gaussian_blur(GaussianBlurParams params)
Definition VisionOp.hpp:456
Builder & defer()
Mark the most recently pushed step deferred.
Definition VisionOp.hpp:573
Builder & normalize_range(NormalizeRangeParams params)
Definition VisionOp.hpp:451
Builder & threshold_otsu(OtsuParams params={})
Definition VisionOp.hpp:441
Builder & find_contours(FindContoursParams params={})
Definition VisionOp.hpp:526
Builder & confine(ConfineParams params)
Definition VisionOp.hpp:541
VisionSequence build(VisionBackend backend=VisionBackend::GPU)
Finish the sequence and select its execution backend.
Definition VisionOp.hpp:565
Builder & push(VisionOp op, VisionParams p=std::monostate {})
Definition VisionOp.hpp:583
Builder & track_keypoints(TrackKeypointsParams params={})
Definition VisionOp.hpp:521
Builder & threshold_adaptive(ThresholdAdaptiveParams params)
Definition VisionOp.hpp:436
Builder & morph_gradient(MorphParams params)
Definition VisionOp.hpp:501
Builder & filter_separable(FilterSeparableParams params)
Definition VisionOp.hpp:461
Fluent builder for VisionSequence.
Definition VisionOp.hpp:409
bool track_follows_peaks(const VisionSequence &seq)
True when an ExtractPeaks step is immediately followed by TrackKeypoints.
Definition VisionOp.hpp:730
VisionBackend
Selects where a VisionSequence executes.
Definition VisionOp.hpp:94
@ CPU
Execute the sequence with the Kinesis host executor.
@ GPU
Execute the sequence with the Yantra Vulkan executor.
void hash_combine(size_t &seed, size_t value)
Combine a hash into an existing seed, FNV-style.
Definition VisionOp.hpp:594
size_t hash_vision_step(VisionOp op, const VisionParams &params)
Hash a VisionStep's op and parameters together.
Definition VisionOp.hpp:621
ChannelMask
Selects channels of the current image for a multichannel threshold.
Definition VisionOp.hpp:111
bool tracks_keypoints(const VisionSequence &seq)
True when any step tracks keypoints.
Definition VisionOp.hpp:717
VisionOp
Named operations available in a VisionSequence.
Definition VisionOp.hpp:30
std::variant< std::monostate, ThresholdParams, ThresholdAdaptiveParams, OtsuParams, NormalizeRangeParams, GaussianBlurParams, FilterSeparableParams, CannyParams, MorphParams, HarrisParams, ExtractPeaksParams, TrackKeypointsParams, ConnectedComponentsParams, FindContoursParams, OpticalFlowDenseParams, ConfineParams > VisionParams
Parameter variant covering all ops that carry parameters.
Definition VisionOp.hpp:358
void hash_region(size_t &seed, const std::optional< BoundingBox > &region)
Combine an optional region into a hash: absence and presence hash differently, presence by its four b...
Definition VisionOp.hpp:603
constexpr std::string_view enum_to_string(EnumType value) noexcept
Universal enum to string converter using magic_enum (original case)
Axis-aligned bounding box in normalised image coordinates.
Definition Features.hpp:19
Inclusive accepted range of one channel.
Definition VisionOp.hpp:123
Connected-component label export and color output.
Definition VisionOp.hpp:302
std::optional< BoundingBox > region
Definition VisionOp.hpp:206
Contour filtering, trace limits, and output selection.
Definition VisionOp.hpp:326
std::optional< BoundingBox > region
Definition VisionOp.hpp:193
Harris response parameters.
Definition VisionOp.hpp:190
std::array< ChannelBand, 3 > bands
Definition VisionOp.hpp:138
VisionBackend backend
Backend used for every step.
Definition VisionOp.hpp:383
Ordered VisionSteps and the backend chosen to execute them.
Definition VisionOp.hpp:381
One step in a VisionSequence: an op and its parameters.
Definition VisionOp.hpp:368