MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionAnalyzer.cpp
Go to the documentation of this file.
1#include "VisionAnalyzer.hpp"
2
8
9namespace MayaFlux::Yantra {
10
11namespace {
12
13 struct TrackReducePC {
14 uint32_t count;
15 };
16
17
18 struct HistogramPC {
19 uint32_t width;
20 uint32_t height;
21 uint32_t channels;
22 };
23
24 struct RegionSamplePC {
25 uint32_t px, py, pw, ph;
26 };
27
28 [[nodiscard]] bool inside(const Kinesis::Vision::BoundingBox& box, const glm::vec2& p)
29 {
30 return p.x >= box.x && p.x <= box.x + box.w
31 && p.y >= box.y && p.y <= box.y + box.h;
32 }
33
34 /** @brief Route one VisionResult into its matching VisionAnalysis field. */
35 void collect_into(
36 std::optional<std::vector<Kinesis::Vision::TrackResult>>& raw_tracks,
37 std::optional<std::vector<Kinesis::Vision::Keypoint>>& raw_keypoints,
38 Kinesis::Vision::VisionAnalysis& analysis,
39 const Kinesis::Vision::VisionResult& result)
40 {
41 if (const auto* tracks = std::get_if<std::vector<Kinesis::Vision::TrackResult>>(&result.structured))
42 raw_tracks = *tracks;
43
44 if (const auto* keypoints = std::get_if<std::vector<Kinesis::Vision::Keypoint>>(&result.structured))
45 raw_keypoints = *keypoints;
46
47 if (const auto* contours = std::get_if<std::vector<Kinesis::Vision::Contour>>(&result.structured)) {
48 analysis.find_elements = Kinesis::Vision::FindElementsAnalysis {
49 .contours = *contours,
50 .boxes = result.component_boxes,
51 };
52 }
53
54 if (result.flow) {
55 analysis.estimate_motion = result.flow;
56 analysis.motion_activity = result.motion_energy;
57 }
58
59 if (result.images.canny)
60 analysis.detect_edges = result.images.canny;
61 }
62
63} // namespace
64
68 : Base([this](const input_type& input) { return run_operation(input); })
69 , m_query(query)
70 , m_context(context)
71{
72}
73
75{
76 for (const auto& executor : m_executors) {
77 if (executor)
78 executor->reset();
79 }
80 m_run = {};
81 m_results = std::make_shared<const std::vector<Kinesis::Vision::VisionResult>>();
82}
83
85{
87 return m_executors[m_label_lane].get();
88 return m_executors.empty() ? nullptr : m_executors.front().get();
89}
90
92 const std::vector<Kinesis::Vision::TrackResult>& tracks, uint32_t w, uint32_t h)
93{
95 result.tracks = tracks;
96
97 if (tracks.empty())
98 return result;
99
100 std::vector<float> track_data;
101 track_data.reserve(tracks.size() * 4);
102 std::vector<float> extra_data;
103 extra_data.reserve(tracks.size() * 2);
104 for (const auto& t : tracks) {
105 track_data.push_back(t.position.x);
106 track_data.push_back(t.position.y);
107 track_data.push_back(t.previous.x);
108 track_data.push_back(t.previous.y);
109 extra_data.push_back(static_cast<float>(t.age));
110 extra_data.push_back(t.tracked ? 1.0F : 0.0F);
111 }
112
113 if (!m_track_reducer) {
114 m_track_reducer = std::make_shared<ShaderExecutionContext<>>(
116 .shader_path = "track_reduce.comp.spv",
117 .workgroup_size = { 256, 1, 1 },
118 .push_constant_size = sizeof(TrackReducePC) });
119 }
120
121 m_track_reducer->input(0, track_data)
122 .output(1, 3 * sizeof(glm::vec4))
123 .input(2, extra_data)
124 .push(TrackReducePC { .count = static_cast<uint32_t>(tracks.size()) });
125
126 const auto output = m_track_reducer->execute(Datum<std::vector<Kakshya::DataVariant>> {}, ExecutionContext {});
127 const auto raw = ShaderExecutionContext<>::read_output<float>(output, 1);
128
129 if (raw.size() >= 12) {
130 const auto n = static_cast<float>(tracks.size());
131 result.centroid = { raw[0] / n, raw[1] / n };
132 result.velocity = { raw[2] / n, raw[3] / n };
133 result.bounds = {
134 .x = raw[4],
135 .y = raw[5],
136 .w = raw[6] - raw[4],
137 .h = raw[7] - raw[5],
138 };
139 result.mean_speed_px = glm::length(glm::vec2 {
140 result.velocity.x * static_cast<float>(w),
141 result.velocity.y * static_cast<float>(h) });
142 result.mean_age = raw[8] / n;
143 result.max_age = static_cast<uint32_t>(std::lround(raw[9]));
144 result.tracked_count = static_cast<uint32_t>(std::lround(raw[10]));
145 result.lost_count = static_cast<uint32_t>(tracks.size()) - result.tracked_count;
146 }
147
148 return result;
149}
150
152 const std::vector<Kinesis::Vision::Keypoint>& keypoints)
153{
155 result.keypoints = keypoints;
156
157 if (keypoints.empty())
158 return result;
159
160 std::vector<float> point_data;
161 point_data.reserve(keypoints.size() * 4);
162 std::vector<float> extra_data;
163 extra_data.reserve(keypoints.size() * 2);
164 for (const auto& k : keypoints) {
165 point_data.push_back(k.position.x);
166 point_data.push_back(k.position.y);
167 point_data.push_back(k.position.x);
168 point_data.push_back(k.position.y);
169 extra_data.push_back(k.response);
170 extra_data.push_back(0.0F);
171 }
172
173 if (!m_track_reducer) {
174 m_track_reducer = std::make_shared<ShaderExecutionContext<>>(
176 .shader_path = "track_reduce.comp.spv",
177 .workgroup_size = { 256, 1, 1 },
178 .push_constant_size = sizeof(TrackReducePC) });
179 }
180
181 m_track_reducer->input(0, point_data)
182 .output(1, 3 * sizeof(glm::vec4))
183 .input(2, extra_data)
184 .push(TrackReducePC { .count = static_cast<uint32_t>(keypoints.size()) });
185
186 const auto output = m_track_reducer->execute(Datum<std::vector<Kakshya::DataVariant>> {}, ExecutionContext {});
187 const auto raw = ShaderExecutionContext<>::read_output<float>(output, 1);
188
189 if (raw.size() >= 12) {
190 const auto n = static_cast<float>(keypoints.size());
191 result.centroid = { raw[0] / n, raw[1] / n };
192 result.mean_response = raw[8] / n;
193 result.bounds = {
194 .x = raw[4],
195 .y = raw[5],
196 .w = raw[6] - raw[4],
197 .h = raw[7] - raw[5],
198 };
199 }
200
201 return result;
202}
203
205{
206 const auto n = fe.boxes.size();
207 fe.shapes.assign(n, {});
208 fe.nearest_neighbor_distance_px.assign(n, 0.0F);
209 if (n == 0)
210 return;
211
212 const auto fw = static_cast<float>(w);
213 const auto fh = static_cast<float>(h);
214
215 std::vector<float> element_data;
216 element_data.reserve(n * 4);
217 std::vector<float> centroid_data;
218 centroid_data.reserve(n * 2);
219
220 for (const auto& b : fe.boxes) {
221 const auto it = std::ranges::find_if(fe.contours,
222 [&](const auto& c) { return c.label_id == b.label_id; });
223 const float area_px = (it != fe.contours.end()) ? it->area * fw * fh : 0.0F;
224 const float perimeter_px = (it != fe.contours.end()) ? it->perimeter : 0.0F;
225 element_data.push_back(area_px);
226 element_data.push_back(perimeter_px);
227 element_data.push_back(b.w * fw);
228 element_data.push_back(b.h * fh);
229
230 centroid_data.push_back((b.x + b.w * 0.5F) * fw);
231 centroid_data.push_back((b.y + b.h * 0.5F) * fh);
232 }
233
234 if (!m_shape_ctx) {
235 m_shape_ctx = std::make_shared<ShaderExecutionContext<>>(
237 .shader_path = "element_shape.comp.spv",
238 .workgroup_size = { 256, 1, 1 },
239 .push_constant_size = sizeof(TrackReducePC) });
240 }
241 m_shape_ctx->input(0, element_data)
242 .output(1, n * sizeof(glm::vec2))
243 .push(TrackReducePC { .count = static_cast<uint32_t>(n) });
244
245 const auto shape_output = m_shape_ctx->execute(Datum<std::vector<Kakshya::DataVariant>> {}, ExecutionContext {});
246 const auto shape_raw = ShaderExecutionContext<>::read_output<float>(shape_output, 1);
247 for (size_t i = 0; i < n && (i + 1) * 2 <= shape_raw.size(); ++i) {
248 fe.shapes[i].compactness = shape_raw[i * 2];
249 fe.shapes[i].aspect_ratio = shape_raw[i * 2 + 1];
250 }
251
252 if (n < 2)
253 return;
254
255 if (!m_neighbor_ctx) {
256 m_neighbor_ctx = std::make_shared<ShaderExecutionContext<>>(
258 .shader_path = "nearest_neighbor.comp.spv",
259 .workgroup_size = { 256, 1, 1 },
260 .push_constant_size = sizeof(TrackReducePC) });
261 }
262 m_neighbor_ctx->input(0, centroid_data)
263 .output(1, n * sizeof(float))
264 .push(TrackReducePC { .count = static_cast<uint32_t>(n) });
265
266 const auto dist_output = m_neighbor_ctx->execute(Datum<std::vector<Kakshya::DataVariant>> {}, ExecutionContext {});
267 const auto dist_raw = ShaderExecutionContext<>::read_output<float>(dist_output, 1);
268 for (size_t i = 0; i < n && i < dist_raw.size(); ++i)
269 fe.nearest_neighbor_distance_px[i] = dist_raw[i];
270}
271
273 const Kinesis::Vision::VisionResult& result,
274 const std::shared_ptr<Core::VKImage>& gray,
275 uint32_t w, uint32_t h)
276{
278
279 if (result.images.sobel)
281
282 if (gray) {
283 out.histogram = compute_histogram(gray, w, h);
284 out.mean_brightness = compute_mean_brightness(out.histogram);
285 }
286
287 return out;
288}
289
291 const std::shared_ptr<Core::VKImage>& image, uint32_t w, uint32_t h)
292{
294 m_gradient_sample_ctx = std::make_shared<TextureExecutionContext>(
296 .shader_path = "region_sample.comp.spv",
297 .workgroup_size = { 256, 1, 1 },
298 .push_constant_size = sizeof(RegionSamplePC) },
299 Portal::Graphics::ImageFormat::RGBA8,
301 1,
302 std::vector<GpuBufferBinding> {
303 { .set = 0, .binding = 2, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 } });
304 }
305
306 auto& foundry = Portal::Graphics::get_shader_foundry();
307
308 m_gradient_sample_ctx->stage_image(image);
309 m_gradient_sample_ctx->set_push_constants(RegionSamplePC { .px = 0, .py = 0, .pw = w, .ph = h });
310 m_gradient_sample_ctx->set_output_size(2, 3 * sizeof(glm::vec4));
311 m_gradient_sample_ctx->set_output_dimensions(1, 1);
312
313 const auto fence = m_gradient_sample_ctx->dispatch_async({});
314 m_gradient_sample_ctx->clear_output_dimensions();
315 foundry.wait_for_fence(fence);
316 foundry.release_fence(fence);
317
318 const auto gpu_result = m_gradient_sample_ctx->collect_result();
319
321 if (auto it = gpu_result.aux.find(2); it != gpu_result.aux.end() && it->second.size() >= 3 * sizeof(glm::vec4)) {
322 std::array<glm::vec4, 3> raw {};
323 std::memcpy(raw.data(), it->second.data(), sizeof(raw));
324 out.mean = raw[0];
325 out.min_val = raw[1];
326 out.max_val = raw[2];
327 }
328 return out;
329}
330
331std::array<uint32_t, 256> VisionAnalyzer::compute_histogram(
332 const std::shared_ptr<Core::VKImage>& image, uint32_t w, uint32_t h)
333{
334 if (!m_histogram_ctx) {
335 m_histogram_ctx = std::make_shared<TextureExecutionContext>(
337 .shader_path = "otsu_histogram.comp.spv",
338 .workgroup_size = { 8, 8, 1 },
339 .push_constant_size = sizeof(HistogramPC) },
340 Portal::Graphics::ImageFormat::RGBA8,
342 1,
343 std::vector<GpuBufferBinding> {
344 { .set = 0, .binding = 3, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 } },
346 }
347
348 m_histogram_ctx->ensure_shared_buffer(0, 3, 768, GpuBufferBinding::ElementType::UINT32,
350
351 const std::array<uint32_t, 768> zero {};
352 m_histogram_ctx->upload_shared_raw(0, 3, reinterpret_cast<const uint8_t*>(zero.data()), sizeof(zero));
353
354 m_histogram_ctx->stage_image(image);
355 m_histogram_ctx->set_push_constants(HistogramPC { .width = w, .height = h, .channels = 0 });
356 m_histogram_ctx->set_output_dimensions(w, h);
357
358 auto& foundry = Portal::Graphics::get_shader_foundry();
359 const auto fence = m_histogram_ctx->dispatch_async({});
360 m_histogram_ctx->clear_output_dimensions();
361 foundry.wait_for_fence(fence);
362 foundry.release_fence(fence);
363
364 std::array<uint32_t, 256> histogram {};
365 m_histogram_ctx->download_shared(0, 3, histogram.data(), sizeof(histogram));
366 return histogram;
367}
368
369float VisionAnalyzer::compute_mean_brightness(const std::array<uint32_t, 256>& histogram)
370{
372 m_brightness_reducer = std::make_shared<ShaderExecutionContext<>>(
374 .shader_path = "histogram_reduce.comp.spv",
375 .workgroup_size = { 256, 1, 1 },
376 .push_constant_size = 0 });
377 }
378
379 const std::vector<uint32_t> hist_vec(histogram.begin(), histogram.end());
381 .output(1, sizeof(float));
382
383 const auto output = m_brightness_reducer->execute(Datum<std::vector<Kakshya::DataVariant>> {}, ExecutionContext {});
384 const auto raw = ShaderExecutionContext<>::read_output<float>(output, 1);
385 return raw.empty() ? 0.0F : raw[0];
386}
387
388std::shared_ptr<Core::VKImage> VisionAnalyzer::resolve_image(
389 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
390 size_t index)
391{
392 if (!source)
393 return nullptr;
394
395 if (auto tc = std::dynamic_pointer_cast<Kakshya::TextureContainer>(source))
396 return tc->to_image(static_cast<uint32_t>(index));
397
398 if (auto wc = std::dynamic_pointer_cast<Kakshya::WindowContainer>(source))
399 return wc->to_image();
400
401 auto vc = std::dynamic_pointer_cast<Kakshya::VideoStreamContainer>(source);
402 if (!vc)
403 return nullptr;
404
405 const uint32_t w = vc->get_width();
406 const uint32_t h = vc->get_height();
407
408 const void* raw = source->get_raw_data();
409 if (!raw)
410 return nullptr;
411
412 if (!m_upload_image || m_upload_w != w || m_upload_h != h) {
414 w, h, Portal::Graphics::ImageFormat::RGBA8, nullptr);
415 m_upload_w = w;
416 m_upload_h = h;
417 }
418 if (!m_upload_image)
419 return nullptr;
420
422 return m_upload_image;
423}
424
426 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
427 size_t index)
428{
429 const auto image = resolve_image(source, index);
430 if (!image)
431 return {};
432
433 return analyze_resolved(image);
434}
435
437 const std::shared_ptr<Core::VKImage>& image)
438{
439 return analyze_resolved(image);
440}
441
442std::optional<Kinesis::Vision::VisionAnalysis> VisionAnalyzer::advance(
443 const std::shared_ptr<Core::VKImage>& image)
444{
445 if (!m_run.active) {
446 if (!image)
447 return std::nullopt;
448
450 if (!m_frame)
451 return std::nullopt;
452
454 for (auto& entry : m_run.resolved) {
455 for (auto& step : entry.sequence.steps) {
458 step.deferred = true;
459 }
460 }
461
462 m_run.results.clear();
463 m_run.results.reserve(m_run.resolved.size());
464 m_run.w = m_frame->get_width();
465 m_run.h = m_frame->get_height();
466 m_run.active = true;
467 m_label_lane = 0;
468 }
469
470 const size_t lane = m_run.results.size();
471 if (lane < m_run.resolved.size() && !run_lane(lane, m_run.resolved, m_run.results, m_frame))
472 return std::nullopt;
473
474 if (m_run.results.size() < m_run.resolved.size())
475 return std::nullopt;
476
477 m_run.active = false;
478 auto analysis = assemble(m_run.resolved, m_run.results, m_run.w, m_run.h);
479 m_results = std::make_shared<const std::vector<Kinesis::Vision::VisionResult>>(std::move(m_run.results));
480 return analysis;
481}
482
483std::shared_ptr<Core::VKImage> VisionAnalyzer::hold_frame(const std::shared_ptr<Core::VKImage>& image)
484{
486 auto held = loom.acquire_cached_image(m_frame_cache, {
487 .width = image->get_width(),
488 .height = image->get_height(),
489 .format = Portal::Graphics::ImageFormat::RGBA32F,
491 });
492
493 constexpr auto filter = Portal::Graphics::FilterMode::NEAREST;
494 if (!held || !loom.can_blit(image, held, filter) || !loom.blit_layer(image, held, { .filter = filter }))
495 return nullptr;
496 return held;
497}
498
500 const std::shared_ptr<Core::VKImage>& image)
501{
502 const auto resolved = Kinesis::Vision::resolve(m_query);
503
504 std::vector<Kinesis::Vision::VisionResult> results;
505 results.reserve(resolved.size());
506 m_label_lane = 0;
507
508 for (size_t lane = 0; lane < resolved.size(); ++lane) {
509 if (!run_lane(lane, resolved, results, image))
510 results.emplace_back();
511 }
512
513 auto analysis = assemble(resolved, results, image->get_width(), image->get_height());
514 m_results = std::make_shared<const std::vector<Kinesis::Vision::VisionResult>>(std::move(results));
515 return analysis;
516}
517
519 size_t lane,
520 const std::vector<Kinesis::Vision::ResolvedSequence>& resolved,
521 std::vector<Kinesis::Vision::VisionResult>& results,
522 const std::shared_ptr<Core::VKImage>& image)
523{
524 if (m_executors.size() < resolved.size())
525 m_executors.resize(resolved.size());
526
527 const auto& entry = resolved[lane];
528 auto& executor = m_executors[lane];
529 if (!executor)
530 executor = std::make_unique<VisionGpuExecutor>();
531
532 if (std::ranges::any_of(entry.sequence.steps,
533 [](const auto& step) { return step.op == Kinesis::Vision::VisionOp::ConnectedComponents; }))
534 m_label_lane = lane;
535
536 const auto seeded = entry.seed_field ? entry.seed_field(results[entry.seed_lane]) : nullptr;
537 const auto& source = seeded ? seeded : image;
538 auto result = executor->run(entry.sequence, source, source->get_width(), source->get_height());
539
540 if (result.status != Kinesis::Vision::VisionStatus::COMPLETE)
541 return false;
542
543 results.push_back(std::move(result));
544 return true;
545}
546
548 const std::vector<Kinesis::Vision::ResolvedSequence>& resolved,
549 const std::vector<Kinesis::Vision::VisionResult>& results,
550 uint32_t w, uint32_t h)
551{
553 std::optional<std::vector<Kinesis::Vision::TrackResult>> raw_tracks;
554 std::optional<std::vector<Kinesis::Vision::Keypoint>> raw_keypoints;
555
556 for (size_t lane = 0; lane < results.size(); ++lane) {
557 const auto& result = results[lane];
558 const auto& entry = resolved[lane];
559
560 collect_into(raw_tracks, raw_keypoints, analysis, result);
561
562 if (result.images.sobel) {
563 const auto& gray = result.gray || !entry.seed_field ? result.gray : results[entry.seed_lane].gray;
564 analysis.measure_appearance = measure_appearance(result, gray, w, h);
565 }
566 }
567
568 if (raw_tracks && m_context.track_objects) {
569 const auto& ctx = *m_context.track_objects;
570 if (ctx.subject_track_id) {
571 const auto id = *ctx.subject_track_id;
572 std::erase_if(*raw_tracks, [&](const auto& t) { return t.id != id; });
573 } else if (ctx.subject_bounds) {
574 const auto& bounds = *ctx.subject_bounds;
575 std::erase_if(*raw_tracks, [&](const auto& t) { return !inside(bounds, t.position); });
576 }
577 }
578
579 if (raw_tracks)
580 analysis.track_objects = reduce_tracks(*raw_tracks, w, h);
581
582 if (raw_keypoints && m_context.detect_features_bounds) {
583 const auto& bounds = *m_context.detect_features_bounds;
584 std::erase_if(*raw_keypoints, [&](const auto& k) { return !inside(bounds, k.position); });
585 }
586
587 if (raw_keypoints)
588 analysis.detect_features = reduce_keypoints(*raw_keypoints);
589
590 if (analysis.find_elements)
591 compute_shapes(*analysis.find_elements, w, h);
592
593 return analysis;
594}
595
597{
598 output_type output;
599 output.metadata = input.metadata;
600
601 if (const auto image = Kakshya::get_metadata_value<std::shared_ptr<Core::VKImage>>(input.metadata, "vision_image");
602 image && *image) {
603 output.metadata["vision_analysis"] = analyze_vision(*image);
604 return output;
605 }
606
607 if (!input.data) {
608 output.metadata["error"] = std::string("VisionAnalyzer: missing container");
609 return output;
610 }
611
612 const auto index = Kakshya::get_metadata_value<size_t>(input.metadata, "container_index").value_or(0);
613
614 output.metadata["vision_analysis"] = analyze_vision(input.data, index);
615 return output;
616}
617
618} // namespace MayaFlux::Yantra
vk::Fence fence
Core::GlobalInputConfig input
Definition Config.cpp:42
IO::ImageData image
Definition Decoder.cpp:47
uint32_t h
Definition InkPress.cpp:29
size_t b
std::vector< float > * out
uint32_t index
Definition VKDevice.cpp:122
size_t count
uint32_t channels
uint32_t ph
uint32_t py
uint32_t pw
uint32_t px
FunctionalOperation<shared_ptr<SignalSourceContainer>, vector<DataVariant>> whose real API matches En...
uint32_t width
uint32_t height
void upload_data(const std::shared_ptr< Core::VKImage > &image, const void *data, size_t size)
Upload pixel data to an existing texture.
std::shared_ptr< Core::VKImage > create_2d(uint32_t width, uint32_t height, ImageFormat format=ImageFormat::RGBA8, const void *data=nullptr, uint32_t mip_levels=1)
Create a 2D texture.
Concrete GpuExecutionContext for a single fixed shader with fixed bindings.
@ SCALAR
Return SSBO readback via collect_result(); no image download.
Kinesis::Vision::MeasureAppearanceAnalysis measure_appearance(const Kinesis::Vision::VisionResult &result, const std::shared_ptr< Core::VKImage > &gray, uint32_t w, uint32_t h)
MeasureAppearance's whole-frame gradient sample (region_sample.comp over result.images....
float compute_mean_brightness(const std::array< uint32_t, 256 > &histogram)
Dispatch histogram_reduce.comp over an already-computed histogram.
std::shared_ptr< Core::VKImage > m_upload_image
std::shared_ptr< ShaderExecutionContext<> > m_shape_ctx
Kinesis::Vision::VisionQuery m_query
Kinesis::Vision::FieldSample sample_full_frame(const std::shared_ptr< Core::VKImage > &image, uint32_t w, uint32_t h)
Dispatch region_sample.comp over the whole image (no region concept for MeasureAppearance).
void compute_shapes(Kinesis::Vision::FindElementsAnalysis &fe, uint32_t w, uint32_t h)
Shape descriptors (element_shape.comp) and nearest-neighbour distances (nearest_neighbor....
std::shared_ptr< Core::VKImage > m_frame
Kinesis::Vision::VisionAnalysis analyze_resolved(const std::shared_ptr< Core::VKImage > &image)
Shared body of both analyze_vision() overloads, once a GPU image is in hand: resolve the configured q...
bool run_lane(size_t lane, const std::vector< Kinesis::Vision::ResolvedSequence > &resolved, std::vector< Kinesis::Vision::VisionResult > &results, const std::shared_ptr< Core::VKImage > &image)
Run or poll sequence lane of resolved on its executor.
std::shared_ptr< Core::VKImage > hold_frame(const std::shared_ptr< Core::VKImage > &image)
Copy image into this analyzer's own frame image.
Portal::Graphics::ImageCacheEntry m_frame_cache
Kinesis::Vision::DetectFeaturesAnalysis reduce_keypoints(const std::vector< Kinesis::Vision::Keypoint > &keypoints)
Dispatch track_reduce.comp over keypoint positions (fed as their own previous, so velocity reduces to...
VisionAnalyzer(Kinesis::Vision::VisionQuery query={}, Kinesis::Vision::VisionAnalysisContext context={})
Construct with the intents to satisfy and the caller-supplied filtering context, matching EnergyAnaly...
std::vector< std::unique_ptr< VisionGpuExecutor > > m_executors
std::shared_ptr< TextureExecutionContext > m_histogram_ctx
std::shared_ptr< ShaderExecutionContext<> > m_neighbor_ctx
std::shared_ptr< const std::vector< Kinesis::Vision::VisionResult > > m_results
void reset()
Abandon outstanding work and clear retained executor state.
VisionGpuExecutor * get_executor() const
The executor that ran the FindElements sequence, or the first executor when the query has none.
std::optional< Kinesis::Vision::VisionAnalysis > advance(const std::shared_ptr< Core::VKImage > &image)
Advance an analysis of image by one sequence per call, without waiting on deferred GPU work.
std::shared_ptr< ShaderExecutionContext<> > m_brightness_reducer
Kinesis::Vision::VisionAnalysisContext m_context
Kinesis::Vision::VisionAnalysis assemble(const std::vector< Kinesis::Vision::ResolvedSequence > &resolved, const std::vector< Kinesis::Vision::VisionResult > &results, uint32_t w, uint32_t h)
Route every completed sequence result into one VisionAnalysis, apply context filtering and run the GP...
Kinesis::Vision::TrackObjectsAnalysis reduce_tracks(const std::vector< Kinesis::Vision::TrackResult > &tracks, uint32_t w, uint32_t h)
Dispatch track_reduce.comp over tracks and fill centroid/velocity/bounds/mean_speed_px/age stats on t...
std::shared_ptr< Core::VKImage > resolve_image(const std::shared_ptr< Kakshya::SignalSourceContainer > &source, size_t index)
Resolve source/index to a GPU image, the same way VisionProcessor does: TextureContainer's own to_ima...
Kinesis::Vision::VisionAnalysis analyze_vision(const std::shared_ptr< Kakshya::SignalSourceContainer > &source, size_t index=0)
Real entry point: resolve the configured query against source, run it, run the GPU analysis.
std::shared_ptr< TextureExecutionContext > m_gradient_sample_ctx
std::shared_ptr< ShaderExecutionContext<> > m_track_reducer
std::array< uint32_t, 256 > compute_histogram(const std::shared_ptr< Core::VKImage > &image, uint32_t w, uint32_t h)
Dispatch otsu_histogram.comp over image, zeroing the buffer first.
output_type run_operation(const input_type &input)
ComputeOperation adapter: uses input.data as the source container, pulls index out of Datum metadata ...
Stateful GPU dispatch engine for VisionSequence execution.
std::optional< T > get_metadata_value(const std::unordered_map< std::string, std::any > &metadata, const std::string &key)
Get a value from a metadata map by key.
std::vector< ResolvedSequence > resolve(const VisionQuery &query)
Resolve a VisionQuery into the ResolvedSequences needed to satisfy every active intent.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ NEAREST
Nearest neighbor (pixelated)
@ COMPUTE
Storage buffer for compute shaders (device-local)
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...
std::shared_ptr< Core::VKImage > sobel
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...
std::optional< TrackObjectsContext > track_objects
std::optional< BoundingBox > detect_features_bounds
Composed context: caller-supplied data for whichever intents need it, consulted after a VisionQuery's...
std::optional< FindElementsAnalysis > find_elements
std::optional< MeasureAppearanceAnalysis > measure_appearance
std::optional< DetectFeaturesAnalysis > detect_features
std::optional< TrackObjectsAnalysis > track_objects
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.
ImageOutputs images
Image output of whichever image-producing ops ran in this sequence, one named slot per op.
Result of executing a VisionSequence on one frame.
enum MayaFlux::Portal::Graphics::GpuBufferBinding::ElementType FLOAT32
Plain-data description of the compute shader to dispatch.
std::unordered_map< std::string, std::any > metadata
Associated metadata.
Definition DataIO.hpp:30
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:26
Context information controlling how a compute operation executes.
std::vector< Kinesis::Vision::ResolvedSequence > resolved
std::vector< Kinesis::Vision::VisionResult > results