MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionAnalyzer.hpp
Go to the documentation of this file.
1#pragma once
2
4
10
11namespace MayaFlux::Yantra {
12
13// ============================================================================
14// Result types
15// ============================================================================
16
17/**
18 * @brief Flattened structured outputs from one VisionResult.
19 *
20 * Each field is populated only when the terminal VisionSequence step
21 * produces the corresponding type. Empty/default otherwise.
22 * BoundingBox is surfaced from ComponentResult::boxes directly.
23 */
24struct MAYAFLUX_API DetectionSummary {
25 std::vector<Kinesis::Vision::BoundingBox> boxes;
26 std::vector<Kinesis::Vision::Contour> contours;
27 std::vector<Kinesis::Vision::Keypoint> keypoints;
28 std::vector<Kinesis::Vision::TrackResult> tracks;
31};
32
33/**
34 * @struct VisionAnalysis
35 * @brief Analysis result produced by VisionAnalyzer for one frame.
36 */
37struct MAYAFLUX_API VisionAnalysis {
39 std::vector<float> pixel_image;
40 uint32_t w { 0 };
41 uint32_t h { 0 };
42};
43
44/**
45 * @brief Extract a named scalar from a VisionAnalysis result.
46 *
47 * Supported qualifiers:
48 * "box_count" number of bounding boxes
49 * "contour_count" number of contours
50 * "keypoint_count" number of keypoints
51 * "track_count" number of tracked points
52 * "component_count" number of connected components
53 * "mean_gradient_magnitude" mean of GradientResult::magnitude
54 *
55 * Unknown or empty qualifier returns 0.0.
56 *
57 * @param analysis Result produced by VisionAnalyzer.
58 * @param qualifier Name of the scalar to extract.
59 */
60[[nodiscard]] MAYAFLUX_API double extract_scalar_vision(
61 const VisionAnalysis& analysis, const std::string& qualifier);
62
63// ============================================================================
64// VisionAnalyzer
65// ============================================================================
66
67/**
68 * @class VisionAnalyzer
69 * @brief UniversalAnalyzer that takes any pixel-bearing input, runs a
70 * VisionSequence via VisionExecutor, and surfaces structured results
71 * into the Yantra compute graph.
72 *
73 * InputType can be any ComputeData type carrying pixel data:
74 * - std::vector<DataVariant> (direct pixel variants)
75 * - std::shared_ptr<SignalSourceContainer> (TextureContainer, VideoStreamContainer, etc.)
76 * - Kakshya::Region / RegionGroup (pixel sub-region with container)
77 *
78 * extract_structured_native handles all unwrapping transparently. Width and
79 * height are read from SPATIAL_X / SPATIAL_Y dimension roles in the
80 * DataStructureInfo, or from metadata keys "width" and "height" as fallback.
81 *
82 * OutputType is std::vector<DataVariant> carrying VisionResult::pixel_image
83 * as vector<float>, matching the standard analyzer pipeline contract.
84 * Structured results (boxes, contours, keypoints, tracks, gradient,
85 * components) are stored via store_current_analysis and retrieved with
86 * get_vision_analysis() / extract_scalar_vision().
87 *
88 * @code
89 * auto va = std::make_shared<StandardVisionAnalyzer>(sequence);
90 * va->apply_operation(pixel_datum);
91 * auto analysis = va->get_vision_analysis();
92 * double n = extract_scalar_vision(analysis, "box_count");
93 * @endcode
94 */
95template <ComputeData InputType = std::vector<Kakshya::DataVariant>,
96 ComputeData OutputType = std::vector<Kakshya::DataVariant>>
98 : public UniversalAnalyzer<InputType, OutputType> {
99public:
103
104 /**
105 * @brief Construct with the VisionSequence to execute each call.
106 * @param sequence Ordered VisionSteps describing the pipeline.
107 */
109 : m_sequence(std::move(sequence))
110 {
111 }
112
113 /**
114 * @brief Type-safe vision analysis.
115 * @param data Input datum carrying pixel data.
116 * @return VisionAnalysis directly.
117 */
119 {
120 this->analyze_data(data);
121 return get_vision_analysis();
122 }
123
124 VisionAnalysis analyze_vision(const InputType& data)
125 {
126 return analyze_vision(input_type { data });
127 }
128
129 /**
130 * @brief Get the last VisionAnalysis result.
131 */
133 {
134 return safe_any_cast_or_throw<VisionAnalysis>(this->get_current_analysis());
135 }
136
137 /**
138 * @brief Replace the pipeline and reset inter-frame executor state.
139 *
140 * Not thread-safe relative to analyze_implementation. Call only when idle.
141 */
143 {
144 m_sequence = std::move(sequence);
146 }
147
148 /**
149 * @brief Clear stored optical flow state.
150 *
151 * Call when the pixel source changes (camera switch, video seek).
152 */
153 void reset() { m_executor.reset(); }
154
155 [[nodiscard]] AnalysisType get_analysis_type() const override
156 {
158 }
159
160 [[nodiscard]] std::vector<std::string> get_available_methods() const override
161 {
162 return { "default" };
163 }
164
165protected:
166 [[nodiscard]] std::string get_analyzer_name() const override
167 {
168 return "VisionAnalyzer";
169 }
170
172 {
173 try {
174 auto [native_spans, structure_info] = OperationHelper::extract_structured_native(
175 const_cast<input_type&>(input));
176
177 if (native_spans.empty()) {
178 error<std::runtime_error>(
181 std::source_location::current(),
182 "VisionAnalyzer: no pixel data in input");
183 }
184
185 uint32_t w = 0;
186 uint32_t h = 0;
187 for (const auto& dim : structure_info.dimensions) {
189 w = static_cast<uint32_t>(dim.size);
190 } else if (dim.role == Kakshya::DataDimension::Role::SPATIAL_Y) {
191 h = static_cast<uint32_t>(dim.size);
192 }
193 }
194 if (w == 0)
195 w = Kakshya::get_metadata_value<uint32_t>(input.metadata, "width").value_or(0);
196 if (h == 0)
197 h = Kakshya::get_metadata_value<uint32_t>(input.metadata, "height").value_or(0);
198
199 if (w == 0 || h == 0) {
200 error<std::runtime_error>(
203 std::source_location::current(),
204 "VisionAnalyzer: width/height not resolvable from dimensions or metadata");
205 }
206
207 auto frame = std::visit([&](const auto& span) -> std::span<const float> {
208 using ElemT = typename std::decay_t<decltype(span)>::value_type;
209 if constexpr (std::is_same_v<ElemT, float>) {
210 return span;
211 } else {
212 using VecT = std::vector<ElemT>;
213 VecT tmp(span.begin(), span.end());
214 Kakshya::DataVariant var(std::move(tmp));
216 }
217 },
218 native_spans[0]);
219
220 if (frame.empty()) {
221 error<std::runtime_error>(
224 std::source_location::current(),
225 "VisionAnalyzer: normalisation produced empty frame");
226 }
227
229
230 VisionAnalysis analysis;
231 if (const auto* v = std::get_if<std::vector<float>>(&vr.pixel_image))
232 analysis.pixel_image = *v;
233
234 analysis.w = vr.w;
235 analysis.h = vr.h;
236
237 std::visit([&](const auto& s) {
238 using T = std::decay_t<decltype(s)>;
239 if constexpr (std::is_same_v<T, Kinesis::Vision::GradientResult>) {
240 analysis.frame.gradient = s;
241 } else if constexpr (std::is_same_v<T, Kinesis::Vision::ComponentResult>) {
242 analysis.frame.components = s;
243 analysis.frame.boxes = s.boxes;
244 } else if constexpr (std::is_same_v<T, std::vector<Kinesis::Vision::Contour>>) {
245 analysis.frame.contours = s;
246 } else if constexpr (std::is_same_v<T, std::vector<Kinesis::Vision::Keypoint>>) {
247 analysis.frame.keypoints = s;
248 } else if constexpr (std::is_same_v<T, std::vector<Kinesis::Vision::TrackResult>>) {
249 analysis.frame.tracks = s;
250 }
251 },
252 vr.structured);
253
254 this->store_current_analysis(analysis);
255
256 return create_pipeline_output(input, vr, structure_info);
257
258 } catch (const std::exception& e) {
260 "VisionAnalyzer: {}", e.what());
261 output_type err;
262 err.metadata = input.metadata;
263 err.metadata["error"] = std::string(e.what());
264 return err;
265 }
266 }
267
268private:
271 mutable std::vector<float> m_float_storage;
272
274 const input_type& input,
276 const DataStructureInfo& info)
277 {
278 std::vector<Kakshya::DataVariant> out_variants;
279 const auto* pix = std::get_if<std::vector<float>>(&vr.pixel_image);
280 if (pix && !pix->empty())
281 out_variants.emplace_back(*pix);
282
283 output_type out = this->convert_result(
284 std::vector<std::vector<double>> {}, info);
285 out.data = OperationHelper::reconstruct_from_double<OutputType>({}, info);
286
287 if constexpr (std::is_same_v<OutputType, std::vector<Kakshya::DataVariant>>) {
288 out.data = std::move(out_variants);
289 }
290
291 out.dimensions = {
293 };
295 out.metadata = input.metadata;
296 out.metadata["source_analyzer"] = std::string("VisionAnalyzer");
297 out.metadata["vision_w"] = vr.w;
298 out.metadata["vision_h"] = vr.h;
299 return out;
300 }
301};
302
303// ============================================================================
304// Aliases
305// ============================================================================
306
307/// Standard: DataVariant pixels in, DataVariant pixel_image out.
309 std::vector<Kakshya::DataVariant>>;
310
311/// Container: any SignalSourceContainer in, DataVariant pixel_image out.
313 std::vector<Kakshya::DataVariant>>;
314
315/// Region: pixel sub-region with container in, DataVariant pixel_image out.
317 std::vector<Kakshya::DataVariant>>;
318
319} // namespace MayaFlux::Yantra
#define MF_ERROR(comp, ctx,...)
Core::GlobalInputConfig input
Definition Config.cpp:38
uint32_t h
Definition InkPress.cpp:28
Modern, digital-first universal analyzer framework for Maya Flux.
Dispatch engine for VisionSequence execution.
Declarative description of a Kinesis::Vision processing sequence.
void reset()
Clear stored inter-frame state.
VisionResult run(const VisionSequence &sequence, std::span< const float > frame, uint32_t w, uint32_t h)
Execute a VisionSequence on one frame.
Stateful executor for a VisionSequence.
output_type convert_result(std::vector< std::vector< double > > &result_data, DataStructureInfo &metadata)
Convert processed double data back to OutputType using metadata and optional callback.
static std::tuple< std::vector< Kakshya::DataSpanVariant >, DataStructureInfo > extract_structured_native(T &compute_data)
Extract native-typed channel spans and structure metadata from a Datum or direct ComputeData,...
std::any get_current_analysis() const
Access cached analysis from last operation.
std::any analyze_data(const input_type &data)
User-facing analysis method - returns analysis results directly.
void store_current_analysis(AnalysisResultType &&result) const
Template-flexible analyzer base with instance-defined I/O types.
void reset()
Clear stored optical flow state.
output_type analyze_implementation(const input_type &input) override
Pure virtual analysis implementation - derived classes implement this.
void set_sequence(Kinesis::Vision::VisionSequence sequence)
Replace the pipeline and reset inter-frame executor state.
VisionAnalyzer(Kinesis::Vision::VisionSequence sequence)
Construct with the VisionSequence to execute each call.
output_type create_pipeline_output(const input_type &input, const Kinesis::Vision::VisionResult &vr, const DataStructureInfo &info)
std::vector< std::string > get_available_methods() const override
Get available analysis methods for this analyzer.
Kinesis::Vision::VisionExecutor m_executor
VisionAnalysis analyze_vision(const input_type &data)
Type-safe vision analysis.
VisionAnalysis get_vision_analysis() const
Get the last VisionAnalysis result.
VisionAnalysis analyze_vision(const InputType &data)
AnalysisType get_analysis_type() const override
Gets the analysis type category for this analyzer.
Kinesis::Vision::VisionSequence m_sequence
std::string get_analyzer_name() const override
Get analyzer-specific name (derived classes override this)
UniversalAnalyzer that takes any pixel-bearing input, runs a VisionSequence via VisionExecutor,...
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
std::span< const float > as_normalised_float(const DataVariant &variant, std::vector< float > &storage)
Extract a DataVariant holding pixel data as a normalised float span.
Definition DataUtils.cpp:61
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:102
@ IMAGE_2D
2D image (grayscale or single channel)
AnalysisType
Categories of analysis operations for discovery and organization.
@ SPATIAL
Multi-dimensional geometric analysis.
double extract_scalar_vision(const VisionAnalysis &analysis, const std::string &qualifier)
Extract a named scalar from a VisionAnalysis result.
static DataDimension spatial_2d(uint64_t width, uint64_t height)
Convenience constructor for a 2D spatial dimension.
Definition NDData.cpp:85
@ SPATIAL_X
Spatial X axis (images, tensors)
Represents a point or span in N-dimensional space.
Definition Region.hpp:73
Result of connected component labelling.
Gradient maps produced by Sobel and Scharr operators.
Definition Gradient.hpp:22
Result of executing a VisionSequence on one frame.
Ordered sequence of VisionSteps describing a complete vision pipeline.
Definition VisionOp.hpp:163
Metadata about data structure for reconstruction.
T data
The actual computation data.
Definition DataIO.hpp:25
std::vector< Kakshya::DataDimension > dimensions
Data dimensional structure.
Definition DataIO.hpp:26
Kakshya::DataModality modality
Data modality (audio, image, spectral, etc.)
Definition DataIO.hpp:27
std::unordered_map< std::string, std::any > metadata
Associated metadata.
Definition DataIO.hpp:28
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:24
Kinesis::Vision::ComponentResult components
std::vector< Kinesis::Vision::Keypoint > keypoints
std::vector< Kinesis::Vision::Contour > contours
std::vector< Kinesis::Vision::BoundingBox > boxes
Kinesis::Vision::GradientResult gradient
std::vector< Kinesis::Vision::TrackResult > tracks
Flattened structured outputs from one VisionResult.
Analysis result produced by VisionAnalyzer for one frame.