MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionAnalyzer.hpp
Go to the documentation of this file.
1#pragma once
2
9
10/**
11 * @file VisionAnalyzer.hpp
12 * @brief FunctionalOperation<shared_ptr<SignalSourceContainer>,
13 * vector<DataVariant>> whose real API matches EnergyAnalyzer/
14 * StatisticalAnalyzer's own convention: query/context are
15 * constructor arguments and setters, not call arguments, and
16 * analyze_vision(source) takes only the data, the same shape as
17 * analyze_energy(data)/analyze_statistics(data).
18 *
19 * width/height are never passed in: VKImage and every container Vision
20 * accepts (TextureContainer, VideoStreamContainer, WindowContainer) already
21 * report their own get_width()/get_height().
22 */
23
25class VisionMatrix;
26}
27
28namespace MayaFlux::Kakshya {
29class SignalSourceContainer;
30}
31
32namespace MayaFlux::Yantra {
33
34/**
35 * @class VisionAnalyzer
36 * @brief Resolves its configured VisionQuery, runs every resolved
37 * VisionSequence against a persistent VisionGpuExecutor, then runs
38 * the GPU reductions (track_reduce.comp) over the (context-filtered)
39 * subject tracks/keypoints for centroid/velocity/bounds.
40 *
41 * Owns its VisionGpuExecutor for the same reason VisionProcessor does:
42 * cross-frame state (retained optical flow atlases and detections) lives
43 * on the executor's contexts and must survive between calls.
44 */
45class MAYAFLUX_API VisionAnalyzer
46 : public FunctionalOperation<std::shared_ptr<Kakshya::SignalSourceContainer>, std::vector<Kakshya::DataVariant>> {
47public:
49
50 /**
51 * @brief Construct with the intents to satisfy and the caller-supplied
52 * filtering context, matching EnergyAnalyzer's
53 * constructor-configures-the-run convention.
54 */
55 explicit VisionAnalyzer(
58
61
62 void set_query(const Kinesis::Vision::VisionQuery& query) { m_query = query; }
63 [[nodiscard]] const Kinesis::Vision::VisionQuery& get_query() const { return m_query; }
64
65 void set_context(const Kinesis::Vision::VisionAnalysisContext& context) { m_context = context; }
66 [[nodiscard]] const Kinesis::Vision::VisionAnalysisContext& get_context() const { return m_context; }
67
68 /**
69 * @brief Real entry point: resolve the configured query against
70 * source, run it, run the GPU analysis. Same shape as
71 * analyze_energy(data)/analyze_statistics(data): only the data
72 * is a call argument. To change what a later call does, call
73 * set_query()/set_context() first, the same way a caller
74 * changes EnergyAnalyzer's behaviour with set_method() before
75 * the next analyze_energy(), not by passing it alongside data.
76 *
77 * @param source Image-bearing container (VideoStreamContainer,
78 * WindowContainer, or TextureContainer; the same set
79 * VisionProcessor accepts). Anything else yields an
80 * empty VisionAnalysis.
81 * @param index Layer/frame index, used only for a TextureContainer's
82 * to_image(index); ignored for the other two, which have
83 * no multi-index concept and always give their current
84 * frame.
85 */
86 [[nodiscard]] Kinesis::Vision::VisionAnalysis analyze_vision(
87 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
88 size_t index = 0);
89
90 /**
91 * @brief analyze_vision(), for a caller that already has a resolved
92 * GPU image (e.g. VisionExtractor's own output) rather than a
93 * container. Skips container resolution entirely; otherwise
94 * identical.
95 */
96 [[nodiscard]] Kinesis::Vision::VisionAnalysis analyze_vision(
97 const std::shared_ptr<Core::VKImage>& image);
98
99 /**
100 * @brief Advance an analysis of @p image by one sequence per call,
101 * without waiting on deferred GPU work.
102 *
103 * The first call copies @p image and starts a run on the copy. Later
104 * calls continue that run and ignore @p image until it completes. The
105 * flow steps of TrackObjects and EstimateMotion are submitted deferred
106 * and polled on later calls; other steps still wait inline.
107 *
108 * @return The analysis once every sequence has completed, otherwise
109 * nullopt.
110 */
111 [[nodiscard]] std::optional<Kinesis::Vision::VisionAnalysis> advance(
112 const std::shared_ptr<Core::VKImage>& image);
113
114 /**
115 * @brief The copy of the frame the current or last advance() run
116 * analyzes. Replaced when the next run starts.
117 */
118 [[nodiscard]] const std::shared_ptr<Core::VKImage>& get_frame() const { return m_frame; }
119
120 /**
121 * @brief The VisionResult of every sequence of the last completed
122 * analysis, in the order they ran. Empty before the first.
123 *
124 * The images in each result are named by the op that produced them and
125 * belong to that sequence's executor: they are valid until the next
126 * analysis runs. A sequence that failed leaves an empty result.
127 */
128 [[nodiscard]] const std::vector<Kinesis::Vision::VisionResult>& get_results() const { return *m_results; }
129
130 /**
131 * @brief The same results, owned: they stay valid after later analyses,
132 * which replace them with new ones.
133 *
134 * The images are the executors' own and are rewritten in place when
135 * their sequence runs again.
136 */
137 [[nodiscard]] std::shared_ptr<const std::vector<Kinesis::Vision::VisionResult>> get_shared_results() const { return m_results; }
138
139 /**
140 * @brief Abandon outstanding work and clear retained executor state.
141 *
142 * Call when the pixel source changes (camera switch, video seek).
143 */
144 void reset();
145
146 /**
147 * @brief The executor that ran the FindElements sequence, or the first
148 * executor when the query has none.
149 *
150 * Each sequence of a query runs on its own executor, so the images one
151 * sequence leaves in its VisionResult are not overwritten by the next.
152 * VisionExtractor::mask() needs the executor holding the label buffer
153 * that VisionAnalysis::find_elements indexes into, which is this one.
154 *
155 * Valid after at least one analyze_vision() call; null before that.
156 */
157 [[nodiscard]] VisionGpuExecutor* get_executor() const;
158
159private:
161
162 /**
163 * @brief ComputeOperation adapter: uses input.data as the source
164 * container, pulls index out of Datum metadata (defaulting to
165 * 0), calls analyze_vision(). See analyze_vision() for the real
166 * API; query/context come from this instance's own
167 * configured state, not from the Datum, matching every other
168 * UniversalAnalyzer-family operation_function.
169 */
170 output_type run_operation(const input_type& input);
171
172 /**
173 * @brief Resolve source/index to a GPU image, the same way
174 * VisionProcessor does: TextureContainer's own to_image(index)
175 * (zero-copy cache), or get_raw_data() uploaded into a
176 * persistent m_upload_image for VideoStreamContainer/
177 * WindowContainer. Null on an unsupported container type.
178 */
179 [[nodiscard]] std::shared_ptr<Core::VKImage> resolve_image(
180 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
181 size_t index);
182
183 /**
184 * @brief Shared body of both analyze_vision() overloads, once a GPU
185 * image is in hand: resolve the configured query, run every
186 * resolved VisionSequence, apply context filtering, run the
187 * GPU reductions. w/h are the image's own dimensions.
188 */
189 [[nodiscard]] Kinesis::Vision::VisionAnalysis analyze_resolved(
190 const std::shared_ptr<Core::VKImage>& image);
191
192 /**
193 * @brief Run or poll sequence @p lane of @p resolved on its executor.
194 * @return False while the sequence is suspended on deferred work; true
195 * once its result is appended to @p results.
196 */
197 bool run_lane(
198 size_t lane,
199 const std::vector<Kinesis::Vision::ResolvedSequence>& resolved,
200 std::vector<Kinesis::Vision::VisionResult>& results,
201 const std::shared_ptr<Core::VKImage>& image);
202
203 /**
204 * @brief Route every completed sequence result into one VisionAnalysis,
205 * apply context filtering and run the GPU reductions.
206 */
207 [[nodiscard]] Kinesis::Vision::VisionAnalysis assemble(
208 const std::vector<Kinesis::Vision::ResolvedSequence>& resolved,
209 const std::vector<Kinesis::Vision::VisionResult>& results,
210 uint32_t w, uint32_t h);
211
212 /** @brief Copy @p image into this analyzer's own frame image. Null on failure. */
213 [[nodiscard]] std::shared_ptr<Core::VKImage> hold_frame(const std::shared_ptr<Core::VKImage>& image);
214
215 /**
216 * @brief State of the run advance() is stepping through.
217 *
218 * resolved is fixed for the whole run: a suspended executor keeps a
219 * pointer to the sequence it is walking.
220 */
221 struct StepRun {
222 std::vector<Kinesis::Vision::ResolvedSequence> resolved;
223 std::vector<Kinesis::Vision::VisionResult> results;
224 uint32_t w {};
225 uint32_t h {};
226 bool active {};
227 };
228
229 /**
230 * @brief Dispatch track_reduce.comp over tracks and fill
231 * centroid/velocity/bounds/mean_speed_px/age stats on the
232 * result. No-op (default fields) when tracks is empty.
233 */
234 [[nodiscard]] Kinesis::Vision::TrackObjectsAnalysis reduce_tracks(
235 const std::vector<Kinesis::Vision::TrackResult>& tracks, uint32_t w, uint32_t h);
236
237 /**
238 * @brief Dispatch track_reduce.comp over keypoint positions (fed as
239 * their own previous, so velocity reduces to zero and is simply
240 * not surfaced) and fill centroid/bounds/mean_response on the
241 * result. No-op (default fields) when keypoints is empty. Same
242 * shader as reduce_tracks, not a duplicate: a keypoint has no
243 * persistent identity or previous position of its own, but its
244 * bounds and centroid are the same min/max/sum reduction either
245 * way.
246 */
247 [[nodiscard]] Kinesis::Vision::DetectFeaturesAnalysis reduce_keypoints(
248 const std::vector<Kinesis::Vision::Keypoint>& keypoints);
249
250 /**
251 * @brief Shape descriptors (element_shape.comp) and nearest-neighbour
252 * distances (nearest_neighbor.comp) over fe.boxes, filled in
253 * place. No-op when fe.boxes is empty.
254 */
255 void compute_shapes(Kinesis::Vision::FindElementsAnalysis& fe, uint32_t w, uint32_t h);
256
257 /**
258 * @brief MeasureAppearance's whole-frame gradient sample (region_sample.comp
259 * over result.images.sobel) and brightness histogram
260 * (otsu_histogram.comp over gray). gray is the frame's gray image,
261 * which a sequence seeded from another lane's RgbaToGray does not
262 * produce itself, so the caller supplies it. Empty fields for
263 * whichever input image is null.
264 */
265 [[nodiscard]] Kinesis::Vision::MeasureAppearanceAnalysis measure_appearance(
266 const Kinesis::Vision::VisionResult& result,
267 const std::shared_ptr<Core::VKImage>& gray,
268 uint32_t w, uint32_t h);
269
270 /** @brief Dispatch region_sample.comp over the whole image (no region concept for MeasureAppearance). */
271 [[nodiscard]] Kinesis::Vision::FieldSample sample_full_frame(
272 const std::shared_ptr<Core::VKImage>& image, uint32_t w, uint32_t h);
273
274 /** @brief Dispatch otsu_histogram.comp over image, zeroing the buffer first. */
275 [[nodiscard]] std::array<uint32_t, 256> compute_histogram(
276 const std::shared_ptr<Core::VKImage>& image, uint32_t w, uint32_t h);
277
278 /** @brief Dispatch histogram_reduce.comp over an already-computed histogram. */
279 [[nodiscard]] float compute_mean_brightness(const std::array<uint32_t, 256>& histogram);
280
283
284 std::vector<std::unique_ptr<VisionGpuExecutor>> m_executors;
285 size_t m_label_lane { 0 };
286 std::shared_ptr<ShaderExecutionContext<>> m_track_reducer;
287 std::shared_ptr<ShaderExecutionContext<>> m_brightness_reducer;
288 std::shared_ptr<ShaderExecutionContext<>> m_shape_ctx;
289 std::shared_ptr<ShaderExecutionContext<>> m_neighbor_ctx;
290 std::shared_ptr<TextureExecutionContext> m_gradient_sample_ctx;
291 std::shared_ptr<TextureExecutionContext> m_histogram_ctx;
292
293 std::shared_ptr<Core::VKImage> m_upload_image;
294 uint32_t m_upload_w { 0 };
295 uint32_t m_upload_h { 0 };
296
298 std::shared_ptr<const std::vector<Kinesis::Vision::VisionResult>> m_results {
299 std::make_shared<const std::vector<Kinesis::Vision::VisionResult>>()
300 };
302 std::shared_ptr<Core::VKImage> m_frame;
303};
304
305} // namespace MayaFlux::Yantra
Core::GlobalInputConfig input
Definition Config.cpp:42
IO::ImageData image
Definition Decoder.cpp:47
uint32_t h
Definition InkPress.cpp:29
uint32_t index
Definition VKDevice.cpp:122
GPU execution layer for Kinesis::Vision::VisionSequence.
Caller-supplied context and GPU-derived output for turning a raw VisionResult into something a caller...
Per-intent parameters and resolution into a set of VisionSequences.
Wraps a single callable as a ComputeOperation, for a wild or one-off idea that does not warrant a han...
std::shared_ptr< Core::VKImage > m_upload_image
std::shared_ptr< ShaderExecutionContext<> > m_shape_ctx
Kinesis::Vision::VisionQuery m_query
std::shared_ptr< Core::VKImage > m_frame
void set_context(const Kinesis::Vision::VisionAnalysisContext &context)
Portal::Graphics::ImageCacheEntry m_frame_cache
const std::shared_ptr< Core::VKImage > & get_frame() const
The copy of the frame the current or last advance() run analyzes.
std::vector< std::unique_ptr< VisionGpuExecutor > > m_executors
const Kinesis::Vision::VisionQuery & get_query() const
std::shared_ptr< TextureExecutionContext > m_histogram_ctx
VisionAnalyzer(const VisionAnalyzer &)=delete
std::shared_ptr< ShaderExecutionContext<> > m_neighbor_ctx
const std::vector< Kinesis::Vision::VisionResult > & get_results() const
The VisionResult of every sequence of the last completed analysis, in the order they ran.
std::shared_ptr< ShaderExecutionContext<> > m_brightness_reducer
Kinesis::Vision::VisionAnalysisContext m_context
VisionAnalyzer & operator=(const VisionAnalyzer &)=delete
const Kinesis::Vision::VisionAnalysisContext & get_context() const
std::shared_ptr< TextureExecutionContext > m_gradient_sample_ctx
std::shared_ptr< ShaderExecutionContext<> > m_track_reducer
void set_query(const Kinesis::Vision::VisionQuery &query)
std::shared_ptr< const std::vector< Kinesis::Vision::VisionResult > > get_shared_results() const
The same results, owned: they stay valid after later analyses, which replace them with new ones.
Resolves its configured VisionQuery, runs every resolved VisionSequence against a persistent VisionGp...
Stateful GPU dispatch engine for VisionSequence execution.
Compute matrix holding one VisionAnalyzer for a query and, when an extraction mode is given,...
Keypoint reduction, computed on GPU by the same reduction shader as TrackObjectsAnalysis (track_reduc...
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....
Track reduction, computed on GPU (Yantra's track_reduce.comp) from the (context-filtered) subject tra...
Composed context: caller-supplied data for whichever intents need it, consulted after a VisionQuery's...
Composed analysis output: one populated field per active intent that produced a result this call.
Composed query: which VisionIntents are active, and each active one's parameters.
Result of executing a VisionSequence on one frame.
Image retained by the producing owner.
std::vector< Kinesis::Vision::ResolvedSequence > resolved
std::vector< Kinesis::Vision::VisionResult > results
State of the run advance() is stepping through.