1use scirs2_core::ndarray::s;
2use crate::error::{SignalError, SignalResult};
30use scirs2_core::ndarray::{Array1, Array2, Array3, Axis};
31
32#[allow(unused_imports)]
33#[derive(Debug, Clone, Copy, PartialEq)]
35pub enum EdgeMode {
36 Reflect,
38
39 Nearest,
41
42 Constant(f64),
44
45 Wrap,
47}
48
49#[derive(Debug, Clone)]
51pub struct MedianConfig {
52 pub edge_mode: EdgeMode,
54
55 pub adaptive: bool,
57
58 pub max_kernel_size: usize,
60
61 pub noise_threshold: f64,
63
64 pub center_weighted: bool,
66
67 pub center_weight: usize,
69}
70
71impl Default for MedianConfig {
72 fn default() -> Self {
73 Self {
74 edge_mode: EdgeMode::Reflect,
75 adaptive: false,
76 max_kernel_size: 9,
77 noise_threshold: 50.0,
78 center_weighted: false,
79 center_weight: 3,
80 }
81 }
82}
83
84#[allow(dead_code)]
107pub fn median_filter_1d(
108 signal: &Array1<f64>,
109 kernel_size: usize,
110 config: &MedianConfig,
111) -> SignalResult<Array1<f64>> {
112 if kernel_size % 2 != 1 {
114 return Err(SignalError::ValueError(
115 "Kernel _size must be odd".to_string(),
116 ));
117 }
118
119 let n = signal.len();
120
121 if n <= 1 || kernel_size > n {
123 return Ok(signal.clone());
124 }
125
126 let half_kernel = kernel_size / 2;
127
128 let paddedsignal = padsignal_1d(signal, half_kernel, config.edge_mode);
130
131 if config.adaptive {
133 adaptive_median_filter_1d(signal, &paddedsignal, half_kernel, config)
134 } else if config.center_weighted {
135 center_weighted_median_filter_1d(signal, &paddedsignal, half_kernel, config)
136 } else {
137 standard_median_filter_1d(signal, &paddedsignal, half_kernel)
138 }
139}
140
141#[allow(dead_code)]
143fn standard_median_filter_1d(
144 signal: &Array1<f64>,
145 paddedsignal: &Array1<f64>,
146 half_kernel: usize,
147) -> SignalResult<Array1<f64>> {
148 let n = signal.len();
149 let mut filtered = Array1::zeros(n);
150
151 for i in 0..n {
153 let window_start = i;
155 let window_end = i + 2 * half_kernel + 1;
156
157 if window_start >= paddedsignal.len() || window_end > paddedsignal.len() {
159 return Err(SignalError::DimensionMismatch(
160 "Window extends beyond padded signal bounds".to_string(),
161 ));
162 }
163
164 let mut window: Vec<f64> = paddedsignal.slice(s![window_start..window_end]).to_vec();
166 window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
167
168 filtered[i] = window[half_kernel];
170 }
171
172 Ok(filtered)
173}
174
175#[allow(dead_code)]
177fn center_weighted_median_filter_1d(
178 signal: &Array1<f64>,
179 paddedsignal: &Array1<f64>,
180 half_kernel: usize,
181 config: &MedianConfig,
182) -> SignalResult<Array1<f64>> {
183 let n = signal.len();
184 let mut filtered = Array1::zeros(n);
185
186 for i in 0..n {
188 let window_start = i;
190 let window_end = i + 2 * half_kernel + 1;
191
192 if window_start >= paddedsignal.len() || window_end > paddedsignal.len() {
194 return Err(SignalError::DimensionMismatch(
195 "Window extends beyond padded signal bounds".to_string(),
196 ));
197 }
198
199 let mut weighted_window = Vec::new();
201
202 for j in window_start..window_end {
203 let value = paddedsignal[j];
204
205 if j == window_start + half_kernel {
207 for _ in 0..config.center_weight {
208 weighted_window.push(value);
209 }
210 } else {
211 weighted_window.push(value);
212 }
213 }
214
215 weighted_window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
217
218 let median_idx = weighted_window.len() / 2;
220
221 filtered[i] = weighted_window[median_idx];
223 }
224
225 Ok(filtered)
226}
227
228#[allow(dead_code)]
230fn adaptive_median_filter_1d(
231 signal: &Array1<f64>,
232 paddedsignal: &Array1<f64>,
233 initial_half_kernel: usize,
234 config: &MedianConfig,
235) -> SignalResult<Array1<f64>> {
236 let n = signal.len();
237 let mut filtered = Array1::zeros(n);
238
239 let max_half_kernel = config.max_kernel_size / 2;
241
242 for i in 0..n {
244 let mut half_kernel = initial_half_kernel;
246 let mut window_size = 2 * half_kernel + 1;
247
248 let curr_val = paddedsignal[i + half_kernel];
250
251 while half_kernel <= max_half_kernel {
253 let window_start = i + (initial_half_kernel - half_kernel);
255 let window_end = window_start + window_size;
256
257 if window_end > paddedsignal.len() {
259 break;
260 }
261
262 let window: Vec<f64> = paddedsignal.slice(s![window_start..window_end]).to_vec();
264 let mut sorted_window = window.clone();
265 sorted_window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
266
267 let median = sorted_window[half_kernel];
269 let min_val = sorted_window[0];
270 let max_val = sorted_window[sorted_window.len() - 1];
271
272 let level_a = min_val < median && median < max_val;
274
275 if level_a {
276 let level_b = min_val < curr_val && curr_val < max_val;
278
279 if level_b {
280 filtered[i] = curr_val;
282 } else {
283 filtered[i] = median;
285 }
286
287 break;
289 } else {
290 half_kernel += 1;
292 window_size = 2 * half_kernel + 1;
293
294 if half_kernel > max_half_kernel {
296 filtered[i] = median;
297 }
298 }
299 }
300 }
301
302 Ok(filtered)
303}
304
305#[allow(dead_code)]
330pub fn median_filter_2d(
331 image: &Array2<f64>,
332 kernel_size: usize,
333 config: &MedianConfig,
334) -> SignalResult<Array2<f64>> {
335 if kernel_size % 2 != 1 {
337 return Err(SignalError::ValueError(
338 "Kernel _size must be odd".to_string(),
339 ));
340 }
341
342 let (height, width) = image.dim();
343
344 if height <= 1 || width <= 1 || kernel_size > height || kernel_size > width {
346 return Ok(image.clone());
347 }
348
349 let half_kernel = kernel_size / 2;
350
351 let paddedimage = padimage_2d(image, half_kernel, config.edge_mode);
353
354 if config.adaptive {
356 adaptive_median_filter_2d(image, &paddedimage, half_kernel, config)
357 } else if config.center_weighted {
358 center_weighted_median_filter_2d(image, &paddedimage, half_kernel, config)
359 } else {
360 standard_median_filter_2d(image, &paddedimage, half_kernel)
361 }
362}
363
364#[allow(dead_code)]
366fn standard_median_filter_2d(
367 image: &Array2<f64>,
368 paddedimage: &Array2<f64>,
369 half_kernel: usize,
370) -> SignalResult<Array2<f64>> {
371 let (height, width) = image.dim();
372 let mut filtered = Array2::zeros((height, width));
373
374 for i in 0..height {
376 for j in 0..width {
377 let window_i_start = i;
379 let window_i_end = i + 2 * half_kernel + 1;
380 let window_j_start = j;
381 let window_j_end = j + 2 * half_kernel + 1;
382
383 if window_i_end > paddedimage.dim().0 || window_j_end > paddedimage.dim().1 {
385 return Err(SignalError::DimensionMismatch(
386 "Window extends beyond padded image bounds".to_string(),
387 ));
388 }
389
390 let window = paddedimage.slice(s![
392 window_i_start..window_i_end,
393 window_j_start..window_j_end
394 ]);
395
396 let mut flat_window: Vec<f64> = window.iter().copied().collect();
398 flat_window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
399
400 let median_idx = flat_window.len() / 2;
402 filtered[[i, j]] = flat_window[median_idx];
403 }
404 }
405
406 Ok(filtered)
407}
408
409#[allow(dead_code)]
411fn center_weighted_median_filter_2d(
412 image: &Array2<f64>,
413 paddedimage: &Array2<f64>,
414 half_kernel: usize,
415 config: &MedianConfig,
416) -> SignalResult<Array2<f64>> {
417 let (height, width) = image.dim();
418 let mut filtered = Array2::zeros((height, width));
419
420 let center_i = half_kernel;
422 let center_j = half_kernel;
423
424 for i in 0..height {
426 for j in 0..width {
427 let window_i_start = i;
429 let window_i_end = i + 2 * half_kernel + 1;
430 let window_j_start = j;
431 let window_j_end = j + 2 * half_kernel + 1;
432
433 if window_i_end > paddedimage.dim().0 || window_j_end > paddedimage.dim().1 {
435 return Err(SignalError::DimensionMismatch(
436 "Window extends beyond padded image bounds".to_string(),
437 ));
438 }
439
440 let mut weighted_window = Vec::new();
442
443 for wi in 0..(2 * half_kernel + 1) {
444 for wj in 0..(2 * half_kernel + 1) {
445 let value = paddedimage[[window_i_start + wi, window_j_start + wj]];
446
447 if wi == center_i && wj == center_j {
449 for _ in 0..config.center_weight {
450 weighted_window.push(value);
451 }
452 } else {
453 weighted_window.push(value);
454 }
455 }
456 }
457
458 weighted_window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
460
461 let median_idx = weighted_window.len() / 2;
463
464 filtered[[i, j]] = weighted_window[median_idx];
466 }
467 }
468
469 Ok(filtered)
470}
471
472#[allow(dead_code)]
474fn adaptive_median_filter_2d(
475 image: &Array2<f64>,
476 paddedimage: &Array2<f64>,
477 initial_half_kernel: usize,
478 config: &MedianConfig,
479) -> SignalResult<Array2<f64>> {
480 let (height, width) = image.dim();
481 let mut filtered = Array2::zeros((height, width));
482
483 let max_half_kernel = config.max_kernel_size / 2;
485
486 for i in 0..height {
488 for j in 0..width {
489 let mut half_kernel = initial_half_kernel;
491
492 let curr_val = paddedimage[[i + half_kernel, j + half_kernel]];
494
495 while half_kernel <= max_half_kernel {
497 let kernel_size = 2 * half_kernel + 1;
498
499 let offset = half_kernel - initial_half_kernel;
501
502 let window_i_start = i + offset;
504 let window_i_end = window_i_start + kernel_size;
505 let window_j_start = j + offset;
506 let window_j_end = window_j_start + kernel_size;
507
508 if window_i_end > paddedimage.dim().0 || window_j_end > paddedimage.dim().1 {
510 break;
511 }
512
513 let window = paddedimage.slice(s![
515 window_i_start..window_i_end,
516 window_j_start..window_j_end
517 ]);
518
519 let mut flat_window: Vec<f64> = window.iter().copied().collect();
521 flat_window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
522
523 let min_val = flat_window[0];
525 let max_val = flat_window[flat_window.len() - 1];
526 let median_idx = flat_window.len() / 2;
527 let median = flat_window[median_idx];
528
529 let level_a = min_val < median && median < max_val;
531
532 if level_a {
533 let level_b = min_val < curr_val && curr_val < max_val;
535
536 if level_b {
537 filtered[[i, j]] = curr_val;
539 } else {
540 filtered[[i, j]] = median;
542 }
543
544 break;
546 } else {
547 half_kernel += 1;
549
550 if half_kernel > max_half_kernel {
552 filtered[[i, j]] = median;
553 }
554 }
555 }
556 }
557 }
558
559 Ok(filtered)
560}
561
562#[allow(dead_code)]
576pub fn median_filter_color(
577 image: &Array3<f64>,
578 kernel_size: usize,
579 config: &MedianConfig,
580 vector_median: bool,
581) -> SignalResult<Array3<f64>> {
582 let (height, width, channels) = image.dim();
583
584 if vector_median {
585 vector_median_filter(image, kernel_size, config)
587 } else {
588 let mut filtered = Array3::zeros((height, width, channels));
590
591 for c in 0..channels {
592 let channel = image.index_axis(Axis(2), c).to_owned();
594
595 let filtered_channel = median_filter_2d(&channel, kernel_size, config)?;
597
598 for i in 0..height {
600 for j in 0..width {
601 filtered[[i, j, c]] = filtered_channel[[i, j]];
602 }
603 }
604 }
605
606 Ok(filtered)
607 }
608}
609
610#[allow(dead_code)]
624fn vector_median_filter(
625 image: &Array3<f64>,
626 kernel_size: usize,
627 config: &MedianConfig,
628) -> SignalResult<Array3<f64>> {
629 if kernel_size % 2 != 1 {
631 return Err(SignalError::ValueError(
632 "Kernel _size must be odd".to_string(),
633 ));
634 }
635
636 let (height, width, channels) = image.dim();
637
638 if height <= 1 || width <= 1 || kernel_size > height || kernel_size > width {
640 return Ok(image.clone());
641 }
642
643 let half_kernel = kernel_size / 2;
644
645 let mut padded_channels = Vec::with_capacity(channels);
647 for c in 0..channels {
648 let channel = image.index_axis(Axis(2), c).to_owned();
649 padded_channels.push(padimage_2d(&channel, half_kernel, config.edge_mode));
650 }
651
652 let mut filtered = Array3::zeros((height, width, channels));
654
655 for i in 0..height {
657 for j in 0..width {
658 let window_i_start = i;
660 let window_i_end = i + 2 * half_kernel + 1;
661 let window_j_start = j;
662 let window_j_end = j + 2 * half_kernel + 1;
663
664 if window_i_end > padded_channels[0].dim().0
666 || window_j_end > padded_channels[0].dim().1
667 {
668 return Err(SignalError::DimensionMismatch(
669 "Window extends beyond padded image bounds".to_string(),
670 ));
671 }
672
673 let kernel_size = 2 * half_kernel + 1;
675 let window_size = kernel_size * kernel_size;
676 let mut window_vectors = Vec::with_capacity(window_size);
677
678 for wi in 0..kernel_size {
679 for wj in 0..kernel_size {
680 let pi = window_i_start + wi;
681 let pj = window_j_start + wj;
682
683 let mut color_vector = Vec::with_capacity(channels);
685 for (_c, padded_channel) in padded_channels.iter().enumerate().take(channels) {
686 color_vector.push(padded_channel[[pi, pj]]);
687 }
688
689 window_vectors.push(color_vector);
690 }
691 }
692
693 let vector_median = find_vector_median(&window_vectors);
695
696 for (c, value) in vector_median.iter().enumerate().take(channels) {
698 filtered[[i, j, c]] = *value;
699 }
700 }
701 }
702
703 Ok(filtered)
704}
705
706#[allow(dead_code)]
711fn find_vector_median(vectors: &[Vec<f64>]) -> Vec<f64> {
712 if vectors.is_empty() {
713 return Vec::new();
714 }
715
716 if vectors.len() == 1 {
717 return vectors[0].clone();
718 }
719
720 let mut min_distance_sum = f64::INFINITY;
722 let mut median_idx = 0;
723
724 for i in 0..vectors.len() {
725 let mut distance_sum = 0.0;
726
727 for j in 0..vectors.len() {
728 if i != j {
729 distance_sum += euclidean_distance(&vectors[i], &vectors[j]);
730 }
731 }
732
733 if distance_sum < min_distance_sum {
734 min_distance_sum = distance_sum;
735 median_idx = i;
736 }
737 }
738
739 vectors[median_idx].clone()
740}
741
742#[allow(dead_code)]
744fn euclidean_distance(v1: &[f64], v2: &[f64]) -> f64 {
745 if v1.len() != v2.len() {
746 return f64::INFINITY;
747 }
748
749 let mut sum_squared = 0.0;
750 for i in 0..v1.len() {
751 let diff = v1[i] - v2[i];
752 sum_squared += diff * diff;
753 }
754
755 sum_squared.sqrt()
756}
757
758#[allow(dead_code)]
772pub fn rank_filter_1d(
773 signal: &Array1<f64>,
774 kernel_size: usize,
775 rank: f64,
776 edge_mode: EdgeMode,
777) -> SignalResult<Array1<f64>> {
778 if kernel_size % 2 != 1 {
780 return Err(SignalError::ValueError(
781 "Kernel _size must be odd".to_string(),
782 ));
783 }
784
785 if !(0.0..=1.0).contains(&rank) {
786 return Err(SignalError::ValueError(
787 "Rank must be between 0.0 and 1.0".to_string(),
788 ));
789 }
790
791 let n = signal.len();
792
793 if n <= 1 || kernel_size > n {
795 return Ok(signal.clone());
796 }
797
798 let half_kernel = kernel_size / 2;
799
800 let paddedsignal = padsignal_1d(signal, half_kernel, edge_mode);
802
803 let mut filtered = Array1::zeros(n);
805
806 for i in 0..n {
808 let window_start = i;
810 let window_end = i + 2 * half_kernel + 1;
811
812 if window_start >= paddedsignal.len() || window_end > paddedsignal.len() {
814 return Err(SignalError::DimensionMismatch(
815 "Window extends beyond padded signal bounds".to_string(),
816 ));
817 }
818
819 let mut window: Vec<f64> = paddedsignal.slice(s![window_start..window_end]).to_vec();
821 window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
822
823 let rank_idx = ((window.len() - 1) as f64 * rank).round() as usize;
825
826 filtered[i] = window[rank_idx];
828 }
829
830 Ok(filtered)
831}
832
833#[allow(dead_code)]
846pub fn hybrid_median_filter_2d(
847 image: &Array2<f64>,
848 kernel_size: usize,
849 config: &MedianConfig,
850) -> SignalResult<Array2<f64>> {
851 if kernel_size % 2 != 1 {
853 return Err(SignalError::ValueError(
854 "Kernel _size must be odd".to_string(),
855 ));
856 }
857
858 let (height, width) = image.dim();
859
860 if height <= 1 || width <= 1 || kernel_size > height || kernel_size > width {
862 return Ok(image.clone());
863 }
864
865 let half_kernel = kernel_size / 2;
866
867 let paddedimage = padimage_2d(image, half_kernel, config.edge_mode);
869
870 let mut filtered = Array2::zeros((height, width));
872
873 for i in 0..height {
875 for j in 0..width {
876 let window_i_start = i;
878 let window_i_end = i + 2 * half_kernel + 1;
879 let window_j_start = j;
880 let window_j_end = j + 2 * half_kernel + 1;
881
882 if window_i_end > paddedimage.dim().0 || window_j_end > paddedimage.dim().1 {
884 return Err(SignalError::DimensionMismatch(
885 "Window extends beyond padded image bounds".to_string(),
886 ));
887 }
888
889 let mut plusshape = Vec::new(); let mut crossshape = Vec::new(); for k in 0..(2 * half_kernel + 1) {
894 plusshape.push(paddedimage[[window_i_start + half_kernel, window_j_start + k]]);
896
897 plusshape.push(paddedimage[[window_i_start + k, window_j_start + half_kernel]]);
899
900 if k < kernel_size {
902 let diag_i = window_i_start + k;
903 let diag_j = window_j_start + k;
904 crossshape.push(paddedimage[[diag_i, diag_j]]);
905 }
906
907 if k < kernel_size {
909 let diag_i = window_i_start + k;
910 let diag_j = window_j_start + kernel_size - 1 - k;
911 crossshape.push(paddedimage[[diag_i, diag_j]]);
912 }
913 }
914
915 if !plusshape.is_empty() {
917 plusshape.pop();
918 }
919
920 plusshape.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
922 crossshape.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
923
924 let plus_median = plusshape[plusshape.len() / 2];
926 let cross_median = crossshape[crossshape.len() / 2];
927
928 let orig_value =
930 paddedimage[[window_i_start + half_kernel, window_j_start + half_kernel]];
931
932 let mut final_values = [plus_median, cross_median, orig_value];
934 final_values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
935
936 filtered[[i, j]] = final_values[1];
938 }
939 }
940
941 Ok(filtered)
942}
943
944#[allow(dead_code)]
946fn padsignal_1d(signal: &Array1<f64>, pad_size: usize, edgemode: EdgeMode) -> Array1<f64> {
947 let n = signal.len();
948 let mut padded = Array1::zeros(n + 2 * pad_size);
949
950 for i in 0..n {
952 padded[i + pad_size] = signal[i];
953 }
954
955 match edgemode {
957 EdgeMode::Reflect => {
958 for i in 0..pad_size {
960 padded[pad_size - 1 - i] = signal[i.min(n - 1)];
962
963 padded[n + pad_size + i] = signal[n - 1 - i.min(n - 1)];
965 }
966 }
967 EdgeMode::Nearest => {
968 let first_val = signal[0];
970 let last_val = signal[n - 1];
971
972 for i in 0..pad_size {
973 padded[i] = first_val;
974 padded[n + pad_size + i] = last_val;
975 }
976 }
977 EdgeMode::Constant(value) => {
978 for i in 0..pad_size {
980 padded[i] = value;
981 padded[n + pad_size + i] = value;
982 }
983 }
984 EdgeMode::Wrap => {
985 for i in 0..pad_size {
987 padded[i] = signal[(n - pad_size + i) % n];
988 padded[n + pad_size + i] = signal[i % n];
989 }
990 }
991 }
992
993 padded
994}
995
996#[allow(dead_code)]
998fn padimage_2d(image: &Array2<f64>, pad_size: usize, edgemode: EdgeMode) -> Array2<f64> {
999 let (height, width) = image.dim();
1000 let mut padded = Array2::zeros((height + 2 * pad_size, width + 2 * pad_size));
1001
1002 for i in 0..height {
1004 for j in 0..width {
1005 padded[[i + pad_size, j + pad_size]] = image[[i, j]];
1006 }
1007 }
1008
1009 match edgemode {
1011 EdgeMode::Reflect => {
1012 for i in 0..pad_size {
1016 for j in 0..width {
1017 padded[[pad_size - 1 - i, j + pad_size]] = image[[i.min(height - 1), j]];
1019
1020 padded[[height + pad_size + i, j + pad_size]] =
1022 image[[height - 1 - i.min(height - 1), j]];
1023 }
1024 }
1025
1026 for i in 0..height + 2 * pad_size {
1028 for j in 0..pad_size {
1029 let src_i = if i < pad_size {
1031 2 * pad_size - i - 1
1032 } else if i >= height + pad_size {
1033 2 * (height + pad_size) - i - 1
1034 } else {
1035 i
1036 };
1037
1038 padded[[i, pad_size - 1 - j]] = padded[[src_i, pad_size + j.min(width - 1)]];
1040
1041 padded[[i, width + pad_size + j]] =
1043 padded[[src_i, width + pad_size - 1 - j.min(width - 1)]];
1044 }
1045 }
1046 }
1047 EdgeMode::Nearest => {
1048 for i in 0..pad_size {
1052 for j in 0..width {
1053 padded[[i, j + pad_size]] = image[[0, j]];
1055
1056 padded[[height + pad_size + i, j + pad_size]] = image[[height - 1, j]];
1058 }
1059 }
1060
1061 for i in 0..height + 2 * pad_size {
1063 for j in 0..pad_size {
1064 let col_left = 0;
1066 let col_right = width - 1;
1067
1068 let row = if i < pad_size {
1070 0
1071 } else if i >= height + pad_size {
1072 height - 1
1073 } else {
1074 i - pad_size
1075 };
1076
1077 padded[[i, j]] = image[[row, col_left]];
1079
1080 padded[[i, width + pad_size + j]] = image[[row, col_right]];
1082 }
1083 }
1084 }
1085 EdgeMode::Constant(value) => {
1086 for i in 0..pad_size {
1090 for j in 0..width + 2 * pad_size {
1091 padded[[i, j]] = value;
1092 padded[[height + pad_size + i, j]] = value;
1093 }
1094 }
1095
1096 for i in pad_size..height + pad_size {
1098 for j in 0..pad_size {
1099 padded[[i, j]] = value;
1100 padded[[i, width + pad_size + j]] = value;
1101 }
1102 }
1103 }
1104 EdgeMode::Wrap => {
1105 for i in 0..pad_size {
1109 for j in 0..width {
1110 padded[[i, j + pad_size]] = image[[(height - pad_size + i) % height, j]];
1112
1113 padded[[height + pad_size + i, j + pad_size]] = image[[i % height, j]];
1115 }
1116 }
1117
1118 for i in 0..height + 2 * pad_size {
1120 for j in 0..pad_size {
1121 let src_i = if i < pad_size {
1123 (height - pad_size + i) % height + pad_size
1124 } else if i >= height + pad_size {
1125 (i - pad_size) % height + pad_size
1126 } else {
1127 i
1128 };
1129
1130 padded[[i, j]] = padded[[src_i, width + j]];
1132
1133 padded[[i, width + pad_size + j]] = padded[[src_i, pad_size + j]];
1135 }
1136 }
1137 }
1138 }
1139
1140 padded
1141}