MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionContext.hpp
Go to the documentation of this file.
1#pragma once
2
4#include "Features.hpp"
5#include "Gradient.hpp"
6#include "OpticalFlow.hpp"
7#include "VisionOp.hpp"
8
10
11namespace MayaFlux::Core {
12class VKImage;
13}
14
16struct GpuBufferHandle;
17}
19
20using StructuredOutput = std::variant<
21 std::monostate,
24 std::vector<Contour>,
25 std::vector<Keypoint>,
26 std::vector<TrackResult>>;
27
28/**
29 * @brief Whether a run carried the sequence to its end.
30 *
31 * SUSPENDED means a deferred step has work outstanding. The result carries
32 * nothing and must not be consumed. Call run again with the same arguments
33 * to poll; the executor resumes where it left off and ignores the image
34 * argument until the sequence completes.
35 */
36enum class VisionStatus : uint8_t {
39};
40
41/**
42 * @brief Live device view of FindContours' traced contours, when
43 * FindContoursParams::export_contours_buffer is set.
44 *
45 * meta is one uvec4 per contour (point_offset, point_count, parent_label,
46 * label_id), points is the flat point array, area_perim is one vec2
47 * (area, perimeter) per contour. Decode via
48 * VisionGpuExecutor::read_exported_contours(). Default constructed (all
49 * null, count 0) when not requested. Valid for one more run before the
50 * executor reuses it.
51 */
53 uint32_t count { 0 };
54 uint32_t points_written { 0 };
55 std::shared_ptr<Portal::Graphics::GpuBufferHandle> meta;
56 std::shared_ptr<Portal::Graphics::GpuBufferHandle> points;
57 std::shared_ptr<Portal::Graphics::GpuBufferHandle> area_perim;
58};
59
60/**
61 * @brief Live device views of the GPU-resident export buffers, one named
62 * slot per exporting op, populated only when its own export flag was
63 * set. Null/default otherwise. The executor owns the memory: each
64 * view is valid for one more run before its buffer is reused.
65 *
66 * tracks/keypoints decode via VisionGpuExecutor::read_exported_tracks()/
67 * read_exported_keypoints(). labels is ConnectedComponents' per-pixel
68 * compact label map (one uint32 per pixel, 0 background, 1..count
69 * foreground, matching component_boxes/Contour::label_id), consumed via
70 * VisionGpuExecutor::select_label() rather than decoded to a host list.
71 * contours decodes via read_exported_contours(); see ContoursBufferView.
72 */
74 std::shared_ptr<Portal::Graphics::GpuBufferHandle> tracks;
75 std::shared_ptr<Portal::Graphics::GpuBufferHandle> keypoints;
76 std::shared_ptr<Portal::Graphics::GpuBufferHandle> labels;
78};
79
80/**
81 * @brief One image slot per VisionOp that produces an image, populated
82 * only for whichever of these ops ran in the sequence. Null means
83 * the op did not run.
84 *
85 * RgbaToGray uses the separate top-level VisionResult::gray instead.
86 * component_colors, contour_image, and flow_visualization come from
87 * ConnectedComponents::with_colors, FindContours::as_image, and
88 * OpticalFlowDense::visualize respectively.
89 */
91 std::shared_ptr<Core::VKImage> rgba_to_hsv;
92 std::shared_ptr<Core::VKImage> gray_to_rgba;
93 std::shared_ptr<Core::VKImage> downsample_2x;
94 std::shared_ptr<Core::VKImage> threshold;
95 std::shared_ptr<Core::VKImage> threshold_adaptive;
96 std::shared_ptr<Core::VKImage> threshold_otsu;
97 std::shared_ptr<Core::VKImage> normalize_inplace;
98 std::shared_ptr<Core::VKImage> normalize_range;
99 std::shared_ptr<Core::VKImage> gaussian_blur;
100 std::shared_ptr<Core::VKImage> filter_separable;
101 std::shared_ptr<Core::VKImage> sobel;
102 std::shared_ptr<Core::VKImage> scharr;
103 std::shared_ptr<Core::VKImage> canny;
104 std::shared_ptr<Core::VKImage> erode;
105 std::shared_ptr<Core::VKImage> dilate;
106 std::shared_ptr<Core::VKImage> open;
107 std::shared_ptr<Core::VKImage> close;
108 std::shared_ptr<Core::VKImage> morph_gradient;
109 std::shared_ptr<Core::VKImage> harris_response;
110 std::shared_ptr<Core::VKImage> confine;
111
112 std::shared_ptr<Core::VKImage> component_colors; ///< ConnectedComponents::with_colors
113 std::shared_ptr<Core::VKImage> contour_image; ///< FindContours::as_image
114 std::shared_ptr<Core::VKImage> flow_visualization; ///< OpticalFlowDense::visualize
115};
116
117/**
118 * @brief Result of executing a VisionSequence on one frame.
119 *
120 * pixel_image holds the final normalised float pixel buffer as a DataVariant
121 * (active alternative: vector<float>). Empty when the terminal step produces
122 * only structured output.
123 *
124 * Callers access pixel data via:
125 * EigenAccess(result.pixel_image).view<Eigen::VectorXf>(): zero-copy Eigen map
126 * std::get<std::vector<float>>(result.pixel_image): direct vector access
127 *
128 * w and h are the dimensions of pixel_image. Both are 0 when pixel_image is empty.
129 */
131 Kakshya::DataVariant pixel_image { std::vector<float> {} };
132 StructuredOutput structured { std::monostate {} };
133 std::vector<SnapshotEntry> snapshots;
134
135 /**
136 * @brief Dense optical flow field from OpticalFlowDense, or null.
137 *
138 * RGBA32F at the resolution of the tracked frame: r and g are the flow in
139 * pixels, b the confidence, a the residual.
140 */
141 std::shared_ptr<Core::VKImage> flow;
142
143 /**
144 * @brief Mean per-pixel intensity change from OpticalFlowDense's own
145 * frame-to-frame comparison, 0 when there is no previous frame.
146 */
147 float motion_energy { 0.0F };
148
149 /**
150 * @brief Output of a RgbaToGray step in this sequence, or null.
151 *
152 * A storage image, so passing it as the image argument to a later
153 * VisionGpuExecutor::run() call skips re-ingesting and re-converting:
154 * op_ingest passes any frame already carrying storage usage through
155 * unchanged. Intended for a caller resolving one VisionQuery into
156 * several VisionSequences that all start from the same gray frame, so
157 * only the first pays for RgbaToGray.
158 */
159 std::shared_ptr<Core::VKImage> gray;
160
161 /**
162 * @brief One BoundingBox per component from a ConnectedComponents step
163 * in this sequence, label 1..count, or empty.
164 *
165 * Populated whenever ConnectedComponents ran, whether or not FindContours
166 * immediately follows: the box computation is a small readback of
167 * already GPU-reduced min/max buffers, not per-pixel work, so it costs
168 * nothing extra to keep regardless of what the sequence does next.
169 * label_id on each box matches Contour::label_id from a following
170 * FindContours step exactly, letting a caller join a contour's polygon
171 * to its own bounding box without re-deriving one from the polygon.
172 */
173 std::vector<BoundingBox> component_boxes;
174
175 /**
176 * @brief GPU-resident export buffers, one named slot per exporting op.
177 * See BufferOutputs.
178 */
180
181 /**
182 * @brief Image output of whichever image-producing ops ran in this
183 * sequence, one named slot per op. See ImageOutputs.
184 */
186
187 uint32_t w { 0 };
188 uint32_t h { 0 };
190 size_t suspended_at { 0 };
191
192 /**
193 * @brief True when the sequence reached its end and this result may be
194 * consumed, cached, or broadcast.
195 */
196 [[nodiscard]] bool is_ready() const noexcept
197 {
199 }
200
201 /**
202 * @brief Zero-copy float span into pixel_image storage.
203 * @return Empty span if pixel_image is not vector<float> or is empty.
204 */
205 [[nodiscard]] std::span<const float> as_span() const noexcept
206 {
207 const auto* v = std::get_if<std::vector<float>>(&pixel_image);
208 if (!v || v->empty())
209 return {};
210 return { v->data(), v->size() };
211 }
212};
213
214/**
215 * @brief State threaded through one execution of a VisionSequence.
216 *
217 * Shared by the CPU and GPU executors so op functions have one signature on
218 * both paths. Three bands: the walk, working storage, and redundancy caches.
219 *
220 * Cross-run retained state is not here. It is owned vectors on the CPU path
221 * and owned images on the GPU path, neither of which is a working storage
222 * handle, so each executor holds its own in its own types.
223 *
224 * @tparam Handle Working storage handle. Slot index on the CPU path,
225 * shared_ptr<VKImage> on the GPU path.
226 *
227 * sequence is non-owning and valid only for the run that constructed the
228 * pass. Storage and caches outlive a single run once the pass is held by
229 * the executor; the walk band is reset per run by begin().
230 */
231template <typename Handle>
233 // -------------------------------------------------------------------------
234 // Walk
235 // -------------------------------------------------------------------------
236
237 const VisionSequence* sequence { nullptr };
238 size_t index { 0 };
239 uint32_t w { 0 };
240 uint32_t h { 0 };
241 uint32_t channels { 4 };
243
244 // -------------------------------------------------------------------------
245 // Working storage
246 // -------------------------------------------------------------------------
247
248 Handle current {};
249 uint32_t storage_w { 0 };
250 uint32_t storage_h { 0 };
251
252 // -------------------------------------------------------------------------
253 // Retained across runs
254 // -------------------------------------------------------------------------
255
256 Handle prev {};
257 Handle prev_cache {};
258 std::vector<Keypoint> prev_keypoints;
259
260 // -------------------------------------------------------------------------
261 // Redundancy caches
262 // -------------------------------------------------------------------------
263
264 struct Completed {
265 Handle output {};
266 Handle input {};
267 };
268
269 std::unordered_map<size_t, Completed> completed;
270
271 /**
272 * @brief Reset the walk band for a fresh run.
273 *
274 * Clears the memo unconditionally: its keys compare image handles, which
275 * proxy for content only within one walk. Across runs the caller's input
276 * and the contexts' output images are reused in place, so an equal handle
277 * does not mean equal pixels.
278 *
279 * Retained cross-run state is dropped only when the geometry changes,
280 * since surviving between runs is what it is for.
281 */
282 void begin(const VisionSequence& seq, uint32_t width, uint32_t height)
283 {
284 completed.clear();
285
286 if (width != w || height != h)
287 forget();
288
289 sequence = &seq;
290 index = 0;
291 channels = 4;
292 result = VisionResult {};
294 }
295
296 /**
297 * @brief Discard retained cross-run state. Storage and caches are kept.
298 */
299 void forget()
300 {
301 prev = Handle {};
302 prev_cache = Handle {};
303 prev_keypoints.clear();
304 }
305
306 [[nodiscard]] const VisionStep& step() const noexcept
307 {
308 return sequence->steps[index];
309 }
310
311 [[nodiscard]] size_t plane_size() const noexcept
312 {
313 return static_cast<size_t>(w) * h;
314 }
315
316 /**
317 * @brief Op at @p offset steps ahead, or nullptr past the end.
318 *
319 * Replaces the adjacency booleans derived by VisionSequence::Builder.
320 * Correct under any mutation of steps because it is evaluated at the
321 * point of use.
322 */
323 [[nodiscard]] const VisionStep* ahead(size_t offset = 1) const noexcept
324 {
325 const auto& steps = sequence->steps;
326 const size_t at = index + offset;
327 return at < steps.size() ? &steps[at] : nullptr;
328 }
329
330 /**
331 * @brief Op at @p offset steps back, or nullptr before the start.
332 *
333 * Counterpart to ahead(), for ops validating what produced their input.
334 */
335 [[nodiscard]] const VisionStep* behind(size_t offset = 1) const noexcept
336 {
337 if (offset > index)
338 return nullptr;
339 return &sequence->steps[index - offset];
340 }
341
342 void set_geometry(uint32_t width, uint32_t height) noexcept
343 {
344 w = width;
345 h = height;
346 result.w = width;
347 result.h = height;
348 }
349
350 /**
351 * @brief Memoised output for @p key when it was produced from @p input.
352 */
353 [[nodiscard]] const Handle* memo(size_t key, const Handle& input) const
354 {
355 auto it = completed.find(key);
356 if (it == completed.end() || !(it->second.input == input))
357 return nullptr;
358 return &it->second.output;
359 }
360};
361
364
365} // namespace MayaFlux::Kinesis::Vision
uint64_t offset
Definition Composite.cpp:10
Core::GlobalInputConfig input
Definition Config.cpp:42
Connected component labelling on normalised float masks.
Gradient and edge detection on normalised float image spans.
Sparse optical flow via Lucas-Kanade tracker.
Declarative description of a Kinesis::Vision processing sequence.
uint32_t width
uint32_t height
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:70
std::variant< std::monostate, GradientResult, ComponentResult, std::vector< Contour >, std::vector< Keypoint >, std::vector< TrackResult > > StructuredOutput
VisionStatus
Whether a run carried the sequence to its end.
std::shared_ptr< Portal::Graphics::GpuBufferHandle > labels
std::shared_ptr< Portal::Graphics::GpuBufferHandle > tracks
std::shared_ptr< Portal::Graphics::GpuBufferHandle > keypoints
Live device views of the GPU-resident export buffers, one named slot per exporting op,...
Result of connected component labelling.
std::shared_ptr< Portal::Graphics::GpuBufferHandle > points
std::shared_ptr< Portal::Graphics::GpuBufferHandle > meta
std::shared_ptr< Portal::Graphics::GpuBufferHandle > area_perim
Live device view of FindContours' traced contours, when FindContoursParams::export_contours_buffer is...
Gradient maps produced by Sobel and Scharr operators.
Definition Gradient.hpp:22
std::shared_ptr< Core::VKImage > scharr
std::shared_ptr< Core::VKImage > filter_separable
std::shared_ptr< Core::VKImage > close
std::shared_ptr< Core::VKImage > confine
std::shared_ptr< Core::VKImage > threshold_adaptive
std::shared_ptr< Core::VKImage > flow_visualization
OpticalFlowDense::visualize.
std::shared_ptr< Core::VKImage > threshold_otsu
std::shared_ptr< Core::VKImage > contour_image
FindContours::as_image.
std::shared_ptr< Core::VKImage > rgba_to_hsv
std::shared_ptr< Core::VKImage > open
std::shared_ptr< Core::VKImage > gaussian_blur
std::shared_ptr< Core::VKImage > threshold
std::shared_ptr< Core::VKImage > harris_response
std::shared_ptr< Core::VKImage > normalize_range
std::shared_ptr< Core::VKImage > downsample_2x
std::shared_ptr< Core::VKImage > component_colors
ConnectedComponents::with_colors.
std::shared_ptr< Core::VKImage > sobel
std::shared_ptr< Core::VKImage > dilate
std::shared_ptr< Core::VKImage > normalize_inplace
std::shared_ptr< Core::VKImage > erode
std::shared_ptr< Core::VKImage > canny
std::shared_ptr< Core::VKImage > gray_to_rgba
std::shared_ptr< Core::VKImage > morph_gradient
One image slot per VisionOp that produces an image, populated only for whichever of these ops ran in ...
void begin(const VisionSequence &seq, uint32_t width, uint32_t height)
Reset the walk band for a fresh run.
const VisionStep & step() const noexcept
const VisionStep * behind(size_t offset=1) const noexcept
Op at offset steps back, or nullptr before the start.
const VisionStep * ahead(size_t offset=1) const noexcept
Op at offset steps ahead, or nullptr past the end.
const Handle * memo(size_t key, const Handle &input) const
Memoised output for key when it was produced from input.
std::unordered_map< size_t, Completed > completed
void set_geometry(uint32_t width, uint32_t height) noexcept
void forget()
Discard retained cross-run state.
State threaded through one execution of a VisionSequence.
std::shared_ptr< Core::VKImage > flow
Dense optical flow field from OpticalFlowDense, or null.
std::vector< BoundingBox > component_boxes
One BoundingBox per component from a ConnectedComponents step in this sequence, label 1....
std::vector< SnapshotEntry > snapshots
std::shared_ptr< Core::VKImage > gray
Output of a RgbaToGray step in this sequence, or null.
BufferOutputs buffers
GPU-resident export buffers, one named slot per exporting op.
bool is_ready() const noexcept
True when the sequence reached its end and this result may be consumed, cached, or broadcast.
float motion_energy
Mean per-pixel intensity change from OpticalFlowDense's own frame-to-frame comparison,...
ImageOutputs images
Image output of whichever image-producing ops ran in this sequence, one named slot per op.
std::span< const float > as_span() const noexcept
Zero-copy float span into pixel_image storage.
Result of executing a VisionSequence on one frame.
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