MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RegionUtils.cpp
Go to the documentation of this file.
1#include "RegionUtils.hpp"
2#include "DataUtils.hpp"
3
4namespace MayaFlux::Kakshya {
5
6void set_region_attribute(Region& region, const std::string& key, std::any value)
7{
8 region.attributes[key] = std::move(value);
9}
10
11/* std::string get_region_label(const Region& region)
12{
13 auto label = get_region_attribute<std::string>(region, "label");
14 return label.value_or("");
15}
16
17void set_region_label(Region& region, const std::string& label)
18{
19 set_region_attribute(region, "label", label);
20} */
21
22std::vector<Region> find_regions_with_label(const RegionGroup& group, const std::string& label)
23{
24 std::vector<Region> result;
25 std::ranges::copy_if(group.regions, std::back_inserter(result),
26 [&label](const Region& region) {
27 return region.get_label() == label;
28 });
29 return result;
30}
31
32std::vector<Region> find_regions_with_attribute(const RegionGroup& group, const std::string& key, const std::any& value)
33{
34 std::vector<Region> result;
35 std::ranges::copy_if(group.regions, std::back_inserter(result),
36 [&key, &value](const Region& region) {
37 if (!region.attributes.contains(key))
38 return false;
39
40 const auto& attr = region.attributes.at(key);
41
42 if (attr.type() != value.type())
43 return false;
44 if (attr.type() == typeid(std::string))
45 return any_equal<std::string>(attr, value);
46 if (attr.type() == typeid(double))
47 return any_equal<double>(attr, value);
48 if (attr.type() == typeid(int))
49 return any_equal<int>(attr, value);
50 if (attr.type() == typeid(float))
51 return any_equal<float>(attr, value);
52 if (attr.type() == typeid(bool))
53 return any_equal<bool>(attr, value);
54 if (attr.type() == typeid(uint32_t))
55 return any_equal<uint32_t>(attr, value);
56 if (attr.type() == typeid(int64_t))
57 return any_equal<int64_t>(attr, value);
58 if (attr.type() == typeid(uint64_t))
59 return any_equal<uint64_t>(attr, value);
60 return false;
61 });
62
63 return result;
64}
65
66std::vector<Region> find_regions_containing_coordinates(const RegionGroup& group, const std::vector<uint64_t>& coordinates)
67{
68 std::vector<Region> result;
69 std::ranges::copy_if(group.regions, std::back_inserter(result),
70 [&coordinates](const Region& region) {
71 return region.contains(coordinates);
72 });
73 return result;
74}
75
76Region translate_region(const Region& region, const std::vector<int64_t>& offset)
77{
78 Region result = region;
79 for (size_t i = 0; i < std::min(offset.size(), region.start_coordinates.size()); ++i) {
80 result.start_coordinates[i] = static_cast<uint64_t>(static_cast<int64_t>(result.start_coordinates[i]) + offset[i]);
81 result.end_coordinates[i] = static_cast<uint64_t>(static_cast<int64_t>(result.end_coordinates[i]) + offset[i]);
82 }
83 return result;
84}
85
86Region scale_region(const Region& region, const std::vector<double>& factors)
87{
88 Region result = region;
89 for (size_t i = 0; i < std::min(factors.size(), region.start_coordinates.size()); ++i) {
90 uint64_t center = (region.start_coordinates[i] + region.end_coordinates[i]) / 2;
91 uint64_t half_span = (region.end_coordinates[i] - region.start_coordinates[i]) / 2;
92 auto new_half_span = static_cast<uint64_t>(factors[i] * static_cast<double>(half_span));
93 result.start_coordinates[i] = center - new_half_span;
94 result.end_coordinates[i] = center + new_half_span;
95 }
96 return result;
97}
98
100{
101 if (group.regions.empty())
102 return Region {};
103 auto min_coords = group.regions.front().start_coordinates;
104 auto max_coords = group.regions.front().end_coordinates;
105
106 for (const auto& region : group.regions) {
107 for (size_t i = 0; i < min_coords.size() && i < region.start_coordinates.size(); ++i) {
108 min_coords[i] = std::min(min_coords[i], region.start_coordinates[i]);
109 }
110 for (size_t i = 0; i < max_coords.size() && i < region.end_coordinates.size(); ++i) {
111 max_coords[i] = std::max(max_coords[i], region.end_coordinates[i]);
112 }
113 }
114
115 Region bounds(min_coords, max_coords);
116 set_region_attribute(bounds, "type", std::string("bounding_box"));
117 return bounds;
118}
119
120void sort_regions_by_dimension(std::vector<Region>& regions, size_t dimension)
121{
122 std::ranges::sort(regions,
123 [dimension](const Region& a, const Region& b) {
124 if (dimension < a.start_coordinates.size() && dimension < b.start_coordinates.size())
125 return a.start_coordinates[dimension] < b.start_coordinates[dimension];
126 return false;
127 });
128}
129
130void sort_regions_by_attribute(std::vector<Region>& regions, const std::string& attr_name)
131{
132 std::ranges::sort(regions,
133 [&attr_name](const Region& a, const Region& b) {
134 auto aval = get_region_attribute<std::string>(a, attr_name);
135 auto bval = get_region_attribute<std::string>(b, attr_name);
136 return aval.value_or("") < bval.value_or("");
137 });
138}
139
140void add_reference_region(std::vector<std::pair<std::string, Region>>& refs, const std::string& name, const Region& region)
141{
142 refs.emplace_back(name, region);
143}
144
145void remove_reference_region(std::vector<std::pair<std::string, Region>>& refs, const std::string& name)
146{
147 auto to_remove = std::ranges::remove_if(refs,
148 [&name](const auto& pair) { return pair.first == name; });
149 refs.erase(to_remove.begin(), to_remove.end());
150}
151
152std::optional<Region> get_reference_region(const std::vector<std::pair<std::string, Region>>& refs, const std::string& name)
153{
154 auto it = std::ranges::find_if(refs,
155 [&name](const auto& pair) { return pair.first == name; });
156 if (it != refs.end())
157 return it->second;
158 return std::nullopt;
159}
160
161std::vector<std::pair<std::string, Region>> find_references_in_region(
162 const std::vector<std::pair<std::string, Region>>& refs, const Region& region)
163{
164 std::vector<std::pair<std::string, Region>> result;
165
166 std::ranges::copy_if(refs, std::back_inserter(result),
167 [&region](const auto& pair) { return region.contains(pair.second.start_coordinates); });
168 return result;
169}
170
171void add_region_group(std::unordered_map<std::string, RegionGroup>& groups, const RegionGroup& group)
172{
173 groups[group.name] = group;
174}
175
176std::optional<RegionGroup> get_region_group(const std::unordered_map<std::string, RegionGroup>& groups, const std::string& name)
177{
178 auto it = groups.find(name);
179 if (it != groups.end())
180 return it->second;
181 return std::nullopt;
182}
183
184void remove_region_group(std::unordered_map<std::string, RegionGroup>& groups, const std::string& name)
185{
186 groups.erase(name);
187}
188
189Region calculate_output_region(const std::vector<uint64_t>& current_pos,
190 const std::vector<uint64_t>& output_shape)
191{
192 if (current_pos.size() != output_shape.size()) {
193 error<std::invalid_argument>(
195 std::source_location::current(),
196 "Position and shape vectors must have same size: current_pos size = "
197 + std::to_string(current_pos.size()) + ", output_shape size = "
198 + std::to_string(output_shape.size()));
199 }
200
201 std::vector<uint64_t> end_pos;
202 end_pos.reserve(current_pos.size());
203
204 for (size_t i = 0; i < current_pos.size(); ++i) {
205 if (output_shape[i] == 0) {
206 error<std::invalid_argument>(
208 std::source_location::current(),
209 "Output shape cannot have zero dimensions: dimension " + std::to_string(i) + " is zero");
210 }
211 end_pos.push_back(current_pos[i] + output_shape[i] - 1);
212 }
213
214 return { current_pos, end_pos };
215}
216
217Region calculate_output_region(uint64_t current_frame,
218 uint64_t frames_to_process,
219 const std::shared_ptr<SignalSourceContainer>& container)
220{
221 const auto& structure = container->get_structure();
222 uint64_t total_frames = structure.get_samples_count_per_channel();
223 uint64_t num_channels = structure.get_channel_count();
224
225 if (current_frame >= total_frames) {
226 error<std::out_of_range>(
228 std::source_location::current(),
229 "Current frame exceeds container bounds: current_frame = "
230 + std::to_string(current_frame) + ", total_frames = "
231 + std::to_string(total_frames));
232 }
233
234 uint64_t available_frames = total_frames - current_frame;
235 uint64_t actual_frames = std::min(frames_to_process, available_frames);
236
237 Region output_shape;
238 output_shape.start_coordinates = { current_frame, 0 };
239 output_shape.end_coordinates = { current_frame + actual_frames - 1, num_channels - 1 };
240
241 return output_shape;
242}
243
245 const std::shared_ptr<SignalSourceContainer>& container)
246{
247 if (!container) {
248 return false;
249 }
250
251 const auto dimensions = container->get_dimensions();
252 const auto memory_layout = container->get_memory_layout();
253
254 // For row-major layout, contiguous access means the last dimension spans its full range
255 // For column-major layout, contiguous access means the first dimension spans its full range
256
257 if (memory_layout == MemoryLayout::ROW_MAJOR) {
258 if (!dimensions.empty() && !region.start_coordinates.empty() && !region.end_coordinates.empty()) {
259 size_t last_dim = dimensions.size() - 1;
260 return (region.start_coordinates[last_dim] == 0 && region.end_coordinates[last_dim] == dimensions[last_dim].size - 1);
261 }
262 } else if (memory_layout == MemoryLayout::COLUMN_MAJOR) {
263 if (!dimensions.empty() && !region.start_coordinates.empty() && !region.end_coordinates.empty()) {
264 return (region.start_coordinates[0] == 0 && region.end_coordinates[0] == dimensions[0].size - 1);
265 }
266 }
267
268 return false;
269}
270
271std::vector<std::unordered_map<std::string, std::any>> extract_all_regions_info(const std::shared_ptr<SignalSourceContainer>& container)
272{
273 if (!container) {
274 error<std::invalid_argument>(
276 std::source_location::current(),
277 "Container is null");
278 }
279
280 auto all_groups = container->get_all_region_groups();
281 std::vector<std::unordered_map<std::string, std::any>> regions_info;
282
283 for (const auto& [group_name, group] : all_groups) {
284 for (size_t i = 0; i < group.regions.size(); ++i) {
285 const auto& region = group.regions[i];
286
287 std::unordered_map<std::string, std::any> region_info;
288 region_info["group_name"] = group_name;
289 region_info["region_index"] = i;
290 region_info["start_coordinates"] = region.start_coordinates;
291 region_info["end_coordinates"] = region.end_coordinates;
292 region_info["attributes"] = region.attributes;
293
294 regions_info.push_back(std::move(region_info));
295 }
296 }
297
298 return regions_info;
299}
300
301std::unordered_map<std::string, std::any> extract_group_bounds_info(const RegionGroup& group)
302{
303 std::unordered_map<std::string, std::any> bounds_info;
304
305 if (group.regions.empty()) {
306 return bounds_info;
307 }
308
309 const auto& first_region = group.regions[0];
310 std::vector<uint64_t> min_coords = first_region.start_coordinates;
311 std::vector<uint64_t> max_coords = first_region.end_coordinates;
312
313 for (size_t i = 1; i < group.regions.size(); ++i) {
314 const auto& region = group.regions[i];
315
316 for (size_t j = 0; j < min_coords.size() && j < region.start_coordinates.size(); ++j) {
317 min_coords[j] = std::min(min_coords[j], region.start_coordinates[j]);
318 }
319
320 for (size_t j = 0; j < max_coords.size() && j < region.end_coordinates.size(); ++j) {
321 max_coords[j] = std::max(max_coords[j], region.end_coordinates[j]);
322 }
323 }
324
325 bounds_info["group_name"] = group.name;
326 bounds_info["num_regions"] = group.regions.size();
327 bounds_info["bounding_min"] = min_coords;
328 bounds_info["bounding_max"] = max_coords;
329 bounds_info["group_attributes"] = group.attributes;
330
331 return bounds_info;
332}
333
334std::vector<std::unordered_map<std::string, std::any>> extract_segments_metadata(const std::vector<RegionSegment>& segments)
335{
336 std::vector<std::unordered_map<std::string, std::any>> metadata_list;
337 metadata_list.reserve(segments.size());
338
339 for (const auto& segment : segments) {
340 std::unordered_map<std::string, std::any> segment_info;
341 segment_info["start_coordinates"] = segment.source_region.start_coordinates;
342 segment_info["end_coordinates"] = segment.source_region.end_coordinates;
343 segment_info["region_attributes"] = segment.source_region.attributes;
344 segment_info["segment_attributes"] = segment.processing_metadata;
345
346 metadata_list.push_back(std::move(segment_info));
347 }
348
349 return metadata_list;
350}
351
352std::unordered_map<std::string, std::any> extract_region_bounds_info(const Region& region)
353{
354 std::unordered_map<std::string, std::any> bounds_info;
355 bounds_info["start_coordinates"] = region.start_coordinates;
356 bounds_info["end_coordinates"] = region.end_coordinates;
357
358 std::vector<uint64_t> sizes;
359 sizes.reserve(region.start_coordinates.size());
360
361 for (size_t i = 0; i < region.start_coordinates.size() && i < region.end_coordinates.size(); ++i) {
362 sizes.push_back(region.end_coordinates[i] - region.start_coordinates[i] + 1);
363 }
364 bounds_info["sizes"] = sizes;
365
366 uint64_t total_elements = 1;
367 for (uint64_t size : sizes) {
368 total_elements *= size;
369 }
370 bounds_info["total_elements"] = total_elements;
371
372 return bounds_info;
373}
374
375std::optional<size_t> find_region_for_position(const std::vector<uint64_t>& position,
376 const std::vector<Region>& regions)
377{
378 for (size_t i = 0; i < regions.size(); ++i) {
379 if (regions[i].contains(position)) {
380 return i;
381 }
382 }
383 return std::nullopt;
384}
385
386std::optional<size_t> find_region_for_position(const std::vector<uint64_t>& position, std::vector<OrganizedRegion> regions)
387{
388 for (size_t i = 0; i < regions.size(); ++i) {
389 if (regions[i].contains_position(position)) {
390 return i;
391 }
392 }
393 return std::nullopt;
394}
395
396Region remove_channel_dimension(const Region& region, const std::vector<DataDimension>& dimensions)
397{
398 Region result = region;
399
400 for (long i = 0; i < dimensions.size(); ++i) {
401 if (dimensions[i].role == DataDimension::Role::CHANNEL) {
402 result.start_coordinates.erase(result.start_coordinates.begin() + i);
403 result.end_coordinates.erase(result.end_coordinates.begin() + i);
404 break;
405 }
406 }
407
408 return result;
409}
410
411std::vector<DataDimension> get_non_channel_dimensions(const std::vector<DataDimension>& dimensions)
412{
413 std::vector<DataDimension> result;
414 std::ranges::copy_if(dimensions, std::back_inserter(result),
415 [](const DataDimension& dim) {
417 });
418 return result;
419}
420
421bool regions_intersect(const Region& r1, const Region& r2) noexcept
422{
423 const size_t ndim = std::min({ r1.start_coordinates.size(),
424 r1.end_coordinates.size(),
425 r2.start_coordinates.size(),
426 r2.end_coordinates.size() });
427 if (ndim < 2)
428 return false;
429
430 for (size_t i = 0; i < ndim; ++i) {
431 if (r1.end_coordinates[i] < r2.start_coordinates[i]
432 || r2.end_coordinates[i] < r1.start_coordinates[i])
433 return false;
434 }
435 return true;
436}
437
439 const std::vector<Region>& regions,
440 const std::shared_ptr<SignalSourceContainer>& source,
441 uint32_t num_channels,
442 const RegionTaper& taper,
443 const RegionWriteFn& write_fn)
444{
445 for (size_t ri = 0; ri < regions.size(); ++ri) {
446 auto variants = source->get_region_data(regions[ri]);
447 for (uint32_t ch = 0; ch < num_channels; ++ch) {
448 if (ch >= variants.size())
449 continue;
450 std::vector<double> storage;
451 extract_from_variant<double>(variants[ch], storage);
452 if (storage.empty())
453 continue;
454 if (taper)
455 taper(std::span<double>(storage.data(), storage.size()));
456 write_fn(ri, ch, std::span<double>(storage.data(), storage.size()));
457 }
458 }
459}
460}
Eigen::MatrixXd storage
size_t a
size_t b
Range size
glm::vec3 position
@ Runtime
General runtime operations (default fallback)
@ Kakshya
Containers[Signalsource, Stream, File], Regions, DataProcessors.
void set_region_attribute(Region &region, const std::string &key, std::any value)
Set an attribute value on a Region.
Region remove_channel_dimension(const Region &region, const std::vector< DataDimension > &dimensions)
Remove the channel dimension from a Region.
void sort_regions_by_attribute(std::vector< Region > &regions, const std::string &attr_name)
Sort a vector of Regions by a specific attribute (numeric).
std::optional< RegionGroup > get_region_group(const std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Get a RegionGroup by name from a group map.
std::vector< Region > find_regions_with_label(const RegionGroup &group, const std::string &label)
Find all regions in a RegionGroup with a given label.
bool is_region_access_contiguous(const Region &region, const std::shared_ptr< SignalSourceContainer > &container)
Check if region access will be contiguous in memory.
std::unordered_map< std::string, std::any > extract_region_bounds_info(const Region &region)
Extract structured bounds information from region.
std::function< void(std::span< double >)> RegionTaper
Per-region taper applied in-place before the write callback is invoked.
void add_reference_region(std::vector< std::pair< std::string, Region > > &refs, const std::string &name, const Region &region)
Add a named reference region to a reference list.
Region translate_region(const Region &region, const std::vector< int64_t > &offset)
Translate a Region by an offset vector.
void add_region_group(std::unordered_map< std::string, RegionGroup > &groups, const RegionGroup &group)
Add a RegionGroup to a group map.
std::function< void(size_t region_idx, uint32_t channel, std::span< double >)> RegionWriteFn
Callback invoked once per region per channel during iteration.
Region scale_region(const Region &region, const std::vector< double > &factors)
Scale a Region about its center by the given factors.
void iterate_region_channels(const std::vector< Region > &regions, const std::shared_ptr< SignalSourceContainer > &source, uint32_t num_channels, const RegionTaper &taper, const RegionWriteFn &write_fn)
Iterate over a set of regions, extracting per-channel samples and dispatching them to a write callbac...
@ ROW_MAJOR
C/C++ style (last dimension varies fastest)
@ COLUMN_MAJOR
Fortran/MATLAB style (first dimension varies fastest)
bool regions_intersect(const Region &r1, const Region &r2) noexcept
Test whether two N-dimensional regions overlap on every shared axis.
std::vector< std::unordered_map< std::string, std::any > > extract_segments_metadata(const std::vector< RegionSegment > &segments)
Extract metadata from region segments.
void remove_region_group(std::unordered_map< std::string, RegionGroup > &groups, const std::string &name)
Remove a RegionGroup by name from a group map.
std::unordered_map< std::string, std::any > extract_group_bounds_info(const RegionGroup &group)
Extract bounds information from region group.
std::vector< Region > find_regions_containing_coordinates(const RegionGroup &group, const std::vector< uint64_t > &coordinates)
Find all regions in a RegionGroup that contain the given coordinates.
void remove_reference_region(std::vector< std::pair< std::string, Region > > &refs, const std::string &name)
Remove a named reference region from a reference list.
std::vector< Region > find_regions_with_attribute(const RegionGroup &group, const std::string &key, const std::any &value)
Find all regions in a RegionGroup with a specific attribute value.
std::vector< std::unordered_map< std::string, std::any > > extract_all_regions_info(const std::shared_ptr< SignalSourceContainer > &container)
Extract all regions from container's region groups.
Region get_bounding_region(const RegionGroup &group)
Get the bounding region that contains all regions in a RegionGroup.
std::vector< std::pair< std::string, Region > > find_references_in_region(const std::vector< std::pair< std::string, Region > > &refs, const Region &region)
Find all references in a reference list that overlap a given region.
std::optional< size_t > find_region_for_position(const std::vector< uint64_t > &position, const std::vector< Region > &regions)
Find optimal region containing given position.
Region calculate_output_region(const std::vector< uint64_t > &current_pos, const std::vector< uint64_t > &output_shape)
Calculate output region bounds from current position and shape.
std::optional< Region > get_reference_region(const std::vector< std::pair< std::string, Region > > &refs, const std::string &name)
Get a named reference region from a reference list.
std::vector< DataDimension > get_non_channel_dimensions(const std::vector< DataDimension > &dimensions)
Get all non-channel dimensions from a list of dimensions.
void sort_regions_by_dimension(std::vector< Region > &regions, size_t dimension)
Sort a vector of Regions by a specific dimension.
@ CHANNEL
Parallel streams (audio channels, color channels)
Role role
Semantic hint for common operations.
Definition NDData.hpp:194
Minimal dimension descriptor focusing on structure only.
Definition NDData.hpp:142
std::unordered_map< std::string, std::any > attributes
Flexible key-value store for group-specific attributes.
std::vector< Region > regions
Collection of regions belonging to this group.
std::string name
Descriptive name of the group.
Organizes related signal regions into a categorized collection.
std::unordered_map< std::string, std::any > attributes
Flexible key-value store for region-specific attributes.
Definition Region.hpp:75
std::vector< uint64_t > end_coordinates
Ending frame index (inclusive)
Definition Region.hpp:72
std::vector< uint64_t > start_coordinates
Starting frame index (inclusive)
Definition Region.hpp:69
bool contains(const std::vector< uint64_t > &coordinates) const
Check if the given coordinates are contained within this region.
Definition Region.hpp:248
Represents a point or span in N-dimensional space.
Definition Region.hpp:67