21 VisionIO run_chain(
const std::shared_ptr<VisionMatrix>& matrix,
VisionIO frame)
23 auto chain = matrix->with(std::move(frame)).then<VisionAnalyzer>(
"analyze");
24 if (matrix->extractor())
25 return chain.then<VisionExtractor>(
"extract").to_io();
29 using FrameResults = std::shared_ptr<Vruta::BroadcastSource<VisionIO>>;
34 std::string run_name(std::string_view
kind)
36 static uint64_t next {};
37 return std::format(
"vision_{}_{}",
kind, next++);
43 void stop_producer(
const std::weak_ptr<Vruta::TaskScheduler>& weak_scheduler,
const std::string&
name)
45 const auto scheduler = weak_scheduler.lock();
49 if (
const auto producer = scheduler->get_task(
name))
50 scheduler->cancel_task(producer);
53 std::shared_ptr<Core::VKImage> extracted_image(
const VisionIO& result)
55 const auto image = Kakshya::get_metadata_value<std::shared_ptr<Core::VKImage>>(
56 result.metadata,
"vision_extraction");
65 const std::shared_ptr<Kakshya::SignalSourceContainer>& source,
66 const Kinesis::Vision::VisionAnalysis& analysis,
67 const std::shared_ptr<Core::VKImage>&
image)
71 frame.metadata[
"vision_analysis"] = analysis;
72 frame.metadata[
"vision_image"] =
image;
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)
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);
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)
103 return [weak_source = std::weak_ptr(source), scheduler, events, consumer,
name]() {
104 const auto stream = weak_source.lock();
108 stop_run(scheduler, events, consumer,
name);
118 std::shared_ptr<Core::VKImage> hold_copy(
119 Portal::Graphics::ImageCacheEntry& cache,
120 const std::shared_ptr<Core::VKImage>&
image)
126 auto held = loom.acquire_cached_image(cache, {
127 .width =
image->get_width(),
129 .format = Portal::Graphics::ImageFormat::RGBA32F,
134 if (!held || !loom.can_blit(
image, held, filter) || !loom.blit_layer(
image, held, { .filter = filter }))
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)
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);
161 Kriya::frame_results<VisionIO>(scheduler, results,
162 [analyzer, frames, apply_to, source, ended, held, companion, mid_run]() -> std::optional<VisionIO> {
166 const auto image = frames->get_gpu_texture();
171 *companion = hold_copy(*held, apply_to->get_gpu_texture());
175 const auto analysis = analyzer->advance(
image);
180 const auto target = apply_to ? *companion : analyzer->get_frame();
183 return analyzed_frame(source, *analysis, target);
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)
207 [weak_results = std::weak_ptr(results), analyzer, frames, apply_to, source, ended]() {
210 const auto target = weak_results.lock();
211 const auto image = frames->get_gpu_texture();
212 if (!target || !
image)
215 const auto applied = apply_to ? apply_to->get_gpu_texture() :
image;
218 target->signal(analyzed_frame(source, analyzer->analyze_vision(
image), applied));
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)
242 const std::shared_ptr<Kakshya::SignalSourceContainer> container =
source;
243 const auto& analyzer = matrix->analyzer();
245 if (interval_seconds > 0.0) {
246 interval_results(scheduler, results, analyzer, frames, apply_to, container, interval_seconds, ended,
name);
248 fit_results(scheduler, results, analyzer, frames, apply_to, container, ended,
name);
257 const std::shared_ptr<VisionMatrix>& matrix,
260 return extracted_image(matrix->with(frame).then<VisionExtractor>(
"extract").to_io());
267 std::function<void(
const VisionIO&)> deliver_to(
268 const std::shared_ptr<VisionMatrix>& matrix,
269 const std::shared_ptr<Buffers::TextureBuffer>& output)
271 return [matrix, weak_output = std::weak_ptr(output)](
const VisionIO& frame) {
272 const auto target = weak_output.lock();
277 target->set_gpu_texture(
image);
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)
292 return [matrix, weak_collection = std::weak_ptr(collection), scheduler,
name](
const VisionIO& frame) {
293 const auto target = weak_collection.lock();
295 stop_producer(scheduler,
name);
300 target->append(
image);
302 if (target->is_full())
303 stop_producer(scheduler,
name);
312 std::shared_ptr<Kakshya::TextureCollection> record_frames(
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)
320 spec.ring_layers = 0;
322 auto collection = std::make_shared<Kakshya::TextureCollection>(spec);
324 for (
size_t i = 0; i <
count; ++i) {
325 const auto image = frame_at(i);
331 if (
const auto extracted = extracted_image(run_chain(matrix, std::move(frame))))
332 collection->append(extracted);
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)
367 if (
const auto event = consumer.lock();
event && events)
368 events->cancel_event(event);
375 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
376 std::string_view caller)
const
380 "VisionWorkflow::{}: no video stream", caller);
387 "VisionWorkflow::{}: the engine's managers are gone", caller);
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);
395 input = io->hook_camera_to_buffer(camera);
397 input = io->get_video_buffer(source);
399 input = io->hook_video_container_to_buffer(source);
404 "VisionWorkflow::{}: the video stream could not be hooked to a buffer", caller);
413 std::erase_if(
m_runs, [](
const auto&
run) {
return run.second.expired(); });
424 const std::shared_ptr<VisionMatrix>& matrix,
425 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
427 const std::optional<LiveConfig>& live_config)
431 "VisionWorkflow::process_to_buffer: no matrix");
435 const auto frames =
source_frames(source,
"process_to_buffer");
437 if (!frames || !buffers)
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();
449 start(source, frames, live, matrix, run_name(
"buffer"), deliver_to(matrix, output));
460 const std::shared_ptr<VisionMatrix>& matrix,
461 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
463 const std::optional<LiveConfig>& live_config)
467 "VisionWorkflow::process_to_live_container: no matrix");
471 const auto frames =
source_frames(source,
"process_to_live_container");
475 const auto live = live_config.value_or(
LiveConfig {});
476 const auto& apply_to = live.apply_to;
479 spec.
width = apply_to ? apply_to->get_width() : source->get_width();
480 spec.
height = apply_to ? apply_to->get_height() : source->get_height();
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",
491 const auto name = run_name(
"container");
500 "VisionWorkflow::process_to_stream: no matrix");
501 return {
nullptr,
nullptr };
506 if (!frames || !buffers)
507 return {
nullptr,
nullptr };
513 const auto name = run_name(
"stream");
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)
521 matrix->extract(*analysis, *image);
523 for (const auto& [collection, pick] : record) {
524 if (!collection || !pick)
526 if (const auto picked = pick(*matrix))
527 collection->append(picked);
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)
534 if (chimera_pipeline->is_running()) {
535 *seen_running =
true;
538 return *seen_running;
541 return { std::move(array), std::move(pipeline) };
544void VisionWorkflow::start(
545 const std::shared_ptr<Kakshya::VideoStreamContainer>& source,
546 const std::shared_ptr<Buffers::TextureBuffer>& frames,
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)
553 auto results = std::make_shared<Vruta::BroadcastSource<VisionIO>>();
554 auto event =
Kriya::subscribe(*m_event_manager.lock(), results, std::move(consumer));
557 auto ended = stop_at_end(source, m_scheduler, m_event_manager, event,
name);
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]() {
565 stop_run(scheduler, events, weak_consumer,
name);
574std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_container(
575 const std::vector<std::shared_ptr<Core::VKImage>>& frames,
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");
587 spec.
width = (*first)->get_width();
588 spec.
height = (*first)->get_height();
591 return record_frames(frames.size(),
592 [&frames](
size_t i) { return frames[i]; },
593 nullptr, VisionMatrix::create(query, extract), spec);
596std::shared_ptr<Kakshya::TextureCollection> VisionWorkflow::process_to_container(
597 const std::shared_ptr<Kakshya::TextureContainer>& frames,
604 "VisionWorkflow::process_to_container: no frames");
609 spec.
width = frames->get_width();
610 spec.
height = frames->get_height();
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);
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
Core::GlobalStreamInfo stream
Core::GlobalInputConfig input
BufferProcessingChain * chain
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.
static TextureLoom & instance()
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)
@ 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.
@ 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.
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.
Input/Output container for computation pipeline data flow with structure preservation.
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
Kinesis::Vision::VisionQuery query
VisionExtractMode extract
std::optional< LiveConfig > live
Portal::Graphics::RenderConfig render
What VisionWorkflow::process_to_buffer() runs: the stream to analyze, the query and extraction,...
Kinesis::Vision::VisionQuery query
VisionExtractMode extract
Kakshya::TextureCollectionSpec collection
std::shared_ptr< Kakshya::VideoStreamContainer > source
std::optional< LiveConfig > live
What VisionWorkflow::process_to_live_container() runs: the stream to analyze, the query and extractio...
Kakshya::VideoStreamSpec layers
std::vector< std::pair< std::shared_ptr< Kakshya::TextureCollection >, MatrixImage > > record
std::optional< LiveConfig > live
std::shared_ptr< Kakshya::VideoStreamContainer > source
std::shared_ptr< VisionMatrix > matrix
std::optional< Portal::Graphics::FitMode > fit
What VisionWorkflow::process_to_stream() runs: the stream to analyze, the matrix that analyzes it,...