/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 | } |