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}