Coverage Report

Created: 2025-12-05 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pic-scale-safe-0.1.5/src/compute_weights.rs
Line
Count
Source
1
/*
2
 * Copyright (c) Radzivon Bartoshyk, 10/2024. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or without modification,
5
 * are permitted provided that the following conditions are met:
6
 *
7
 * 1.  Redistributions of source code must retain the above copyright notice, this
8
 * list of conditions and the following disclaimer.
9
 *
10
 * 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * this list of conditions and the following disclaimer in the documentation
12
 * and/or other materials provided with the distribution.
13
 *
14
 * 3.  Neither the name of the copyright holder nor the names of its
15
 * contributors may be used to endorse or promote products derived from
16
 * this software without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::filter_weights::{FilterBounds, FilterWeights};
30
use crate::math::{ConstPI, ConstSqrt2, Jinc};
31
use crate::sampler::ResamplingFunction;
32
use num_traits::{AsPrimitive, Float, Signed};
33
use std::fmt::Debug;
34
use std::ops::{AddAssign, Div, MulAssign, Neg};
35
36
0
pub(crate) fn generate_weights<T>(
37
0
    function: ResamplingFunction,
38
0
    in_size: usize,
39
0
    out_size: usize,
40
0
) -> FilterWeights<T>
41
0
where
42
0
    T: Copy
43
0
        + Neg
44
0
        + Signed
45
0
        + Float
46
0
        + 'static
47
0
        + ConstPI
48
0
        + MulAssign<T>
49
0
        + AddAssign<T>
50
0
        + AsPrimitive<f64>
51
0
        + AsPrimitive<i64>
52
0
        + AsPrimitive<usize>
53
0
        + Jinc<T>
54
0
        + ConstSqrt2
55
0
        + Default
56
0
        + AsPrimitive<i32>
57
0
        + Div<T, Output = T>
58
0
        + Debug,
59
0
    f32: AsPrimitive<T>,
60
0
    f64: AsPrimitive<T>,
61
0
    i64: AsPrimitive<T>,
62
0
    i32: AsPrimitive<T>,
63
0
    usize: AsPrimitive<T>,
64
{
65
0
    let resampling_filter = function.get_resampling_filter();
66
0
    let scale = in_size.as_() / out_size.as_();
67
0
    let is_resizable_kernel = resampling_filter.is_resizable_kernel;
68
0
    let filter_scale_cutoff = match is_resizable_kernel {
69
0
        true => scale.max(1f32.as_()),
70
0
        false => 1f32.as_(),
71
    };
72
0
    let filter_base_size = resampling_filter.min_kernel_size * 2.;
73
0
    let resampling_function = resampling_filter.kernel;
74
0
    let window_func = resampling_filter.window;
75
76
0
    let mut bounds: Vec<FilterBounds> = vec![FilterBounds::new(0, 0); out_size];
77
78
0
    let is_area = resampling_filter.is_area_filter && scale < 1.as_();
79
80
0
    if !is_area {
81
0
        let base_size: usize = (filter_base_size.as_() * filter_scale_cutoff).round().as_();
82
0
        let kernel_size = base_size;
83
0
        let filter_radius = base_size.as_() / 2.as_();
84
0
        let filter_scale = 1f32.as_() / filter_scale_cutoff;
85
0
        let mut weights: Vec<T> = vec![T::default(); kernel_size * out_size];
86
0
        let mut local_filters = vec![T::default(); kernel_size];
87
0
        let mut filter_position = 0usize;
88
0
        let blur_scale = match window_func {
89
0
            None => 1f32.as_(),
90
0
            Some(window) => {
91
0
                if window.blur.as_() > 0f32.as_() {
92
0
                    1f32.as_() / window.blur.as_()
93
                } else {
94
0
                    0f32.as_()
95
                }
96
            }
97
        };
98
99
0
        for (i, bound) in bounds.iter_mut().enumerate() {
100
0
            let center_x = ((i.as_() + 0.5.as_()) * scale).min(in_size.as_());
101
0
            let mut weights_sum: T = 0f32.as_();
102
103
0
            let start: usize = (center_x - filter_radius).floor().max(0f32.as_()).as_();
104
0
            let end: usize = (center_x + filter_radius)
105
0
                .ceil()
106
0
                .min(in_size.as_())
107
0
                .min(start.as_() + kernel_size.as_())
108
0
                .as_();
109
110
0
            let center = center_x - 0.5.as_();
111
112
0
            for (local_filter_iteration, k) in (start..end).enumerate() {
113
0
                let dx = k.as_() - center;
114
                let weight;
115
0
                if let Some(resampling_window) = window_func {
116
0
                    let mut x = dx.abs();
117
0
                    x = if resampling_window.blur.as_() > 0f32.as_() {
118
0
                        x * blur_scale
119
                    } else {
120
0
                        x
121
                    };
122
0
                    x = if x <= resampling_window.taper.as_() {
123
0
                        0f32.as_()
124
                    } else {
125
0
                        (x - resampling_window.taper.as_())
126
0
                            / (1f32.as_() - resampling_window.taper.as_())
127
                    };
128
0
                    let window_producer = resampling_window.window;
129
0
                    let x_kernel_scaled = x * filter_scale;
130
0
                    let window = if x < resampling_window.window_size.as_() {
131
0
                        window_producer(x_kernel_scaled * resampling_window.window_size.as_())
132
                    } else {
133
0
                        0f32.as_()
134
                    };
135
0
                    weight = window * resampling_function(x_kernel_scaled);
136
0
                } else {
137
0
                    let dx = dx.abs();
138
0
                    weight = resampling_function(dx * filter_scale);
139
0
                }
140
0
                weights_sum += weight;
141
0
                local_filters[local_filter_iteration] = weight;
142
            }
143
144
0
            let size = end - start;
145
146
0
            *bound = FilterBounds::new(start, size);
147
148
0
            if weights_sum != 0f32.as_() {
149
0
                let recpeq = 1f32.as_() / weights_sum;
150
151
0
                for (dst, src) in weights
152
0
                    .iter_mut()
153
0
                    .skip(filter_position)
154
0
                    .take(size)
155
0
                    .zip(local_filters.iter().take(size))
156
0
                {
157
0
                    *dst = *src * recpeq;
158
0
                }
159
0
            }
160
161
0
            filter_position += kernel_size;
162
        }
163
164
0
        FilterWeights::<T>::new(
165
0
            weights,
166
0
            kernel_size,
167
0
            kernel_size,
168
0
            out_size,
169
0
            filter_radius.as_(),
170
0
            bounds,
171
        )
172
    } else {
173
        // Simulating INTER_AREA from OpenCV, for up scaling here,
174
        // this is necessary because weight computation is different
175
        // from any other func
176
0
        let inv_scale: T = 1.as_() / scale;
177
0
        let kernel_size = 2;
178
0
        let filter_radius: T = 1.as_();
179
0
        let mut weights: Vec<T> = vec![T::default(); kernel_size * out_size];
180
0
        let mut local_filters = vec![T::default(); kernel_size];
181
0
        let mut filter_position = 0usize;
182
183
0
        for (i, bound) in bounds.iter_mut().enumerate() {
184
0
            let mut weights_sum: T = 0f32.as_();
185
186
0
            let sx: T = (i.as_() * scale).floor();
187
0
            let fx = (i as i64 + 1).as_() - (sx + 1.as_()) * inv_scale;
188
0
            let dx = if fx <= 0.as_() {
189
0
                0.as_()
190
            } else {
191
0
                fx - fx.floor()
192
            };
193
0
            let dx = dx.abs();
194
0
            let weight0 = 1.as_() - dx;
195
0
            let weight1: T = dx;
196
0
            local_filters[0] = weight0;
197
0
            local_filters[1] = weight1;
198
199
0
            let start: usize = sx.floor().max(0f32.as_()).as_();
200
0
            let end: usize = (sx + kernel_size.as_())
201
0
                .ceil()
202
0
                .min(in_size.as_())
203
0
                .min(start.as_() + kernel_size.as_())
204
0
                .as_();
205
206
0
            let size = end - start;
207
208
0
            weights_sum += weight0;
209
0
            if size > 1 {
210
0
                weights_sum += weight1;
211
0
            }
212
0
            *bound = FilterBounds::new(start, size);
213
214
0
            if weights_sum != 0f32.as_() {
215
0
                let recpeq = 1f32.as_() / weights_sum;
216
217
0
                for (dst, src) in weights
218
0
                    .iter_mut()
219
0
                    .skip(filter_position)
220
0
                    .take(size)
221
0
                    .zip(local_filters.iter().take(size))
222
0
                {
223
0
                    *dst = *src * recpeq;
224
0
                }
225
0
            } else {
226
0
                weights[filter_position] = 1.as_();
227
0
            }
228
229
0
            filter_position += kernel_size;
230
        }
231
232
0
        FilterWeights::new(
233
0
            weights,
234
0
            kernel_size,
235
0
            kernel_size,
236
0
            out_size,
237
0
            filter_radius.as_(),
238
0
            bounds,
239
        )
240
    }
241
0
}