MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionExtractor.hpp
Go to the documentation of this file.
1#pragma once
2
8
9/**
10 * @file VisionExtractor.hpp
11 * @brief GPU getter for what VisionAnalyzer found. Every method takes a
12 * Kinesis::Vision::VisionAnalysis and derives its geometry from
13 * whichever field is populated; an analysis with nothing in it
14 * extracts nothing. All pixel work runs on the GPU, the host only
15 * packs the analysis into buffers and dispatches.
16 *
17 * FunctionalOperation<shared_ptr<SignalSourceContainer>, vector<DataVariant>>,
18 * like VisionAnalyzer. run_operation reads the analysis from Datum metadata
19 * key "vision_analysis" (the key VisionAnalyzer::run_operation writes) and
20 * runs the method selected by VisionExtractMode, so a VisionAnalyzer ->
21 * VisionExtractor chain needs no adapter. mask() alone stays outside that
22 * path: it needs the VisionGpuExecutor that owns the label buffer.
23 */
24
25namespace MayaFlux::Kakshya {
26class SignalSourceContainer;
27}
28
29namespace MayaFlux::Yantra {
30
31/**
32 * @enum VisionExtractMode
33 * @brief Which method run_operation dispatches to.
34 */
35enum class VisionExtractMode : uint8_t {
36 Crop,
37 Sample,
38 Patches,
39 Crops,
44 Expose,
45 Edges,
49};
50
51/**
52 * @enum ElementCriterion
53 * @brief Per-element measure select_element() ranks find_elements by, taken
54 * from what the analyzer already computed: contour area, shape
55 * compactness, aspect ratio, nearest-neighbour distance (isolation),
56 * or distance from a point.
57 */
58enum class ElementCriterion : uint8_t {
59 Area,
64};
65
66/**
67 * @brief How the extractor chooses among several found elements: rank by
68 * criterion, highest first when largest, lowest first otherwise.
69 * Proximity ignores largest and picks the element whose centre is
70 * closest to point.
71 */
74 bool largest { true };
75 glm::vec2 point { 0.5F, 0.5F };
76};
77
78/**
79 * @class VisionExtractor
80 * @brief Extraction from each VisionAnalysis field.
81 *
82 * find_elements: crop() one element, crops() all, mask() by label buffer,
83 * silhouettes() by contour polygon (no executor), sample_regions(), and
84 * select_element() to rank by shape. tracks and keypoints: patches() and
85 * sample_points(). measure_appearance: equalize() and expose(). detect_edges:
86 * edges(). estimate_motion: motion_mask(). Any field: annotate() draws
87 * everything found onto the frame.
88 *
89 * Which find_elements entry the single-element methods use comes from
90 * set_element_focus() when set (GPU-ranked by shape), otherwise from
91 * set_element_index() into contours (sorted by area when max_contours was
92 * set) or, with no contours, into boxes.
93 *
94 * Thresholds and sizes used by run_operation are set through setters, the
95 * same convention as the other operations; the direct methods take only
96 * the data and the dimensions of what they read or write.
97 *
98 * Image-producing methods share one output slot, so a returned image stays
99 * valid only until the next call that produces an image of the same size.
100 * Hold on to a result by using it (display, copy) before extracting again.
101 */
102class MAYAFLUX_API VisionExtractor
103 : public FunctionalOperation<std::shared_ptr<Kakshya::SignalSourceContainer>, std::vector<Kakshya::DataVariant>> {
104public:
106
107 explicit VisionExtractor(VisionExtractMode mode = VisionExtractMode::Crop);
108
110 [[nodiscard]] VisionExtractMode get_mode() const { return m_mode; }
111
112 void set_element_index(size_t index) { m_element_index = index; }
113 [[nodiscard]] size_t get_element_index() const { return m_element_index; }
114
115 /** @brief Rank elements by shape instead of using a fixed index; empty restores the index. */
116 void set_element_focus(std::optional<ElementFocus> focus) { m_focus = focus; }
117 [[nodiscard]] const std::optional<ElementFocus>& get_element_focus() const { return m_focus; }
118
119 void set_output_size(uint32_t w, uint32_t h) { m_out_w = w; m_out_h = h; }
120 void set_patch_size(uint32_t w, uint32_t h) { m_patch_w = w; m_patch_h = h; }
121 void set_tile_size(uint32_t w, uint32_t h) { m_tile_w = w; m_tile_h = h; }
122
123 /** @brief Window radius sample_points() averages over. */
124 void set_sample_radius(uint32_t radius) { m_sample_radius = radius; }
125
126 /** @brief Edge value edges() keeps a pixel at. */
127 void set_edge_threshold(float threshold) { m_edge_threshold = threshold; }
128
129 /**
130 * @brief motion_mask() limits: minimum flow speed in pixels of the flow
131 * image, minimum solver confidence, and a minimum
132 * VisionAnalysis::motion_activity below which nothing is
133 * dispatched.
134 */
135 void set_motion_limits(float min_speed, float min_confidence, float min_activity)
136 {
137 m_min_speed = min_speed;
138 m_min_confidence = min_confidence;
139 m_min_activity = min_activity;
140 }
141
142 /** @brief Mean brightness expose() scales the frame to. */
143 void set_exposure_target(float target) { m_exposure_target = target; }
144
145 /** @brief Crop of source at the focused element, or track/keypoint bounds. Null with nothing to crop. */
146 [[nodiscard]] std::shared_ptr<Core::VKImage> crop(
147 const std::shared_ptr<Core::VKImage>& source,
148 const Kinesis::Vision::VisionAnalysis& analysis,
149 uint32_t out_w, uint32_t out_h);
150
151 /**
152 * @brief Every find_elements box resampled to tile_w x tile_h, side by
153 * side in one atlas (tile_w * count, tile_h), in box order.
154 */
155 [[nodiscard]] std::shared_ptr<Core::VKImage> crops(
156 const std::shared_ptr<Core::VKImage>& source,
157 const Kinesis::Vision::VisionAnalysis& analysis,
158 uint32_t tile_w, uint32_t tile_h);
159
160 /**
161 * @brief Pixel-precise silhouette of the focused element from the
162 * executor's label buffer. The executor must be the one that ran
163 * the analysis, with export_label_buffer set.
164 */
165 [[nodiscard]] std::shared_ptr<Core::VKImage> mask(
166 VisionGpuExecutor& executor,
167 const std::shared_ptr<Core::VKImage>& source,
168 const Kinesis::Vision::VisionAnalysis& analysis,
169 uint32_t w, uint32_t h);
170
171 /**
172 * @brief Silhouette of every element cut from its contour polygons: the
173 * outer contour and its holes, even-odd filled. Needs no executor
174 * and no label buffer, so it works on any analysis kept from
175 * earlier. Same atlas layout as crops(). Contours truncated by
176 * max_points_per_contour fill wrongly.
177 */
178 [[nodiscard]] std::shared_ptr<Core::VKImage> silhouettes(
179 const std::shared_ptr<Core::VKImage>& source,
180 const Kinesis::Vision::VisionAnalysis& analysis,
181 uint32_t tile_w, uint32_t tile_h);
182
183 /** @brief silhouettes() for the focused element only. */
184 [[nodiscard]] std::shared_ptr<Core::VKImage> silhouette(
185 const std::shared_ptr<Core::VKImage>& source,
186 const Kinesis::Vision::VisionAnalysis& analysis,
187 uint32_t tile_w, uint32_t tile_h);
188
189 /** @brief Mean, min and max over the focused element or track/keypoint bounds. */
190 [[nodiscard]] Kinesis::Vision::FieldSample sample(
191 const std::shared_ptr<Core::VKImage>& source,
192 const Kinesis::Vision::VisionAnalysis& analysis,
193 uint32_t source_w, uint32_t source_h);
194
195 /** @brief sample() for every find_elements box in one dispatch, in box order. */
196 [[nodiscard]] std::vector<Kinesis::Vision::FieldSample> sample_regions(
197 const std::shared_ptr<Core::VKImage>& source,
198 const Kinesis::Vision::VisionAnalysis& analysis,
199 uint32_t source_w, uint32_t source_h);
200
201 /**
202 * @brief Mean over a (2 * radius + 1) squared window at every keypoint,
203 * else every track, in one dispatch, in point order. With flow as
204 * the source this is the motion under each point, with the frame
205 * the colour under it.
206 */
207 [[nodiscard]] std::vector<glm::vec4> sample_points(
208 const std::shared_ptr<Core::VKImage>& source,
209 const Kinesis::Vision::VisionAnalysis& analysis,
210 uint32_t radius,
211 uint32_t source_w, uint32_t source_h);
212
213 /** @brief One fixed-size crop per keypoint, else per track, side by side in one atlas. */
214 [[nodiscard]] std::shared_ptr<Core::VKImage> patches(
215 const std::shared_ptr<Core::VKImage>& source,
216 const Kinesis::Vision::VisionAnalysis& analysis,
217 uint32_t patch_w, uint32_t patch_h,
218 uint32_t source_w, uint32_t source_h);
219
220 /**
221 * @brief Index into find_elements boxes of the element ranked first by
222 * focus, chosen on the GPU from the analyzer's own area, shape and
223 * nearest-neighbour results. Empty without find_elements boxes.
224 */
225 [[nodiscard]] std::optional<size_t> select_element(
226 const Kinesis::Vision::VisionAnalysis& analysis,
227 const ElementFocus& focus);
228
229 /**
230 * @brief Histogram equalisation of source from the histogram
231 * measure_appearance measured. Null without measure_appearance.
232 */
233 [[nodiscard]] std::shared_ptr<Core::VKImage> equalize(
234 const std::shared_ptr<Core::VKImage>& source,
235 const Kinesis::Vision::VisionAnalysis& analysis,
236 uint32_t source_w, uint32_t source_h);
237
238 /**
239 * @brief source scaled so its measured mean brightness reaches
240 * target. Null without measure_appearance.
241 */
242 [[nodiscard]] std::shared_ptr<Core::VKImage> expose(
243 const std::shared_ptr<Core::VKImage>& source,
244 const Kinesis::Vision::VisionAnalysis& analysis,
245 float target,
246 uint32_t source_w, uint32_t source_h);
247
248 /** @brief source kept where analysis.detect_edges reaches the edge threshold, transparent elsewhere. */
249 [[nodiscard]] std::shared_ptr<Core::VKImage> edges(
250 const std::shared_ptr<Core::VKImage>& source,
251 const Kinesis::Vision::VisionAnalysis& analysis,
252 uint32_t source_w, uint32_t source_h);
253
254 /**
255 * @brief source kept where analysis.estimate_motion moves faster than
256 * the motion limits with enough confidence. Null when no flow was
257 * computed or motion_activity is below the activity limit.
258 */
259 [[nodiscard]] std::shared_ptr<Core::VKImage> motion_mask(
260 const std::shared_ptr<Core::VKImage>& source,
261 const Kinesis::Vision::VisionAnalysis& analysis,
262 uint32_t source_w, uint32_t source_h);
263
264 /**
265 * @brief source with everything the analysis found drawn on it: element
266 * boxes (yellow) and contours (cyan), track arrows from previous
267 * to current position (green tracked, red lost) with their bounds
268 * and centroid (white), keypoints (magenta) with their bounds.
269 */
270 [[nodiscard]] std::shared_ptr<Core::VKImage> annotate(
271 const std::shared_ptr<Core::VKImage>& source,
272 const Kinesis::Vision::VisionAnalysis& analysis,
273 uint32_t source_w, uint32_t source_h);
274
275private:
276 output_type run_operation(const input_type& input);
277
278 [[nodiscard]] std::shared_ptr<Core::VKImage> resolve_image(
279 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
280 size_t index);
281
282 /** @brief Box index of the element the single-element methods act on. */
283 [[nodiscard]] std::optional<size_t> focus_box_index(const Kinesis::Vision::VisionAnalysis& analysis);
284
285 [[nodiscard]] std::optional<Kinesis::Vision::BoundingBox> region_for_analysis(
286 const Kinesis::Vision::VisionAnalysis& analysis);
287
288 [[nodiscard]] std::optional<uint32_t> label_for_analysis(
289 const Kinesis::Vision::VisionAnalysis& analysis);
290
291 [[nodiscard]] std::vector<glm::vec2> points_for_analysis(
292 const Kinesis::Vision::VisionAnalysis& analysis) const;
293
294 [[nodiscard]] std::shared_ptr<Core::VKImage> fill_elements(
295 const std::shared_ptr<Core::VKImage>& source,
297 const std::vector<size_t>& indices,
298 uint32_t tile_w, uint32_t tile_h);
299
300 [[nodiscard]] std::shared_ptr<Core::VKImage> tone(
301 const std::shared_ptr<Core::VKImage>& source,
303 uint32_t mode, float gain,
304 uint32_t source_w, uint32_t source_h);
305
307 size_t m_element_index { 0 };
308 std::optional<ElementFocus> m_focus;
309 uint32_t m_out_w { 0 };
310 uint32_t m_out_h { 0 };
311 uint32_t m_patch_w { 0 };
312 uint32_t m_patch_h { 0 };
313 uint32_t m_tile_w { 0 };
314 uint32_t m_tile_h { 0 };
315 uint32_t m_sample_radius { 1 };
316 float m_edge_threshold { 0.5F };
317 float m_min_speed { 0.5F };
318 float m_min_confidence { 0.1F };
319 float m_min_activity { 0.0F };
320 float m_exposure_target { 0.5F };
321
322 std::shared_ptr<Core::VKImage> m_upload_image;
323 uint32_t m_upload_w { 0 };
324 uint32_t m_upload_h { 0 };
325
326 /**
327 * @brief The two contexts every method shares, one shader swapped in per
328 * call. All extractor shaders use one binding layout: 0 output
329 * image, 1 source image, 2 second image, 3 to 5 input buffers, 6
330 * output buffer. m_image_ctx serves the image-producing methods,
331 * m_reduce_ctx the ones that read back numbers.
332 */
333 [[nodiscard]] TextureExecutionContext& image_context();
334 [[nodiscard]] TextureExecutionContext& reduce_context();
335
336 std::shared_ptr<TextureExecutionContext> m_image_ctx;
337 std::shared_ptr<TextureExecutionContext> m_reduce_ctx;
338};
339
340} // namespace MayaFlux::Yantra
Core::GlobalInputConfig input
Definition Config.cpp:42
std::vector< uint32_t > indices
float radius
uint32_t h
Definition InkPress.cpp:29
uint32_t mode
uint32_t index
Definition VKDevice.cpp:122
uint32_t out_h
float threshold
float min_confidence
uint32_t tile_h
uint32_t patch_w
uint32_t tile_w
uint32_t patch_h
float gain
uint32_t out_w
float min_speed
GPU execution layer for Kinesis::Vision::VisionSequence.
Caller-supplied context and GPU-derived output for turning a raw VisionResult into something a caller...
Wraps a single callable as a ComputeOperation, for a wild or one-off idea that does not warrant a han...
GpuExecutionContext specialisation for image compute shaders.
const std::optional< ElementFocus > & get_element_focus() const
void set_sample_radius(uint32_t radius)
Window radius sample_points() averages over.
std::shared_ptr< Core::VKImage > m_upload_image
VisionExtractMode get_mode() const
std::shared_ptr< TextureExecutionContext > m_reduce_ctx
void set_tile_size(uint32_t w, uint32_t h)
void set_motion_limits(float min_speed, float min_confidence, float min_activity)
motion_mask() limits: minimum flow speed in pixels of the flow image, minimum solver confidence,...
void set_mode(VisionExtractMode mode)
std::shared_ptr< TextureExecutionContext > m_image_ctx
std::optional< ElementFocus > m_focus
void set_patch_size(uint32_t w, uint32_t h)
void set_element_focus(std::optional< ElementFocus > focus)
Rank elements by shape instead of using a fixed index; empty restores the index.
void set_exposure_target(float target)
Mean brightness expose() scales the frame to.
void set_edge_threshold(float threshold)
Edge value edges() keeps a pixel at.
void set_output_size(uint32_t w, uint32_t h)
Extraction from each VisionAnalysis field.
Stateful GPU dispatch engine for VisionSequence execution.
ElementCriterion
Per-element measure select_element() ranks find_elements by, taken from what the analyzer already com...
VisionExtractMode
Which method run_operation dispatches to.
Per-channel mean/min/max over a sampled image region, computed on GPU (Yantra's region_sample....
FindElements output: contours plus the per-component BoundingBoxes ConnectedComponents already comput...
MeasureAppearance output: whole-frame photometric characterisation, computed on GPU (region_sample....
Composed analysis output: one populated field per active intent that produced a result this call.
How the extractor chooses among several found elements: rank by criterion, highest first when largest...