MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionWorkflow.cpp
Go to the documentation of this file.
1#include "VisionWorkflow.hpp"
2
14
16
18
19namespace {
20
21 VisionIO run_chain(const std::shared_ptr<VisionMatrix>& matrix, VisionIO frame)
22 {
23 auto chain = matrix->with(std::move(frame)).then<VisionAnalyzer>("analyze");
24 if (matrix->extractor())
25 return chain.then<VisionExtractor>("extract").to_io();
26 return chain.to_io();
27 }
28
29 using FrameResults = std::shared_ptr<Vruta::BroadcastSource<VisionIO>>;
30
31 /**
32 * @brief A name for one live run's producer task.
33 */
34 std::string run_name(std::string_view kind)
35 {
36 static uint64_t next {};
37 return std::format("vision_{}_{}", kind, next++);
38 }
39
40 /**
41 * @brief Cancel the producer task registered as @p name.
42 */
43 void stop_producer(const std::weak_ptr<Vruta::TaskScheduler>& weak_scheduler, const std::string& name)
44 {
45 const auto scheduler = weak_scheduler.lock();
46 if (!scheduler)
47 return;
48
49 if (const auto producer = scheduler->get_task(name))
50 scheduler->cancel_task(producer);
51 }
52
53 std::shared_ptr<Core::VKImage> extracted_image(const VisionIO& result)
54 {
55 const auto image = Kakshya::get_metadata_value<std::shared_ptr<Core::VKImage>>(
56 result.metadata, "vision_extraction");
57 return image ? *image : nullptr;
58 }
59
60 /**
61 * @brief @p source carrying @p analysis and the frame @p image it was
62 * taken on.
63 */
64 VisionIO analyzed_frame(
65 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
66 const Kinesis::Vision::VisionAnalysis& analysis,
67 const std::shared_ptr<Core::VKImage>& image)
68 {
69 VisionIO frame { source };
70 frame.container = source;
71 frame.metadata["vision_analysis"] = analysis;
72 frame.metadata["vision_image"] = image;
73 return frame;
74 }
75
76 /**
77 * @brief Cancel one live run: the producer named @p name and @p consumer.
78 */
79 void stop_run(
80 const std::weak_ptr<Vruta::TaskScheduler>& scheduler,
81 const std::weak_ptr<Vruta::EventManager>& events,
82 const std::weak_ptr<Vruta::Event>& consumer,
83 const std::string& name)
84 {
85 stop_producer(scheduler, name);
86 const auto manager = events.lock();
87 if (const auto event = consumer.lock(); event && manager)
88 manager->cancel_event(event);
89 }
90
91 /**
92 * @brief Check run by the producer each tick: once @p source is at its
93 * end, cancels the producer named @p name and @p consumer, and
94 * reports true.
95 */
96 std::function<bool()> stop_at_end(
97 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
98 const std::weak_ptr<Vruta::TaskScheduler>& scheduler,
99 const std::weak_ptr<Vruta::EventManager>& events,
100 const std::weak_ptr<Vruta::Event>& consumer,
101 const std::string& name)
102 {
103 return [weak_source = std::weak_ptr(source), scheduler, events, consumer, name]() {
104 const auto stream = weak_source.lock();
105 if (stream && !stream->is_at_end())
106 return false;
107
108 stop_run(scheduler, events, consumer, name);
109 return true;
110 };
111 }
112
113 /**
114 * @brief Copy of @p image in an image @p cache keeps, so it survives the
115 * source being rewritten. Null when @p image is null or cannot be
116 * blitted.
117 */
118 std::shared_ptr<Core::VKImage> hold_copy(
119 Portal::Graphics::ImageCacheEntry& cache,
120 const std::shared_ptr<Core::VKImage>& image)
121 {
122 if (!image)
123 return nullptr;
124
126 auto held = loom.acquire_cached_image(cache, {
127 .width = image->get_width(),
128 .height = image->get_height(),
129 .format = Portal::Graphics::ImageFormat::RGBA32F,
131 });
132
133 constexpr auto filter = Portal::Graphics::FilterMode::NEAREST;
134 if (!held || !loom.can_blit(image, held, filter) || !loom.blit_layer(image, held, { .filter = filter }))
135 return nullptr;
136 return held;
137 }
138
139 /**
140 * @brief Analyses of @p frames into @p results, paced by the work:
141 * advanced one sequence per graphics frame, signalled as each
142 * completes.
143 *
144 * With @p apply_to, its frame is copied in the tick the analyzed frame
145 * is taken, and the analysis is applied to that copy.
146 */
147 void fit_results(
148 Vruta::TaskScheduler& scheduler,
149 const FrameResults& results,
150 const std::shared_ptr<VisionAnalyzer>& analyzer,
151 const std::shared_ptr<Buffers::TextureBuffer>& frames,
152 const std::shared_ptr<Buffers::TextureBuffer>& apply_to,
153 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
154 const std::function<bool()>& ended,
155 const std::string& name)
156 {
157 auto held = std::make_shared<Portal::Graphics::ImageCacheEntry>();
158 auto companion = std::make_shared<std::shared_ptr<Core::VKImage>>();
159 auto mid_run = std::make_shared<bool>(false);
160
161 Kriya::frame_results<VisionIO>(scheduler, results,
162 [analyzer, frames, apply_to, source, ended, held, companion, mid_run]() -> std::optional<VisionIO> {
163 if (ended())
164 return std::nullopt;
165
166 const auto image = frames->get_gpu_texture();
167 if (!*mid_run) {
168 if (!image)
169 return std::nullopt;
170 if (apply_to)
171 *companion = hold_copy(*held, apply_to->get_gpu_texture());
172 *mid_run = true;
173 }
174
175 const auto analysis = analyzer->advance(image);
176 if (!analysis)
177 return std::nullopt;
178 *mid_run = false;
179
180 const auto target = apply_to ? *companion : analyzer->get_frame();
181 if (!target)
182 return std::nullopt;
183 return analyzed_frame(source, *analysis, target);
184 },
185 name);
186 }
187
188 /**
189 * @brief Analyses of @p frames into @p results every @p interval_seconds
190 * on the graphics clock, each run to completion when it starts.
191 *
192 * With @p apply_to, the analysis is applied to its frame of the same
193 * tick.
194 */
195 void interval_results(
196 Vruta::TaskScheduler& scheduler,
197 const FrameResults& results,
198 const std::shared_ptr<VisionAnalyzer>& analyzer,
199 const std::shared_ptr<Buffers::TextureBuffer>& frames,
200 const std::shared_ptr<Buffers::TextureBuffer>& apply_to,
201 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
202 double interval_seconds,
203 const std::function<bool()>& ended,
204 const std::string& name)
205 {
206 scheduler.add_task(Kriya::metro(interval_seconds,
207 [weak_results = std::weak_ptr(results), analyzer, frames, apply_to, source, ended]() {
208 if (ended())
209 return;
210 const auto target = weak_results.lock();
211 const auto image = frames->get_gpu_texture();
212 if (!target || !image)
213 return;
214
215 const auto applied = apply_to ? apply_to->get_gpu_texture() : image;
216 if (!applied)
217 return;
218 target->signal(analyzed_frame(source, analyzer->analyze_vision(image), applied));
219 },
221 name);
222 }
223
224 /**
225 * @brief Producer of analyses of @p source's frames into @p results,
226 * paced by @p interval_seconds or, at zero, by the work, as a task
227 * named @p name. Results carry the image the analysis is applied
228 * to: the analyzed frame, or @p apply_to's frame of the same
229 * moment.
230 */
231 void start_producer(
232 Vruta::TaskScheduler& scheduler,
233 const FrameResults& results,
234 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
235 const std::shared_ptr<Buffers::TextureBuffer>& frames,
236 const std::shared_ptr<Buffers::TextureBuffer>& apply_to,
237 const std::shared_ptr<VisionMatrix>& matrix,
238 double interval_seconds,
239 const std::function<bool()>& ended,
240 const std::string& name)
241 {
242 const std::shared_ptr<Kakshya::SignalSourceContainer> container = source;
243 const auto& analyzer = matrix->analyzer();
244
245 if (interval_seconds > 0.0) {
246 interval_results(scheduler, results, analyzer, frames, apply_to, container, interval_seconds, ended, name);
247 } else {
248 fit_results(scheduler, results, analyzer, frames, apply_to, container, ended, name);
249 }
250 }
251
252 /**
253 * @brief What @p matrix's "extract" operation takes from @p frame, with
254 * its analysis and the image the producer chose.
255 */
256 std::shared_ptr<Core::VKImage> extract_frame(
257 const std::shared_ptr<VisionMatrix>& matrix,
258 const VisionIO& frame)
259 {
260 return extracted_image(matrix->with(frame).then<VisionExtractor>("extract").to_io());
261 }
262
263 /**
264 * @brief Consumer extracting from each analyzed frame and showing the
265 * image in @p output.
266 */
267 std::function<void(const VisionIO&)> deliver_to(
268 const std::shared_ptr<VisionMatrix>& matrix,
269 const std::shared_ptr<Buffers::TextureBuffer>& output)
270 {
271 return [matrix, weak_output = std::weak_ptr(output)](const VisionIO& frame) {
272 const auto target = weak_output.lock();
273 if (!target)
274 return;
275
276 if (const auto image = extract_frame(matrix, frame))
277 target->set_gpu_texture(image);
278 };
279 }
280
281 /**
282 * @brief Consumer extracting from each analyzed frame and appending the
283 * image to @p collection, cancelling the producer named @p name
284 * once the collection is full or released.
285 */
286 std::function<void(const VisionIO&)> record_into(
287 const std::shared_ptr<VisionMatrix>& matrix,
288 const std::shared_ptr<Kakshya::TextureCollection>& collection,
289 const std::weak_ptr<Vruta::TaskScheduler>& scheduler,
290 const std::string& name)
291 {
292 return [matrix, weak_collection = std::weak_ptr(collection), scheduler, name](const VisionIO& frame) {
293 const auto target = weak_collection.lock();
294 if (!target) {
295 stop_producer(scheduler, name);
296 return;
297 }
298
299 if (const auto image = extract_frame(matrix, frame))
300 target->append(image);
301
302 if (target->is_full())
303 stop_producer(scheduler, name);
304 };
305 }
306
307 /**
308 * @brief Analyze and extract @p count frames in order on one matrix,
309 * appending each extracted image to a collection of exactly that
310 * many layers at most.
311 */
312 std::shared_ptr<Kakshya::TextureCollection> record_frames(
313 size_t count,
314 const std::function<std::shared_ptr<Core::VKImage>(size_t)>& frame_at,
315 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
316 const std::shared_ptr<VisionMatrix>& matrix,
317 Kakshya::TextureCollectionSpec spec)
318 {
319 spec.max_bytes = 0;
320 spec.ring_layers = 0;
321
322 auto collection = std::make_shared<Kakshya::TextureCollection>(spec);
323
324 for (size_t i = 0; i < count; ++i) {
325 const auto image = frame_at(i);
326 if (!image)
327 continue;
328
329 VisionIO frame = source ? VisionIO { source, source } : VisionIO {};
330 frame.metadata["vision_image"] = image;
331 if (const auto extracted = extracted_image(run_chain(matrix, std::move(frame))))
332 collection->append(extracted);
333 }
334
335 return collection;
336 }
337
338} // namespace
339
340//=============================================================================
341// VisionWorkflow
342//=============================================================================
343
345 const std::shared_ptr<Vruta::TaskScheduler>& scheduler,
346 const std::shared_ptr<Vruta::EventManager>& event_manager,
347 const std::shared_ptr<IO::IOManager>& io_manager,
348 const std::shared_ptr<Buffers::BufferManager>& buffer_manager)
349 : m_scheduler(scheduler)
350 , m_event_manager(event_manager)
351 , m_io_manager(io_manager)
352 , m_buffer_manager(buffer_manager)
353{
354}
355
360
362{
363 const auto events = m_event_manager.lock();
364
365 for (const auto& [name, consumer] : m_runs) {
366 stop_producer(m_scheduler, name);
367 if (const auto event = consumer.lock(); event && events)
368 events->cancel_event(event);
369 }
370
371 m_runs.clear();
372}
373
374std::shared_ptr<Buffers::TextureBuffer> VisionWorkflow::source_frames(
375 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
376 std::string_view caller) const
377{
378 if (!source) {
380 "VisionWorkflow::{}: no video stream", caller);
381 return nullptr;
382 }
383
384 const auto io = m_io_manager.lock();
385 if (!io || m_scheduler.expired() || m_event_manager.expired()) {
387 "VisionWorkflow::{}: the engine's managers are gone", caller);
388 return nullptr;
389 }
390
391 std::shared_ptr<Buffers::TextureBuffer> input;
392 if (const auto camera = std::dynamic_pointer_cast<Kakshya::CameraContainer>(source)) {
393 input = io->get_camera_buffer(camera);
394 if (!input)
395 input = io->hook_camera_to_buffer(camera);
396 } else {
397 input = io->get_video_buffer(source);
398 if (!input)
399 input = io->hook_video_container_to_buffer(source);
400 }
401
402 if (!input) {
404 "VisionWorkflow::{}: the video stream could not be hooked to a buffer", caller);
405 return nullptr;
406 }
407
408 return input;
409}
410
411void VisionWorkflow::keep(const std::string& name, const std::shared_ptr<Vruta::Event>& consumer)
412{
413 std::erase_if(m_runs, [](const auto& run) { return run.second.expired(); });
414 m_runs.emplace_back(name, consumer);
415}
416
417std::shared_ptr<Buffers::TextureBuffer> VisionWorkflow::process_to_buffer(
418 const WorkflowBufferConfig& config)
419{
420 return process_to_buffer(VisionMatrix::create(config.query, config.extract), config.source, config.render, config.live);
421}
422
423std::shared_ptr<Buffers::TextureBuffer> VisionWorkflow::process_to_buffer(
424 const std::shared_ptr<VisionMatrix>& matrix,
425 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
426 const Portal::Graphics::RenderConfig& render,
427 const std::optional<LiveConfig>& live_config)
428{
429 if (!matrix) {
431 "VisionWorkflow::process_to_buffer: no matrix");
432 return nullptr;
433 }
434
435 const auto frames = source_frames(source, "process_to_buffer");
436 const auto buffers = m_buffer_manager.lock();
437 if (!frames || !buffers)
438 return nullptr;
439
440 const auto live = live_config.value_or(LiveConfig {});
441 const auto& apply_to = live.apply_to;
442 const uint32_t width = apply_to ? apply_to->get_width() : source->get_width();
443 const uint32_t height = apply_to ? apply_to->get_height() : source->get_height();
444
445 auto output = buffers->create_graphics_buffer<Buffers::TextureBuffer>(
446 Buffers::ProcessingToken::GRAPHICS_BACKEND, width, height, Portal::Graphics::ImageFormat::RGBA8);
447 output->setup_rendering(render);
448
449 start(source, frames, live, matrix, run_name("buffer"), deliver_to(matrix, output));
450 return output;
451}
452
453std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_live_container(
454 const WorkflowContainerConfig& config)
455{
456 return process_to_live_container(VisionMatrix::create(config.query, config.extract), config.source, config.collection, config.live);
457}
458
459std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_live_container(
460 const std::shared_ptr<VisionMatrix>& matrix,
461 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
463 const std::optional<LiveConfig>& live_config)
464{
465 if (!matrix) {
467 "VisionWorkflow::process_to_live_container: no matrix");
468 return nullptr;
469 }
470
471 const auto frames = source_frames(source, "process_to_live_container");
472 if (!frames)
473 return nullptr;
474
475 const auto live = live_config.value_or(LiveConfig {});
476 const auto& apply_to = live.apply_to;
477
478 if (spec.width == 0 || spec.height == 0) {
479 spec.width = apply_to ? apply_to->get_width() : source->get_width();
480 spec.height = apply_to ? apply_to->get_height() : source->get_height();
481 }
482
483 auto collection = std::make_shared<Kakshya::TextureCollection>(spec);
484 if (collection->is_full()) {
486 "VisionWorkflow::process_to_live_container: a {}-byte cap holds no {}x{} layer, nothing recorded",
487 spec.max_bytes, spec.width, spec.height);
488 return collection;
489 }
490
491 const auto name = run_name("container");
492 start(source, frames, live, matrix, name, record_into(matrix, collection, m_scheduler, name));
493 return collection;
494}
495
497{
498 if (!config.matrix) {
500 "VisionWorkflow::process_to_stream: no matrix");
501 return { nullptr, nullptr };
502 }
503
504 const auto frames = source_frames(config.source, "process_to_stream");
505 const auto buffers = m_buffer_manager.lock();
506 if (!frames || !buffers)
507 return { nullptr, nullptr };
508
509 auto array = buffers->create_graphics_buffer<Buffers::TextureArrayBuffer>(
511 auto pipeline = Kriya::BufferPipeline::create(*m_scheduler.lock(), buffers, m_io_manager.lock());
512
513 const auto name = run_name("stream");
514 start(config.source, frames, config.live.value_or(LiveConfig {}), config.matrix, name,
515 [matrix = config.matrix, record = config.record](const VisionIO& frame) {
516 const auto analysis = Kakshya::get_metadata_value<Kinesis::Vision::VisionAnalysis>(frame.metadata, "vision_analysis");
517 const auto image = Kakshya::get_metadata_value<std::shared_ptr<Core::VKImage>>(frame.metadata, "vision_image");
518 if (!analysis || !image)
519 return;
520
521 matrix->extract(*analysis, *image);
522
523 for (const auto& [collection, pick] : record) {
524 if (!collection || !pick)
525 continue;
526 if (const auto picked = pick(*matrix))
527 collection->append(picked);
528 }
529 },
530 [weak_pipeline = std::weak_ptr(pipeline), seen_running = std::make_shared<bool>(false)]() {
531 const auto chimera_pipeline = weak_pipeline.lock();
532 if (!chimera_pipeline)
533 return true;
534 if (chimera_pipeline->is_running()) {
535 *seen_running = true;
536 return false;
537 }
538 return *seen_running;
539 });
540
541 return { std::move(array), std::move(pipeline) };
542}
543
544void VisionWorkflow::start(
545 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
546 const std::shared_ptr<Buffers::TextureBuffer>& frames,
547 const LiveConfig& live,
548 const std::shared_ptr<VisionMatrix>& matrix,
549 const std::string& name,
550 std::function<void(const VisionIO&)> consumer,
551 std::function<bool()> stop_when)
552{
553 auto results = std::make_shared<Vruta::BroadcastSource<VisionIO>>();
554 auto event = Kriya::subscribe(*m_event_manager.lock(), results, std::move(consumer));
555 keep(name, event);
556
557 auto ended = stop_at_end(source, m_scheduler, m_event_manager, event, name);
558 if (stop_when) {
559 ended = [at_end = std::move(ended), stop_when = std::move(stop_when),
560 scheduler = m_scheduler, events = m_event_manager, weak_consumer = std::weak_ptr(event), name]() {
561 if (at_end())
562 return true;
563 if (!stop_when())
564 return false;
565 stop_run(scheduler, events, weak_consumer, name);
566 return true;
567 };
568 }
569
570 start_producer(*m_scheduler.lock(), results, source, frames, live.apply_to, matrix, live.interval_seconds,
571 ended, name);
572}
573
574std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_container(
575 const std::vector<std::shared_ptr<Core::VKImage>>& frames,
577 VisionExtractMode extract,
579{
580 if (spec.width == 0 || spec.height == 0) {
581 const auto first = std::ranges::find_if(frames, [](const auto& image) { return image != nullptr; });
582 if (first == frames.end()) {
584 "VisionWorkflow::process_to_container: no frames to size the collection from");
585 return nullptr;
586 }
587 spec.width = (*first)->get_width();
588 spec.height = (*first)->get_height();
589 }
590
591 return record_frames(frames.size(),
592 [&frames](size_t i) { return frames[i]; },
593 nullptr, VisionMatrix::create(query, extract), spec);
594}
595
596std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_container(
597 const std::shared_ptr<Kakshya::TextureContainer>& frames,
599 VisionExtractMode extract,
601{
602 if (!frames) {
604 "VisionWorkflow::process_to_container: no frames");
605 return nullptr;
606 }
607
608 if (spec.width == 0 || spec.height == 0) {
609 spec.width = frames->get_width();
610 spec.height = frames->get_height();
611 }
612
613 return record_frames(frames->get_layer_count(),
614 [&frames](size_t i) { return frames->to_image(static_cast<uint32_t>(i)); },
615 frames, VisionMatrix::create(query, extract), spec);
616}
617
618} // namespace MayaFlux::Yantra::Vision
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Core::GlobalStreamInfo stream
Definition Config.cpp:40
Core::GlobalInputConfig input
Definition Config.cpp:42
IO::ImageData image
Definition Decoder.cpp:47
BufferProcessingChain * chain
AttributeKind kind
std::string name
Definition VKDevice.cpp:123
size_t count
uint32_t width
uint32_t height
A TextureBuffer whose image is a stack of layers, composed in one draw.
virtual void setup_rendering(const RenderConfig &config)
Setup rendering with RenderProcessor.
A hybrid buffer managing both a textured quad geometry and its pixel data.
static std::shared_ptr< BufferPipeline > create(Vruta::TaskScheduler &scheduler, std::shared_ptr< Buffers::BufferManager > buffer_manager=nullptr, std::shared_ptr< IO::IOManager > io_manager=nullptr)
Declares the layers of a Chimera, in order, and starts feeding them.
Definition Chimera.hpp:190
static std::shared_ptr< ComputeMatrix > create()
Create a new ComputeMatrix instance.
std::shared_ptr< Buffers::TextureBuffer > source_frames(const std::shared_ptr< Kakshya::VideoStreamContainer > &source, std::string_view caller) const
void stop_all()
Stop every live run this workflow started and release what they hold.
std::shared_ptr< Kakshya::TextureCollection > process_to_live_container(const WorkflowContainerConfig &config)
Run config's query on its source live and record what its extraction takes from each frame into a gro...
std::shared_ptr< Buffers::TextureBuffer > process_to_buffer(const WorkflowBufferConfig &config)
Run config's query on its source live and show what its extraction takes from each frame in a texture...
std::weak_ptr< Vruta::EventManager > m_event_manager
~VisionWorkflow()
Stops every live run this workflow started.
void start(const std::shared_ptr< Kakshya::VideoStreamContainer > &source, const std::shared_ptr< Buffers::TextureBuffer > &frames, const LiveConfig &live, const std::shared_ptr< VisionMatrix > &matrix, const std::string &name, std::function< void(const VisionIO &)> consumer, std::function< bool()> stop_when={})
Subscribe consumer and start the producer named name, which stops the run at source's end,...
void keep(const std::string &name, const std::shared_ptr< Vruta::Event > &consumer)
std::weak_ptr< Vruta::TaskScheduler > m_scheduler
Kriya::ChimeraBuilder process_to_stream(const WorkflowStreamConfig &config)
Run config's matrix on its source live and return a Chimera builder over a new layer array,...
std::vector< std::pair< std::string, std::weak_ptr< Vruta::Event > > > m_runs
Per live run: its producer task's name and its consumer.
std::weak_ptr< Buffers::BufferManager > m_buffer_manager
std::weak_ptr< IO::IOManager > m_io_manager
VisionWorkflow(const std::shared_ptr< Vruta::TaskScheduler > &scheduler, const std::shared_ptr< Vruta::EventManager > &event_manager, const std::shared_ptr< IO::IOManager > &io_manager, const std::shared_ptr< Buffers::BufferManager > &buffer_manager)
void run()
Definition main.cpp:21
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
constexpr std::span< T > extract_frame(std::span< T > data, uint64_t frame_index, uint64_t frame_size) noexcept
Extract a single frame of data from a span.
std::shared_ptr< Vruta::Event > subscribe(Vruta::EventManager &events, std::shared_ptr< Vruta::BroadcastSource< T > > source, Callback callback, const std::string &name="")
Call callback with every value source signals, registered on events.
std::shared_ptr< Vruta::Routine > metro(double interval_seconds, std::function< void()> callback, Vruta::ProcessingToken token)
Creates a periodic event generator that executes a callback at regular intervals.
Definition Tasks.cpp:12
Source source()
Begin a Source chain.
Definition Plot.hpp:127
@ NEAREST
Nearest neighbor (pixelated)
@ FRAME_ACCURATE
Coroutine is frame-accurate.
VisionExtractMode
Which method run_operation dispatches to.
std::shared_ptr< Kriya::BufferPipeline > record_into(const std::shared_ptr< Kakshya::DynamicSoundStream > &stream, Kriya::CaptureBuilder source, uint32_t channel)
Record a signal into one channel of a stream, a block at a time.
Definition Rigs.cpp:215
Describes a TextureCollection so it can be built by designated initializer.
Composed query: which VisionIntents are active, and each active one's parameters.
Unified rendering configuration for graphics buffers.
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
std::shared_ptr< Buffers::TextureBuffer > apply_to
How a live workflow run is paced and which frames it delivers.
std::shared_ptr< Kakshya::VideoStreamContainer > source
What VisionWorkflow::process_to_buffer() runs: the stream to analyze, the query and extraction,...
std::shared_ptr< Kakshya::VideoStreamContainer > source
What VisionWorkflow::process_to_live_container() runs: the stream to analyze, the query and extractio...
std::vector< std::pair< std::shared_ptr< Kakshya::TextureCollection >, MatrixImage > > record
std::shared_ptr< Kakshya::VideoStreamContainer > source
std::optional< Portal::Graphics::FitMode > fit
What VisionWorkflow::process_to_stream() runs: the stream to analyze, the matrix that analyzes it,...