MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionQuery.cpp
Go to the documentation of this file.
1#include "VisionQuery.hpp"
2
4
5namespace {
6
7 VisionSequence find_elements_chain(const FindElementsParams& r)
8 {
9 auto components = r.components;
10 components.export_boxes = true;
11
12 VisionSequence::Builder builder;
13 switch (r.segmentation.space) {
15 builder.rgba_to_gray();
16 break;
18 builder.rgba_to_hsv();
19 break;
21 break;
22 }
23
24 std::visit([&builder](const auto& method) {
25 using T = std::decay_t<decltype(method)>;
26 if constexpr (std::is_same_v<T, OtsuParams>) {
27 builder.threshold_otsu(method);
28 } else if constexpr (std::is_same_v<T, ThresholdAdaptiveParams>) {
29 builder.threshold_adaptive(method);
30 } else {
31 builder.threshold(method);
32 }
33 },
34 r.segmentation.method);
35
36 return builder
37 .connected_components(components)
38 .find_contours(r.contours)
39 .build();
40 }
41
42 VisionSequence track_objects_chain(const TrackObjectsParams& r)
43 {
44 auto harris = r.harris;
45 auto peaks = r.peaks;
46 harris.region = r.region;
47 peaks.region = r.region;
48
49 return VisionSequence::Builder {}
50 .rgba_to_gray()
51 .harris_response(harris)
52 .extract_peaks(peaks)
53 .track_keypoints(r.track)
54 .build();
55 }
56
57 VisionSequence detect_features_chain(const DetectFeaturesParams& r)
58 {
59 auto harris = r.harris;
60 auto peaks = r.peaks;
61 harris.region = r.region;
62 peaks.region = r.region;
63
64 return VisionSequence::Builder {}
65 .rgba_to_gray()
66 .harris_response(harris)
67 .extract_peaks(peaks)
68 .build();
69 }
70
71 VisionSequence detect_edges_chain(const DetectEdgesParams& r)
72 {
73 return VisionSequence::Builder {}
74 .rgba_to_gray()
75 .canny(r.canny)
76 .build();
77 }
78
79 VisionSequence estimate_motion_chain(const EstimateMotionParams& r)
80 {
81 return VisionSequence::Builder {}
82 .rgba_to_gray()
83 .optical_flow_dense(r.flow)
84 .build();
85 }
86
87 VisionSequence measure_appearance_chain(const MeasureAppearanceParams&)
88 {
89 return VisionSequence::Builder {}
90 .rgba_to_gray()
91 .sobel()
92 .build();
93 }
94
95 /** @brief VisionResult field an op's output can be handed off through, or nullptr. */
96 SeedAccessor seed_accessor_for(VisionOp op)
97 {
98 switch (op) {
100 return [](const VisionResult& r) { return r.gray; };
102 return [](const VisionResult& r) { return r.images.rgba_to_hsv; };
104 return [](const VisionResult& r) { return r.images.confine; };
106 return [](const VisionResult& r) { return r.images.harris_response; };
107 default:
108 return nullptr;
109 }
110 }
111
112 /**
113 * @brief True when an op writes the executor's shared output images.
114 *
115 * Those images rotate between two per size, so anything a later op of this
116 * kind writes can land on an image an earlier step handed out. Only the
117 * ops below run on other contexts.
118 */
119 bool overwrites_shared_images(VisionOp op)
120 {
121 switch (op) {
126 return false;
127 default:
128 return true;
129 }
130 }
131
132 /** @brief True when no step from index from onward overwrites the shared output images. */
133 bool preserves_shared_images(const std::vector<VisionStep>& steps, size_t from)
134 {
135 return std::none_of(steps.begin() + static_cast<std::ptrdiff_t>(std::min(from, steps.size())), steps.end(),
136 [](const VisionStep& s) { return overwrites_shared_images(s.op); });
137 }
138
139 /**
140 * @brief Leading steps identical to reference's own, up to the first
141 * divergence or unseedable step.
142 *
143 * Sharing hands a later sequence the image the reference produced at the
144 * end of the prefix, so it is only allowed when that image survives:
145 * neither sequence may run a step that overwrites the shared images after
146 * the prefix, or the seed would already be gone. Otherwise nothing is
147 * shared and the candidate recomputes its own prefix.
148 */
149 size_t shared_prefix_length(
150 const std::vector<VisionStep>& reference,
151 const std::vector<VisionStep>& candidate)
152 {
153 size_t n = 0;
154 while (n < reference.size() && n < candidate.size()) {
155 if (!seed_accessor_for(candidate[n].op))
156 break;
157 if (hash_vision_step(reference[n].op, reference[n].params)
158 != hash_vision_step(candidate[n].op, candidate[n].params))
159 break;
160 ++n;
161 }
162
163 if (n > 0 && !(preserves_shared_images(reference, n) && preserves_shared_images(candidate, n)))
164 return 0;
165 return n;
166 }
167
168} // namespace
169
170std::vector<ResolvedSequence> resolve(const VisionQuery& query)
171{
172 std::vector<VisionStep> flow_lane;
173 std::vector<std::vector<VisionStep>> other_chains;
174
175 auto append_to_flow_lane = [&](VisionSequence chain) {
176 auto steps = std::move(chain.steps);
177 if (!flow_lane.empty() && !steps.empty() && steps.front().op == VisionOp::RgbaToGray)
178 steps.erase(steps.begin());
179 flow_lane.insert(flow_lane.end(),
180 std::make_move_iterator(steps.begin()),
181 std::make_move_iterator(steps.end()));
182 };
183
184 if (has_flag(query.intents, VisionIntent::TrackObjects) && query.track_objects)
185 append_to_flow_lane(track_objects_chain(*query.track_objects));
186
187 if (has_flag(query.intents, VisionIntent::EstimateMotion) && query.estimate_motion)
188 append_to_flow_lane(estimate_motion_chain(*query.estimate_motion));
189
190 if (has_flag(query.intents, VisionIntent::FindElements) && query.find_elements)
191 other_chains.push_back(find_elements_chain(*query.find_elements).steps);
192
193 if (has_flag(query.intents, VisionIntent::DetectFeatures) && query.detect_features)
194 other_chains.push_back(detect_features_chain(*query.detect_features).steps);
195
196 if (has_flag(query.intents, VisionIntent::MeasureAppearance) && query.measure_appearance)
197 other_chains.push_back(measure_appearance_chain(*query.measure_appearance).steps);
198
199 if (has_flag(query.intents, VisionIntent::DetectEdges) && query.detect_edges)
200 other_chains.push_back(detect_edges_chain(*query.detect_edges).steps);
201
202 std::vector<ResolvedSequence> sequences;
203 sequences.reserve(1 + other_chains.size());
204 std::optional<size_t> reference_index;
205
206 if (!flow_lane.empty()) {
207 sequences.push_back({ .sequence = VisionSequence { .steps = std::move(flow_lane) } });
208 reference_index = 0;
209 }
210
211 std::vector<ResolvedSequence> seeded;
212 std::vector<ResolvedSequence> unseeded;
213
214 for (auto& steps : other_chains) {
215 if (!reference_index) {
216 sequences.push_back({ .sequence = VisionSequence { .steps = std::move(steps) } });
217 reference_index = 0;
218 continue;
219 }
220
221 const auto& reference = sequences[*reference_index].sequence.steps;
222 ResolvedSequence resolved;
223 const size_t shared = shared_prefix_length(reference, steps);
224 if (shared > 0) {
225 resolved.seed_lane = *reference_index;
226 resolved.seed_field = seed_accessor_for(reference[shared - 1].op);
227 steps.erase(steps.begin(), steps.begin() + static_cast<std::ptrdiff_t>(shared));
228 }
229 resolved.sequence = VisionSequence { .steps = std::move(steps) };
230 (resolved.seed_field ? seeded : unseeded).push_back(std::move(resolved));
231 }
232
233 sequences.insert(sequences.end(), std::make_move_iterator(seeded.begin()), std::make_move_iterator(seeded.end()));
234 sequences.insert(sequences.end(), std::make_move_iterator(unseeded.begin()), std::make_move_iterator(unseeded.end()));
235
236 return sequences;
237}
238
239} // namespace MayaFlux::Kinesis::Vision
BufferProcessingChain * chain
Per-intent parameters and resolution into a set of VisionSequences.
@ DetectFeatures
Distinctive points with no persistence claim (HarrisResponse into ExtractPeaks alone).
@ EstimateMotion
Dense, whole-field motion with no identity claim (OpticalFlowDense).
@ DetectEdges
Boundary/gradient detection (Sobel, Scharr, Canny).
@ MeasureAppearance
Photometric/statistical characterization of the frame itself, independent of anything found in it.
@ TrackObjects
Maintain identity of specific points across frames (ExtractPeaks into TrackKeypoints).
@ FindElements
Localize discrete things in the frame (thresholding/morphology into ConnectedComponents/FindContours)...
size_t hash_vision_step(VisionOp op, const VisionParams &params)
Hash a VisionStep's op and parameters together.
Definition VisionOp.hpp:621
VisionOp
Named operations available in a VisionSequence.
Definition VisionOp.hpp:30
std::vector< ResolvedSequence > resolve(const VisionQuery &query)
Resolve a VisionQuery into the ResolvedSequences needed to satisfy every active intent.
std::shared_ptr< Core::VKImage >(*)(const VisionResult &) SeedAccessor
Picks a VisionResult field to seed a later sequence's image argument with, instead of the query's own...
One VisionSequence to run, and where its image argument comes from.
std::optional< MeasureAppearanceParams > measure_appearance
std::optional< TrackObjectsParams > track_objects
std::optional< FindElementsParams > find_elements
std::optional< EstimateMotionParams > estimate_motion
std::optional< DetectEdgesParams > detect_edges
std::optional< DetectFeaturesParams > detect_features
Composed query: which VisionIntents are active, and each active one's parameters.
std::shared_ptr< Core::VKImage > gray
Output of a RgbaToGray step in this sequence, or null.
Ordered VisionSteps and the backend chosen to execute them.
Definition VisionOp.hpp:381