MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RegionUtils.cpp
Go to the documentation of this file.
1#include "RegionUtils.hpp"
2
3namespace MayaFlux::Kakshya {
4
5void set_region_attribute(Region& region, const std::string& key, std::any value)
6{
7 region.attributes[key] = std::move(value);
8}
9
10/* std::string get_region_label(const Region& region)
11{
12 auto label = get_region_attribute<std::string>(region, "label");
13 return label.value_or("");
14}
15
16void set_region_label(Region& region, const std::string& label)
17{
18 set_region_attribute(region, "label", label);
19} */
20
21std::vector<Region> find_regions_with_label(const RegionGroup& group, const std::string& label)
22{
23 std::vector<Region> result;
24 std::ranges::copy_if(group.regions, std::back_inserter(result),
25 [&label](const Region& region) {
26 return region.get_label() == label;
27 });
28 return result;
29}
30
31std::vector<Region> find_regions_with_attribute(const RegionGroup& group, const std::string& key, const std::any& value)
32{
33 std::vector<Region> result;
34 std::ranges::copy_if(group.regions, std::back_inserter(result),
35 [&key, &value](const Region& region) {
36 auto attr_it = region.attributes.find(key);
37 if (attr_it != region.attributes.end()) {
38 if (value.type() == typeid(std::string)) {
39 auto region_value = get_region_attribute<std::string>(region, key);
40 auto search_value = std::any_cast<std::string>(value);
41 return region_value.has_value() && *region_value == search_value;
42 }
43 if (value.type() == typeid(double)) {
44 auto region_value = get_region_attribute<double>(region, key);
45 auto search_value = std::any_cast<double>(value);
46 return region_value.has_value() && *region_value == search_value;
47 }
48 if (value.type() == typeid(int)) {
49 auto region_value = get_region_attribute<int>(region, key);
50 auto search_value = std::any_cast<int>(value);
51 return region_value.has_value() && *region_value == search_value;
52 }
53 // TODO: Add more types as needed
54 }
55 return false;
56 });
57 return result;
58}
59
60std::vector<Region> find_regions_containing_coordinates(const RegionGroup& group, const std::vector<uint64_t>& coordinates)
61{
62 std::vector<Region> result;
63 std::ranges::copy_if(group.regions, std::back_inserter(result),
64 [&coordinates](const Region& region) {
65 return region.contains(coordinates);
66 });
67 return result;
68}
69
70Region translate_region(const Region& region, const std::vector<int64_t>& offset)
71{
72 Region result = region;
73 for (size_t i = 0; i < std::min(offset.size(), region.start_coordinates.size()); ++i) {
74 result.start_coordinates[i] = static_cast<uint64_t>(static_cast<int64_t>(result.start_coordinates[i]) + offset[i]);
75 result.end_coordinates[i] = static_cast<uint64_t>(static_cast<int64_t>(result.end_coordinates[i]) + offset[i]);
76 }
77 return result;
78}
79
80Region scale_region(const Region& region, const std::vector<double>& factors)
81{
82 Region result = region;
83 for (size_t i = 0; i < std::min(factors.size(), region.start_coordinates.size()); ++i) {
84 uint64_t center = (region.start_coordinates[i] + region.end_coordinates[i]) / 2;
85 uint64_t half_span = (region.end_coordinates[i] - region.start_coordinates[i]) / 2;
86 auto new_half_span = static_cast<uint64_t>(factors[i] * static_cast<double>(half_span));
87 result.start_coordinates[i] = center - new_half_span;
88 result.end_coordinates[i] = center + new_half_span;
89 }
90 return result;
91}
92
94{
95 if (group.regions.empty())
96 return Region {};
97 auto min_coords = group.regions.front().start_coordinates;
98 auto max_coords = group.regions.front().end_coordinates;
99
100 for (const auto& region : group.regions) {
101 for (size_t i = 0; i < min_coords.size() && i < region.start_coordinates.size(); ++i) {
102 min_coords[i] = std::min(min_coords[i], region.start_coordinates[i]);
103 }
104 for (size_t i = 0; i < max_coords.size() && i < region.end_coordinates.size(); ++i) {
105 max_coords[i] = std::max(max_coords[i], region.end_coordinates[i]);
106 }
107 }
108
109 Region bounds(min_coords, max_coords);
110 set_region_attribute(bounds, "type", std::string("bounding_box"));
111 return bounds;
112}
113
114void sort_regions_by_dimension(std::vector<Region>& regions, size_t dimension)
115{
116 std::ranges::sort(regions,
117 [dimension](const Region& a, const Region& b) {
118 if (dimension < a.start_coordinates.size() && dimension < b.start_coordinates.size())
119 return a.start_coordinates[dimension] < b.start_coordinates[dimension];
120 return false;
121 });
122}
123
124void sort_regions_by_attribute(std::vector<Region>& regions, const std::string& attr_name)
125{
126 std::ranges::sort(regions,
127 [&attr_name](const Region& a, const Region& b) {
128 auto aval = get_region_attribute<std::string>(a, attr_name);
129 auto bval = get_region_attribute<std::string>(b, attr_name);
130 return aval.value_or("") < bval.value_or("");
131 });
132}
133
134void add_reference_region(std::vector<std::pair<std::string, Region>>& refs, const std::string& name, const Region& region)
135{
136 refs.emplace_back(name, region);
137}
138
139void remove_reference_region(std::vector<std::pair<std::string, Region>>& refs, const std::string& name)
140{
141 auto to_remove = std::ranges::remove_if(refs,
142 [&name](const auto& pair) { return pair.first == name; });
143 refs.erase(to_remove.begin(), to_remove.end());
144}
145
146std::optional<Region> get_reference_region(const std::vector<std::pair<std::string, Region>>& refs, const std::string& name)
147{
148 auto it = std::ranges::find_if(refs,
149 [&name](const auto& pair) { return pair.first == name; });
150 if (it != refs.end())
151 return it->second;
152 return std::nullopt;
153}
154
155std::vector<std::pair<std::string, Region>> find_references_in_region(
156 const std::vector<std::pair<std::string, Region>>& refs, const Region& region)
157{
158 std::vector<std::pair<std::string, Region>> result;
159
160 std::ranges::copy_if(refs, std::back_inserter(result),
161 [&region](const auto& pair) { return region.contains(pair.second.start_coordinates); });
162 return result;
163}
164
165void add_region_group(std::unordered_map<std::string, RegionGroup>& groups, const RegionGroup& group)
166{
167 groups[group.name] = group;
168}
169
170std::optional<RegionGroup> get_region_group(const std::unordered_map<std::string, RegionGroup>& groups, const std::string& name)
171{
172 auto it = groups.find(name);
173 if (it != groups.end())
174 return it->second;
175 return std::nullopt;
176}
177
178void remove_region_group(std::unordered_map<std::string, RegionGroup>& groups, const std::string& name)
179{
180 groups.erase(name);
181}
182
183Region calculate_output_region(const std::vector<uint64_t>& current_pos,
184 const std::vector<uint64_t>& output_shape)
185{
186 if (current_pos.size() != output_shape.size()) {
187 error<std::invalid_argument>(
188 Journal::Component::Kakshya, Journal::Context::Runtime,
189 std::source_location::current(),
190 "Position and shape vectors must have same size: current_pos size = "
191 + std::to_string(current_pos.size()) + ", output_shape size = "
192 + std::to_string(output_shape.size()));
193 }
194
195 std::vector<uint64_t> end_pos;
196 end_pos.reserve(current_pos.size());
197
198 for (size_t i = 0; i < current_pos.size(); ++i) {
199 if (output_shape[i] == 0) {
200 error<std::invalid_argument>(
201 Journal::Component::Kakshya, Journal::Context::Runtime,
202 std::source_location::current(),
203 "Output shape cannot have zero dimensions: dimension " + std::to_string(i) + " is zero");
204 }
205 end_pos.push_back(current_pos[i] + output_shape[i] - 1);
206 }
207
208 return { current_pos, end_pos };
209}
210
211Region calculate_output_region(uint64_t current_frame,
212 uint64_t frames_to_process,
213 const std::shared_ptr<SignalSourceContainer>& container)
214{
215 const auto& structure = container->get_structure();
216 uint64_t total_frames = structure.get_samples_count_per_channel();
217 uint64_t num_channels = structure.get_channel_count();
218
219 if (current_frame >= total_frames) {
220 error<std::out_of_range>(
221 Journal::Component::Kakshya, Journal::Context::Runtime,
222 std::source_location::current(),
223 "Current frame exceeds container bounds: current_frame = "
224 + std::to_string(current_frame) + ", total_frames = "
225 + std::to_string(total_frames));
226 }
227
228 uint64_t available_frames = total_frames - current_frame;
229 uint64_t actual_frames = std::min(frames_to_process, available_frames);
230
231 Region output_shape;
232 output_shape.start_coordinates = { current_frame, 0 };
233 output_shape.end_coordinates = { current_frame + actual_frames - 1, num_channels - 1 };
234
235 return output_shape;
236}
237
239 const std::shared_ptr<SignalSourceContainer>& container)
240{
241 if (!container) {
242 return false;
243 }
244
245 const auto dimensions = container->get_dimensions();
246 const auto memory_layout = container->get_memory_layout();
247
248 // For row-major layout, contiguous access means the last dimension spans its full range
249 // For column-major layout, contiguous access means the first dimension spans its full range
250
251 if (memory_layout == MemoryLayout::ROW_MAJOR) {
252 if (!dimensions.empty() && !region.start_coordinates.empty() && !region.end_coordinates.empty()) {
253 size_t last_dim = dimensions.size() - 1;
254 return (region.start_coordinates[last_dim] == 0 && region.end_coordinates[last_dim] == dimensions[last_dim].size - 1);
255 }
256 } else if (memory_layout == MemoryLayout::COLUMN_MAJOR) {
257 if (!dimensions.empty() && !region.start_coordinates.empty() && !region.end_coordinates.empty()) {
258 return (region.start_coordinates[0] == 0 && region.end_coordinates[0] == dimensions[0].size - 1);
259 }
260 }
261
262 return false;
263}
264
265std::vector<std::unordered_map<std::string, std::any>> extract_all_regions_info(const std::shared_ptr<SignalSourceContainer>& container)
266{
267 if (!container) {
268 error<std::invalid_argument>(
269 Journal::Component::Kakshya, Journal::Context::Runtime,
270 std::source_location::current(),
271 "Container is null");
272 }
273
274 auto all_groups = container->get_all_region_groups();
275 std::vector<std::unordered_map<std::string, std::any>> regions_info;
276
277 for (const auto& [group_name, group] : all_groups) {
278 for (size_t i = 0; i < group.regions.size(); ++i) {
279 const auto& region = group.regions[i];
280
281 std::unordered_map<std::string, std::any> region_info;
282 region_info["group_name"] = group_name;
283 region_info["region_index"] = i;
284 region_info["start_coordinates"] = region.start_coordinates;
285 region_info["end_coordinates"] = region.end_coordinates;
286 region_info["attributes"] = region.attributes;
287
288 regions_info.push_back(std::move(region_info));
289 }
290 }
291
292 return regions_info;
293}
294
295std::unordered_map<std::string, std::any> extract_group_bounds_info(const RegionGroup& group)
296{
297 std::unordered_map<std::string, std::any> bounds_info;
298
299 if (group.regions.empty()) {
300 return bounds_info;
301 }
302
303 const auto& first_region = group.regions[0];
304 std::vector<uint64_t> min_coords = first_region.start_coordinates;
305 std::vector<uint64_t> max_coords = first_region.end_coordinates;
306
307 for (size_t i = 1; i < group.regions.size(); ++i) {
308 const auto& region = group.regions[i];
309
310 for (size_t j = 0; j < min_coords.size() && j < region.start_coordinates.size(); ++j) {
311 min_coords[j] = std::min(min_coords[j], region.start_coordinates[j]);
312 }
313
314 for (size_t j = 0; j < max_coords.size() && j < region.end_coordinates.size(); ++j) {
315 max_coords[j] = std::max(max_coords[j], region.end_coordinates[j]);
316 }
317 }
318
319 bounds_info["group_name"] = group.name;
320 bounds_info["num_regions"] = group.regions.size();
321 bounds_info["bounding_min"] = min_coords;
322 bounds_info["bounding_max"] = max_coords;
323 bounds_info["group_attributes"] = group.attributes;
324
325 return bounds_info;
326}
327
328std::vector<std::unordered_map<std::string, std::any>> extract_segments_metadata(const std::vector<RegionSegment>& segments)
329{
330 std::vector<std::unordered_map<std::string, std::any>> metadata_list;
331 metadata_list.reserve(segments.size());
332
333 for (const auto& segment : segments) {
334 std::unordered_map<std::string, std::any> segment_info;
335 segment_info["start_coordinates"] = segment.source_region.start_coordinates;
336 segment_info["end_coordinates"] = segment.source_region.end_coordinates;
337 segment_info["region_attributes"] = segment.source_region.attributes;
338 segment_info["segment_attributes"] = segment.processing_metadata;
339
340 metadata_list.push_back(std::move(segment_info));
341 }
342
343 return metadata_list;
344}
345
346std::unordered_map<std::string, std::any> extract_region_bounds_info(const Region& region)
347{
348 std::unordered_map<std::string, std::any> bounds_info;
349 bounds_info["start_coordinates"] = region.start_coordinates;
350 bounds_info["end_coordinates"] = region.end_coordinates;
351
352 std::vector<uint64_t> sizes;
353 sizes.reserve(region.start_coordinates.size());
354
355 for (size_t i = 0; i < region.start_coordinates.size() && i < region.end_coordinates.size(); ++i) {
356 sizes.push_back(region.end_coordinates[i] - region.start_coordinates[i] + 1);
357 }
358 bounds_info["sizes"] = sizes;
359
360 uint64_t total_elements = 1;
361 for (uint64_t size : sizes) {
362 total_elements *= size;
363 }
364 bounds_info["total_elements"] = total_elements;
365
366 return bounds_info;
367}
368
369std::optional<size_t> find_region_for_position(const std::vector<uint64_t>& position,
370 const std::vector<Region>& regions)
371{
372 for (size_t i = 0; i < regions.size(); ++i) {
373 if (regions[i].contains(position)) {
374 return i;
375 }
376 }
377 return std::nullopt;
378}
379
380std::optional<size_t> find_region_for_position(const std::vector<uint64_t>& position, std::vector<OrganizedRegion> regions)
381{
382 for (size_t i = 0; i < regions.size(); ++i) {
383 if (regions[i].contains_position(position)) {
384 return i;
385 }
386 }
387 return std::nullopt;
388}
389
390Region remove_channel_dimension(const Region& region, const std::vector<DataDimension>& dimensions)
391{
392 Region result = region;
393
394 for (long i = 0; i < dimensions.size(); ++i) {
395 if (dimensions[i].role == DataDimension::Role::CHANNEL) {
396 result.start_coordinates.erase(result.start_coordinates.begin() + i);
397 result.end_coordinates.erase(result.end_coordinates.begin() + i);
398 break;
399 }
400 }
401
402 return result;
403}
404
405std::vector<DataDimension> get_non_channel_dimensions(const std::vector<DataDimension>& dimensions)
406{
407 std::vector<DataDimension> result;
408 std::ranges::copy_if(dimensions, std::back_inserter(result),
409 [](const DataDimension& dim) {
410 return dim.role != DataDimension::Role::CHANNEL;
411 });
412 return result;
413}
414
415bool regions_intersect(const Region& r1, const Region& r2) noexcept
416{
417 const size_t ndim = std::min({ r1.start_coordinates.size(),
418 r1.end_coordinates.size(),
419 r2.start_coordinates.size(),
420 r2.end_coordinates.size() });
421 if (ndim < 2)
422 return false;
423
424 for (size_t i = 0; i < ndim; ++i) {
425 if (r1.end_coordinates[i] < r2.start_coordinates[i]
426 || r2.end_coordinates[i] < r1.start_coordinates[i])
427 return false;
428 }
429 return true;
430}
431}
size_t a
size_t b
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.
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.
Region scale_region(const Region &region, const std::vector< double > &factors)
Scale a Region about its center by the given factors.
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.
Role role
Semantic hint for common operations.
Definition NDData.hpp:191
Minimal dimension descriptor focusing on structure only.
Definition NDData.hpp:139
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