MayaFlux 0.1.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MatrixHelper.hpp
Go to the documentation of this file.
1#pragma once
2
7#include <Eigen/Dense>
8
9/**
10 * @file MatrixHelper.hpp
11 * @brief Region-aware and matrix transformation functions leveraging existing ecosystem
12 *
13 * - Uses EnergyAnalyzer for region detection instead of manual implementation
14 * - Leverages StatisticalAnalyzer for outlier detection instead of manual stats
15 * - Uses ExtractionHelper functions for data extraction
16 * - Uses normalization from MathematicalTransform where appropriate
17 */
18
19namespace MayaFlux::Yantra {
20
21/**
22 * @brief Region-selective transformation using container-based extraction (IN-PLACE)
23 * @tparam DataType OperationReadyData type
24 * @tparam TransformFunc Function type for transformation
25 * @param input Input data - WILL BE MODIFIED (serves as the target)
26 * @param container Signal source container
27 * @param regions Regions to transform and place back into input
28 * @param transform_func Transformation function
29 * @return Transformed data
30 */
31template <OperationReadyData DataType, typename TransformFunc>
32 requires std::invocable<TransformFunc, DataType>
33DataType transform_regions(DataType& input,
34 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
35 const std::vector<Kakshya::Region>& regions,
36 TransformFunc transform_func)
37{
38 if (!container) {
39 throw std::invalid_argument("Container is required for region-based transformations");
40 }
41
42 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
43
44 for (const auto& region : regions) {
45 auto region_data = container->get_region_data(region);
46 auto transformed_region = transform_func(region_data);
47 auto transformed_doubles = OperationHelper::extract_numeric_data(transformed_region);
48
49 auto start_sample = static_cast<uint64_t>(region.start_coordinates[0]);
50 auto end_sample = static_cast<uint64_t>(region.end_coordinates[0]);
51
52 for (size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
53 auto& target_channel = target_data[channel_idx];
54 const auto& transformed_channel = transformed_doubles[channel_idx];
55
56 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
57 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
58 auto copy_size = std::min(transformed_channel.size(), target_span.size());
59
60 std::ranges::copy(transformed_channel | std::views::take(copy_size),
61 target_span.begin());
62 }
63 }
64 }
65
66 auto reconstructed_data = target_data
67 | std::views::transform([](const auto& span) {
68 return std::vector<double>(span.begin(), span.end());
69 })
70 | std::ranges::to<std::vector>();
71
72 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
73}
74
75/**
76 * @brief Region-selective transformation using container-based extraction (OUT-OF-PLACE)
77 * @tparam DataType OperationReadyData type
78 * @tparam TransformFunc Function type for transformation
79 * @param input Input data - will NOT be modified
80 * @param container Signal source container
81 * @param regions Regions to transform and place back into copy
82 * @param transform_func Transformation function
83 * @param working_buffer Buffer for operations (will be resized if needed)
84 * @return Transformed data
85 */
86template <OperationReadyData DataType, typename TransformFunc>
87 requires std::invocable<TransformFunc, DataType>
88DataType transform_regions(DataType& input,
89 const std::shared_ptr<Kakshya::SignalSourceContainer>& container,
90 const std::vector<Kakshya::Region>& regions,
91 TransformFunc transform_func,
92 std::vector<std::vector<double>>& working_buffer)
93{
94 if (!container) {
95 throw std::invalid_argument("Container is required for region-based transformations");
96 }
97
98 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
99
100 for (const auto& region : regions) {
101 auto region_data = container->get_region_data(region);
102 auto transformed_region = transform_func(region_data);
103 auto transformed_doubles = OperationHelper::extract_numeric_data(transformed_region);
104
105 auto start_sample = static_cast<uint64_t>(region.start_coordinates[0]);
106 auto end_sample = static_cast<uint64_t>(region.end_coordinates[0]);
107
108 for (size_t channel_idx = 0; channel_idx < target_data.size() && channel_idx < transformed_doubles.size(); ++channel_idx) {
109 auto& target_channel = target_data[channel_idx];
110 const auto& transformed_channel = transformed_doubles[channel_idx];
111
112 if (start_sample < target_channel.size() && end_sample <= target_channel.size()) {
113 auto target_span = target_channel.subspan(start_sample, end_sample - start_sample);
114 auto copy_size = std::min(transformed_channel.size(), target_span.size());
115
116 std::ranges::copy(transformed_channel | std::views::take(copy_size),
117 target_span.begin());
118 }
119 }
120 }
121
122 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
123}
124
125/**
126 * @brief Energy-based transformation using existing EnergyAnalyzer (IN-PLACE)
127 * @tparam DataType OperationReadyData type
128 * @tparam TransformFunc Function type for transformation
129 * @param input Input data - WILL BE MODIFIED
130 * @param energy_threshold Energy threshold for transformation
131 * @param transform_func Transformation function
132 * @param window_size Analysis window size
133 * @param hop_size Hop size between windows
134 * @return Transformed data
135 */
136template <OperationReadyData DataType, typename TransformFunc>
137 requires std::invocable<TransformFunc, double>
138DataType transform_by_energy(DataType& input,
139 double energy_threshold,
140 TransformFunc transform_func,
141 uint32_t window_size = 1024,
142 uint32_t hop_size = 512)
143{
144 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
145
146 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
147 energy_analyzer->set_energy_method(EnergyMethod::RMS);
148 auto energy_result = energy_analyzer->analyze_energy(input);
149
150 for (size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
151 const auto& channel_energy = energy_result.channels[ch];
152 auto& target_channel = target_data[ch];
153
154 for (size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
155 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
156 auto [start_idx, end_idx] = channel_energy.window_positions[i];
157
158 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
159 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
160 std::ranges::transform(window_span, window_span.begin(), transform_func);
161 }
162 }
163 }
164 }
165
166 auto reconstructed_data = target_data
167 | std::views::transform([](const auto& span) {
168 return std::vector<double>(span.begin(), span.end());
169 })
170 | std::ranges::to<std::vector>();
171
172 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
173}
174
175/**
176 * @brief Energy-based transformation using existing EnergyAnalyzer (OUT-OF-PLACE)
177 * @tparam DataType OperationReadyData type
178 * @tparam TransformFunc Function type for transformation
179 * @param input Input data - will NOT be modified
180 * @param energy_threshold Energy threshold for transformation
181 * @param transform_func Transformation function
182 * @param window_size Analysis window size
183 * @param hop_size Hop size between windows
184 * @param working_buffer Buffer for operations (will be resized if needed)
185 * @return Transformed data
186 */
187template <OperationReadyData DataType, typename TransformFunc>
188 requires std::invocable<TransformFunc, double>
189DataType transform_by_energy(DataType& input,
190 double energy_threshold,
191 TransformFunc transform_func,
192 uint32_t window_size,
193 uint32_t hop_size,
194 std::vector<std::vector<double>>& working_buffer)
195{
196 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
197
198 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
199 energy_analyzer->set_energy_method(EnergyMethod::RMS);
200 auto energy_result = energy_analyzer->analyze_energy(input);
201
202 for (size_t ch = 0; ch < energy_result.channels.size() && ch < target_data.size(); ++ch) {
203 const auto& channel_energy = energy_result.channels[ch];
204 auto& target_channel = target_data[ch];
205
206 for (size_t i = 0; i < channel_energy.energy_values.size(); ++i) {
207 if (channel_energy.energy_values[i] > energy_threshold && i < channel_energy.window_positions.size()) {
208 auto [start_idx, end_idx] = channel_energy.window_positions[i];
209
210 if (start_idx < target_channel.size() && end_idx <= target_channel.size()) {
211 auto window_span = target_channel.subspan(start_idx, end_idx - start_idx);
212 std::ranges::transform(window_span, window_span.begin(), transform_func);
213 }
214 }
215 }
216 }
217
218 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
219}
220
221/**
222 * @brief Statistical outlier transformation using existing StatisticalAnalyzer (IN-PLACE)
223 * @tparam DataType OperationReadyData type
224 * @tparam TransformFunc Function type for transformation
225 * @param input Input data - WILL BE MODIFIED
226 * @param std_dev_threshold Standard deviation threshold for outliers
227 * @param transform_func Transformation function
228 * @return Transformed data
229 */
230template <OperationReadyData DataType, typename TransformFunc>
231 requires std::invocable<TransformFunc, double>
232DataType transform_outliers(DataType& input,
233 double std_dev_threshold,
234 TransformFunc transform_func)
235{
236 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
237
238 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
239 auto stats = stat_analyzer->analyze_statistics(input);
240
241 if (stats.channel_statistics.empty()) {
242 throw std::runtime_error("No channel statistics available for outlier detection");
243 }
244 const auto& first_channel_stats = stats.channel_statistics[0];
245 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
246 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
247
248 for (auto& channel_span : target_data) {
249 std::ranges::transform(channel_span, channel_span.begin(),
250 [&](double x) {
251 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
252 });
253 }
254
255 auto reconstructed_data = target_data
256 | std::views::transform([](const auto& span) {
257 return std::vector<double>(span.begin(), span.end());
258 })
259 | std::ranges::to<std::vector>();
260
261 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
262}
263
264/**
265 * @brief Statistical outlier transformation using existing StatisticalAnalyzer (OUT-OF-PLACE)
266 * @tparam DataType OperationReadyData type
267 * @tparam TransformFunc Function type for transformation
268 * @param input Input data - will NOT be modified
269 * @param std_dev_threshold Standard deviation threshold for outliers
270 * @param transform_func Transformation function
271 * @param working_buffer Buffer for operations (will be resized if needed)
272 * @return Transformed data
273 */
274template <OperationReadyData DataType, typename TransformFunc>
275 requires std::invocable<TransformFunc, double>
276DataType transform_outliers(DataType& input,
277 double std_dev_threshold,
278 TransformFunc transform_func,
279 std::vector<std::vector<double>>& working_buffer)
280{
281 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
282
283 auto stat_analyzer = std::make_shared<StandardStatisticalAnalyzer>();
284 auto stats = stat_analyzer->analyze_statistics(input);
285
286 if (stats.channel_statistics.empty()) {
287 throw std::runtime_error("No channel statistics available for outlier detection");
288 }
289 const auto& first_channel_stats = stats.channel_statistics[0];
290 double threshold_low = first_channel_stats.mean_stat - std_dev_threshold * first_channel_stats.stat_std_dev;
291 double threshold_high = first_channel_stats.mean_stat + std_dev_threshold * first_channel_stats.stat_std_dev;
292
293 for (auto& channel_span : target_data) {
294 std::ranges::transform(channel_span, channel_span.begin(),
295 [&](double x) {
296 return (x < threshold_low || x > threshold_high) ? transform_func(x) : x;
297 });
298 }
299
300 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
301}
302
303/**
304 * @brief Cross-fade between regions with smooth transitions (IN-PLACE)
305 * @tparam DataType OperationReadyData type
306 * @param input Input data - WILL BE MODIFIED
307 * @param fade_regions Pairs of regions to crossfade
308 * @param fade_duration Duration of fade in samples
309 * @return Crossfaded data
310 */
311template <OperationReadyData DataType>
312DataType transform_crossfade_regions(DataType& input,
313 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
314 uint32_t fade_duration)
315{
316 // OLD: auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
317 auto extraction_result = OperationHelper::extract_structured_double(input);
318 auto& target_data = std::get<0>(extraction_result);
319 auto& structure_info = std::get<1>(extraction_result);
320
321 std::ranges::for_each(fade_regions, [&target_data, fade_duration](const auto& fade_pair) {
322 const auto& [region_a, region_b] = fade_pair;
323
324 auto start_a = static_cast<uint64_t>(region_a.start_coordinates[0]);
325 auto end_a = static_cast<uint64_t>(region_a.end_coordinates[0]);
326 auto start_b = static_cast<uint64_t>(region_b.start_coordinates[0]);
327
328 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
329 uint64_t fade_end = std::min(start_b + fade_duration, static_cast<uint64_t>(target_data[0].size()));
330
331 for (auto& channel_span : target_data) {
332 if (fade_start < fade_end && fade_start < channel_span.size()) {
333 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
334
335 auto fade_indices = std::views::iota(size_t { 0 }, fade_span.size());
336 std::ranges::for_each(fade_indices, [&fade_span](size_t i) {
337 double ratio = static_cast<double>(i) / (fade_span.size() - 1);
338 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
339 fade_span[i] *= (1.0 - smooth_ratio);
340 });
341 }
342 }
343 });
344
345 auto reconstructed_data = target_data
346 | std::views::transform([](const auto& span) {
347 return std::vector<double>(span.begin(), span.end());
348 })
349 | std::ranges::to<std::vector>();
350
351 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
352}
353
354/**
355 * @brief Cross-fade between regions with smooth transitions (OUT-OF-PLACE)
356 * @tparam DataType OperationReadyData type
357 * @param input Input data - will NOT be modified
358 * @param fade_regions Pairs of regions to crossfade
359 * @param fade_duration Duration of fade in samples
360 * @param working_buffer Buffer for operations (will be resized if needed)
361 * @return Crossfaded data
362 */
363template <OperationReadyData DataType>
364DataType transform_crossfade_regions(DataType& input,
365 const std::vector<std::pair<Kakshya::Region, Kakshya::Region>>& fade_regions,
366 uint32_t fade_duration,
367 std::vector<std::vector<double>>& working_buffer)
368{
369 // OLD: auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
370 auto setup_result = OperationHelper::setup_operation_buffer(input, working_buffer);
371 auto& target_data = std::get<0>(setup_result);
372 auto& structure_info = std::get<1>(setup_result);
373
374 std::ranges::for_each(fade_regions, [&target_data, fade_duration](const auto& fade_pair) {
375 const auto& [region_a, region_b] = fade_pair;
376
377 auto start_a = static_cast<uint64_t>(region_a.start_coordinates[0]);
378 auto end_a = static_cast<uint64_t>(region_a.end_coordinates[0]);
379 auto start_b = static_cast<uint64_t>(region_b.start_coordinates[0]);
380
381 uint64_t fade_start = (end_a > fade_duration) ? end_a - fade_duration : 0;
382 uint64_t fade_end = std::min(start_b + fade_duration, static_cast<uint64_t>(target_data[0].size()));
383
384 for (auto& channel_span : target_data) {
385 if (fade_start < fade_end && fade_start < channel_span.size()) {
386 auto fade_span = channel_span.subspan(fade_start, fade_end - fade_start);
387
388 auto fade_indices = std::views::iota(size_t { 0 }, fade_span.size());
389 std::ranges::for_each(fade_indices, [&fade_span](size_t i) {
390 double ratio = static_cast<double>(i) / (fade_span.size() - 1);
391 double smooth_ratio = 0.5 * (1.0 - std::cos(ratio * M_PI));
392 fade_span[i] *= (1.0 - smooth_ratio);
393 });
394 }
395 }
396 });
397
398 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
399}
400
401/**
402 * @brief Matrix transformation using existing infrastructure (IN-PLACE)
403 * @tparam DataType OperationReadyData type
404 * @param input Input data - WILL BE MODIFIED
405 * @param transformation_matrix Matrix to apply
406 * @return Transformed data
407 */
408template <OperationReadyData DataType>
409DataType transform_matrix(DataType& input, const Eigen::MatrixXd& transformation_matrix)
410{
411 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
412
413 for (auto& channel_span : target_data) {
414 if (transformation_matrix.cols() == static_cast<long>(channel_span.size())) {
415 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
416 Eigen::VectorXd result = transformation_matrix * data_vector;
417
418 auto copy_size = std::min<long>(result.size(), static_cast<long>(channel_span.size()));
419 std::ranges::copy(result.head(copy_size), channel_span.begin());
420 }
421 }
422
423 auto reconstructed_data = target_data
424 | std::views::transform([](const auto& span) {
425 return std::vector<double>(span.begin(), span.end());
426 })
427 | std::ranges::to<std::vector>();
428
429 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
430}
431
432/**
433 * @brief Matrix transformation using existing infrastructure (OUT-OF-PLACE)
434 * @tparam DataType OperationReadyData type
435 * @param input Input data - will NOT be modified
436 * @param transformation_matrix Matrix to apply
437 * @param working_buffer Buffer for operations (will be resized if needed)
438 * @return Transformed data
439 */
440template <OperationReadyData DataType>
441DataType transform_matrix(DataType& input, const Eigen::MatrixXd& transformation_matrix, std::vector<std::vector<double>>& working_buffer)
442{
443 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
444
445 for (size_t channel_idx = 0; channel_idx < target_data.size(); ++channel_idx) {
446 auto& channel_span = target_data[channel_idx];
447 if (transformation_matrix.cols() == static_cast<long>(channel_span.size())) {
448 Eigen::Map<Eigen::VectorXd> data_vector(channel_span.data(), channel_span.size());
449 Eigen::VectorXd result = transformation_matrix * data_vector;
450
451 working_buffer[channel_idx].resize(result.size());
452 std::ranges::copy(result, working_buffer[channel_idx].begin());
453 }
454 }
455
456 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
457}
458
459/**
460 * @brief Multi-channel matrix transformation with error handling (IN-PLACE)
461 * @tparam DataType OperationReadyData type
462 * @param input Input data - WILL BE MODIFIED
463 * @param transformation_matrix Matrix to apply per channel
464 * @param num_channels Number of channels
465 * @return Transformed data
466 */
467template <OperationReadyData DataType>
468DataType transform_matrix_multichannel(DataType& input,
469 const Eigen::MatrixXd& transformation_matrix,
470 uint32_t num_channels)
471{
472 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
473
474 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
475 throw std::invalid_argument("Transformation matrix dimensions must match number of channels");
476 }
477
478 if (target_data.size() != num_channels) {
479 throw std::invalid_argument("Data channel count must match specified number of channels");
480 }
481
482 size_t min_frames = std::ranges::min(target_data | std::views::transform([](const auto& span) { return span.size(); }));
483
484 auto frame_indices = std::views::iota(size_t { 0 }, min_frames);
485
486 std::ranges::for_each(frame_indices, [&](size_t frame) {
487 Eigen::VectorXd frame_vector(num_channels);
488
489 for (size_t channel = 0; channel < num_channels; ++channel) {
490 frame_vector[channel] = target_data[channel][frame];
491 }
492
493 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
494
495 for (size_t channel = 0; channel < num_channels; ++channel) {
496 target_data[channel][frame] = transformed[channel];
497 }
498 });
499
500 auto reconstructed_data = target_data
501 | std::views::transform([](const auto& span) {
502 return std::vector<double>(span.begin(), span.end());
503 })
504 | std::ranges::to<std::vector>();
505
506 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
507}
508
509/**
510 * @brief Multi-channel matrix transformation with error handling (OUT-OF-PLACE)
511 * @tparam DataType OperationReadyData type
512 * @param input Input data - will NOT be modified
513 * @param transformation_matrix Matrix to apply per channel
514 * @param num_channels Number of channels
515 * @param working_buffer Buffer for operations (will be resized if needed)
516 * @return Transformed data
517 */
518template <OperationReadyData DataType>
519DataType transform_matrix_multichannel(DataType& input,
520 const Eigen::MatrixXd& transformation_matrix,
521 uint32_t num_channels,
522 std::vector<std::vector<double>>& working_buffer)
523{
524 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
525
526 if (transformation_matrix.rows() != num_channels || transformation_matrix.cols() != num_channels) {
527 throw std::invalid_argument("Transformation matrix dimensions must match number of channels");
528 }
529
530 if (target_data.size() != num_channels) {
531 throw std::invalid_argument("Data channel count must match specified number of channels");
532 }
533
534 size_t min_frames = std::ranges::min(target_data | std::views::transform([](const auto& span) { return span.size(); }));
535
536 auto frame_indices = std::views::iota(size_t { 0 }, min_frames);
537
538 std::ranges::for_each(frame_indices, [&](size_t frame) {
539 Eigen::VectorXd frame_vector(num_channels);
540
541 for (size_t channel = 0; channel < num_channels; ++channel) {
542 frame_vector[channel] = target_data[channel][frame];
543 }
544
545 Eigen::VectorXd transformed = transformation_matrix * frame_vector;
546
547 for (size_t channel = 0; channel < num_channels; ++channel) {
548 working_buffer[channel][frame] = transformed[channel];
549 }
550 });
551
552 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
553}
554
555/**
556 * @brief Channel operations using C++20 ranges (IN-PLACE)
557 * @tparam DataType OperationReadyData type
558 * @param input Input data - WILL BE MODIFIED
559 * @param num_channels Number of channels
560 * @param interleave Whether to interleave or deinterleave
561 * @return Transformed data
562 */
563template <OperationReadyData DataType>
564DataType transform_channel_operation(DataType& input,
565 uint32_t num_channels,
566 bool interleave)
567{
568 auto [target_data, structure_info] = OperationHelper::extract_structured_double(input);
569
570 if (target_data.size() != num_channels) {
571 throw std::invalid_argument("Data channel count must match specified number of channels");
572 }
573
574 auto reconstructed_data = target_data
575 | std::views::transform([](const auto& span) {
576 return std::vector<double>(span.begin(), span.end());
577 })
578 | std::ranges::to<std::vector>();
579
580 return OperationHelper::reconstruct_from_double<DataType>(reconstructed_data, structure_info);
581}
582
583/**
584 * @brief Channel operations using C++20 ranges (OUT-OF-PLACE)
585 * @tparam DataType OperationReadyData type
586 * @param input Input data - will NOT be modified
587 * @param num_channels Number of channels
588 * @param interleave Whether to interleave or deinterleave
589 * @param working_buffer Buffer for operations (will be resized if needed)
590 * @return Transformed data
591 */
592template <OperationReadyData DataType>
593DataType transform_channel_operation(DataType& input,
594 uint32_t num_channels,
595 bool interleave,
596 std::vector<std::vector<double>>& working_buffer)
597{
598 auto [target_data, structure_info] = OperationHelper::setup_operation_buffer(input, working_buffer);
599
600 if (target_data.size() != num_channels) {
601 throw std::invalid_argument("Data channel count must match specified number of channels");
602 }
603
604 return OperationHelper::reconstruct_from_double<DataType>(working_buffer, structure_info);
605}
606
607/**
608 * @brief Detect regions based on energy threshold using existing EnergyAnalyzer
609 * @tparam DataType OperationReadyData type
610 * @param input Input data
611 * @param energy_threshold Energy threshold for region detection
612 * @param min_region_size Minimum size of detected regions in samples
613 * @param window_size Analysis window size
614 * @param hop_size Hop size between windows
615 * @return Detected regions
616 */
617template <OperationReadyData DataType>
618std::vector<Kakshya::Region> detect_regions_by_energy(const DataType& input,
619 double energy_threshold,
620 uint32_t min_region_size,
621 uint32_t window_size,
622 uint32_t hop_size)
623{
624 auto energy_analyzer = std::make_shared<StandardEnergyAnalyzer>(window_size, hop_size);
625 energy_analyzer->set_energy_method(EnergyMethod::RMS);
626 auto energy_result = energy_analyzer->analyze_energy(input);
627
628 std::vector<Kakshya::Region> regions;
629
630 if (energy_result.channels.empty()) {
631 return regions;
632 }
633
634 const auto& first_channel = energy_result.channels[0];
635 std::optional<uint64_t> region_start;
636
637 for (size_t i = 0; i < first_channel.energy_values.size(); ++i) {
638 bool above_threshold = first_channel.energy_values[i] > energy_threshold;
639
640 if (above_threshold && !region_start.has_value()) {
641 if (i < first_channel.window_positions.size()) {
642 region_start = first_channel.window_positions[i].first;
643 }
644 } else if (!above_threshold && region_start.has_value()) {
645 if (i < first_channel.window_positions.size()) {
646 uint64_t region_end = first_channel.window_positions[i].second;
647
648 if (region_end - region_start.value() >= min_region_size) {
649 Kakshya::Region region;
650 region.start_coordinates = { region_start.value() };
651 region.end_coordinates = { region_end };
652 regions.push_back(region);
653 }
654 region_start.reset();
655 }
656 }
657 }
658
659 if (region_start.has_value() && !first_channel.window_positions.empty()) {
660 uint64_t region_end = first_channel.window_positions.back().second;
661 if (region_end - region_start.value() >= min_region_size) {
662 Kakshya::Region region;
663
664 region.start_coordinates = { region_start.value() };
665 region.end_coordinates = { region_end };
666 regions.push_back(region);
667 }
668 }
669
670 return regions;
671}
672
673Eigen::MatrixXd create_rotation_matrix(double angle, uint32_t axis = 2, uint32_t dimensions = 2);
674Eigen::MatrixXd create_scaling_matrix(const std::vector<double>& scale_factors);
675
676} // namespace MayaFlux::Yantra
Span-based energy analysis for digital signals in Maya Flux.
static std::tuple< std::vector< std::span< double > >, DataStructureInfo > extract_structured_double(T &compute_data)
Extract structured double data from IO container or direct ComputeData with automatic container handl...
static auto setup_operation_buffer(T &input, std::vector< std::vector< double > > &working_buffer)
Setup operation buffer from IO or ComputeData type.
static std::span< double > extract_numeric_data(const T &compute_data)
extract numeric data from single-variant types
DataType transform_regions(DataType &input, const std::shared_ptr< Kakshya::SignalSourceContainer > &container, const std::vector< Kakshya::Region > &regions, TransformFunc transform_func)
Region-selective transformation using container-based extraction (IN-PLACE)
@ RMS
Root Mean Square energy.
DataType transform_matrix(DataType &input, const Eigen::MatrixXd &transformation_matrix)
Matrix transformation using existing infrastructure (IN-PLACE)
DataType transform_matrix_multichannel(DataType &input, const Eigen::MatrixXd &transformation_matrix, uint32_t num_channels)
Multi-channel matrix transformation with error handling (IN-PLACE)
DataType transform_by_energy(DataType &input, double energy_threshold, TransformFunc transform_func, uint32_t window_size=1024, uint32_t hop_size=512)
Energy-based transformation using existing EnergyAnalyzer (IN-PLACE)
Eigen::MatrixXd create_rotation_matrix(double angle, uint32_t axis, uint32_t dimensions)
DataType transform_channel_operation(DataType &input, uint32_t num_channels, bool interleave)
Channel operations using C++20 ranges (IN-PLACE)
std::vector< Kakshya::Region > detect_regions_by_energy(const DataType &input, double energy_threshold, uint32_t min_region_size, uint32_t window_size, uint32_t hop_size)
Detect regions based on energy threshold using existing EnergyAnalyzer.
DataType transform_crossfade_regions(DataType &input, const std::vector< std::pair< Kakshya::Region, Kakshya::Region > > &fade_regions, uint32_t fade_duration)
Cross-fade between regions with smooth transitions (IN-PLACE)
Eigen::MatrixXd create_scaling_matrix(const std::vector< double > &scale_factors)
DataType transform_outliers(DataType &input, double std_dev_threshold, TransformFunc transform_func)
Statistical outlier transformation using existing StatisticalAnalyzer (IN-PLACE)
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
Represents a point or span in N-dimensional space.
Definition Region.hpp:67