pic_scale/
sampler.rs

1/*
2 * Copyright (c) Radzivon Bartoshyk. 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#![allow(clippy::excessive_precision)]
30
31use crate::bartlett::{bartlett, bartlett_hann};
32use crate::bc_spline::{
33    b_spline, catmull_rom, hermite_spline, mitchell_netravalli, robidoux, robidoux_sharp,
34};
35use crate::bilinear::bilinear;
36use crate::blackman::blackman;
37use crate::bohman::bohman;
38use crate::cubic::{bicubic_spline, cubic_spline};
39use crate::gaussian::gaussian;
40use crate::hann::{hamming, hann, hanning};
41use crate::kaiser::kaiser;
42use crate::lagrange::{lagrange2, lagrange3};
43use crate::lanczos::{
44    lanczos2, lanczos2_jinc, lanczos3, lanczos3_jinc, lanczos4, lanczos4_jinc, lanczos6,
45    lanczos6_jinc,
46};
47use crate::math::gaussian::Exponential;
48use crate::math::sinc::Trigonometry;
49use crate::quadric::quadric;
50use crate::sinc::sinc;
51use crate::sphinx::sphinx;
52use crate::spline_n::{spline16, spline36, spline64};
53use crate::welch::welch;
54use crate::{ConstPI, ConstSqrt2, Jinc};
55use num_traits::{AsPrimitive, Float, Signed};
56use std::ops::{AddAssign, MulAssign, Neg};
57
58#[inline(always)]
59pub(crate) fn box_weight<V: Copy + 'static>(_: V) -> V
60where
61    f32: AsPrimitive<V>,
62{
63    1f32.as_()
64}
65
66#[derive(Debug, Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq)]
67/// Describes resampling function that will be used
68pub enum ResamplingFunction {
69    Bilinear,
70    Nearest,
71    Cubic,
72    #[default]
73    MitchellNetravalli,
74    CatmullRom,
75    Hermite,
76    BSpline,
77    Hann,
78    Bicubic,
79    Hamming,
80    Hanning,
81    EwaHanning,
82    Blackman,
83    EwaBlackman,
84    Welch,
85    Quadric,
86    EwaQuadric,
87    Gaussian,
88    Sphinx,
89    Bartlett,
90    Robidoux,
91    EwaRobidoux,
92    RobidouxSharp,
93    EwaRobidouxSharp,
94    Spline16,
95    Spline36,
96    Spline64,
97    Kaiser,
98    BartlettHann,
99    Box,
100    Bohman,
101    Lanczos2,
102    Lanczos3,
103    Lanczos4,
104    Lanczos2Jinc,
105    Lanczos3Jinc,
106    Lanczos4Jinc,
107    EwaLanczos3Jinc,
108    Ginseng,
109    EwaGinseng,
110    EwaLanczosSharp,
111    EwaLanczos4Sharpest,
112    EwaLanczosSoft,
113    HaasnSoft,
114    Lagrange2,
115    Lagrange3,
116    Lanczos6,
117    Lanczos6Jinc,
118    /// This method replicates `INTER_AREA` behaviour from OpenCV
119    Area,
120}
121
122impl From<u32> for ResamplingFunction {
123    fn from(value: u32) -> Self {
124        match value {
125            0 => ResamplingFunction::Bilinear,
126            1 => ResamplingFunction::Nearest,
127            2 => ResamplingFunction::Cubic,
128            3 => ResamplingFunction::MitchellNetravalli,
129            4 => ResamplingFunction::CatmullRom,
130            5 => ResamplingFunction::Hermite,
131            6 => ResamplingFunction::BSpline,
132            7 => ResamplingFunction::Hann,
133            8 => ResamplingFunction::Bicubic,
134            9 => ResamplingFunction::Hamming,
135            10 => ResamplingFunction::Hanning,
136            11 => ResamplingFunction::Blackman,
137            12 => ResamplingFunction::Welch,
138            13 => ResamplingFunction::Quadric,
139            14 => ResamplingFunction::Gaussian,
140            15 => ResamplingFunction::Sphinx,
141            16 => ResamplingFunction::Bartlett,
142            17 => ResamplingFunction::Robidoux,
143            18 => ResamplingFunction::RobidouxSharp,
144            19 => ResamplingFunction::Spline16,
145            20 => ResamplingFunction::Spline36,
146            21 => ResamplingFunction::Spline64,
147            22 => ResamplingFunction::Kaiser,
148            23 => ResamplingFunction::BartlettHann,
149            24 => ResamplingFunction::Box,
150            25 => ResamplingFunction::Bohman,
151            26 => ResamplingFunction::Lanczos2,
152            27 => ResamplingFunction::Lanczos3,
153            28 => ResamplingFunction::Lanczos4,
154            29 => ResamplingFunction::Lanczos2Jinc,
155            30 => ResamplingFunction::Lanczos3Jinc,
156            31 => ResamplingFunction::Lanczos4Jinc,
157            32 => ResamplingFunction::EwaHanning,
158            33 => ResamplingFunction::EwaRobidoux,
159            34 => ResamplingFunction::EwaBlackman,
160            35 => ResamplingFunction::EwaQuadric,
161            36 => ResamplingFunction::EwaRobidouxSharp,
162            37 => ResamplingFunction::EwaLanczos3Jinc,
163            38 => ResamplingFunction::Ginseng,
164            39 => ResamplingFunction::EwaGinseng,
165            40 => ResamplingFunction::EwaLanczosSharp,
166            41 => ResamplingFunction::EwaLanczos4Sharpest,
167            42 => ResamplingFunction::EwaLanczosSoft,
168            43 => ResamplingFunction::HaasnSoft,
169            44 => ResamplingFunction::Lagrange2,
170            45 => ResamplingFunction::Lagrange3,
171            46 => ResamplingFunction::Lanczos6,
172            47 => ResamplingFunction::Lanczos6Jinc,
173            48 => ResamplingFunction::Area,
174            _ => ResamplingFunction::Bilinear,
175        }
176    }
177}
178
179/// Resampling window representation
180#[derive(Debug, Copy, Clone)]
181pub(crate) struct ResamplingWindow<T> {
182    pub(crate) window: fn(T) -> T,
183    pub(crate) window_size: f32,
184    pub(crate) blur: f32,
185    pub(crate) taper: f32,
186}
187
188impl<T> ResamplingWindow<T> {
189    fn new(window: fn(T) -> T, window_size: f32, blur: f32, taper: f32) -> ResamplingWindow<T> {
190        ResamplingWindow {
191            window,
192            window_size,
193            blur,
194            taper,
195        }
196    }
197}
198
199/// Resampling filter representation
200#[derive(Debug, Copy, Clone)]
201pub(crate) struct ResamplingFilter<T> {
202    pub kernel: fn(T) -> T,
203    pub window: Option<ResamplingWindow<T>>,
204    pub min_kernel_size: f32,
205    pub is_ewa: bool,
206    pub is_resizable_kernel: bool,
207    pub is_area: bool,
208}
209
210impl<T> ResamplingFilter<T> {
211    fn new(kernel: fn(T) -> T, min_kernel_size: f32, is_ewa: bool) -> ResamplingFilter<T> {
212        ResamplingFilter {
213            kernel,
214            window: None,
215            min_kernel_size,
216            is_ewa,
217            is_resizable_kernel: true,
218            is_area: false,
219        }
220    }
221
222    fn new_with_window(
223        kernel: fn(T) -> T,
224        window: ResamplingWindow<T>,
225        min_kernel_size: f32,
226        is_ewa: bool,
227    ) -> ResamplingFilter<T> {
228        ResamplingFilter::<T> {
229            kernel,
230            window: Some(window),
231            min_kernel_size,
232            is_ewa,
233            is_resizable_kernel: true,
234            is_area: false,
235        }
236    }
237
238    fn new_with_fixed_kernel(
239        kernel: fn(T) -> T,
240        min_kernel_size: f32,
241        is_ewa: bool,
242    ) -> ResamplingFilter<T> {
243        ResamplingFilter::<T> {
244            kernel,
245            window: None,
246            min_kernel_size,
247            is_ewa,
248            is_resizable_kernel: false,
249            is_area: false,
250        }
251    }
252
253    fn new_with_area(kernel: fn(T) -> T, min_kernel_size: f32) -> ResamplingFilter<T> {
254        ResamplingFilter {
255            kernel,
256            window: None,
257            min_kernel_size,
258            is_ewa: false,
259            is_resizable_kernel: true,
260            is_area: true,
261        }
262    }
263}
264
265const JINC_R3: f32 = 3.2383154841662362f32;
266const JINC_R4: f32 = 4.2410628637960699f32;
267
268impl ResamplingFunction {
269    pub(crate) fn get_resampling_filter<T>(&self) -> ResamplingFilter<T>
270    where
271        T: Copy
272            + Neg
273            + Signed
274            + Float
275            + 'static
276            + ConstPI
277            + MulAssign<T>
278            + AddAssign<T>
279            + AsPrimitive<f64>
280            + AsPrimitive<usize>
281            + Jinc<T>
282            + ConstSqrt2
283            + Trigonometry
284            + Exponential,
285        f32: AsPrimitive<T>,
286        f64: AsPrimitive<T>,
287        usize: AsPrimitive<T>,
288    {
289        match self {
290            ResamplingFunction::Bilinear => ResamplingFilter::new(bilinear, 2f32, false),
291            ResamplingFunction::Nearest => {
292                // Just a stab for nearest
293                ResamplingFilter::new(bilinear, 2f32, false)
294            }
295            ResamplingFunction::Cubic => ResamplingFilter::new(cubic_spline, 2f32, false),
296            ResamplingFunction::MitchellNetravalli => {
297                ResamplingFilter::new(mitchell_netravalli, 2f32, false)
298            }
299            ResamplingFunction::Lanczos3 => ResamplingFilter::new(lanczos3, 3f32, false),
300            ResamplingFunction::CatmullRom => ResamplingFilter::new(catmull_rom, 2f32, false),
301            ResamplingFunction::Hermite => ResamplingFilter::new(hermite_spline, 2f32, false),
302            ResamplingFunction::BSpline => ResamplingFilter::new(b_spline, 2f32, false),
303            ResamplingFunction::Hann => ResamplingFilter::new(hann, 3f32, false),
304            ResamplingFunction::Bicubic => ResamplingFilter::new(bicubic_spline, 2f32, false),
305            ResamplingFunction::Lanczos4 => ResamplingFilter::new(lanczos4, 4f32, false),
306            ResamplingFunction::Lanczos2 => ResamplingFilter::new(lanczos2, 2f32, false),
307            ResamplingFunction::Hamming => ResamplingFilter::new(hamming, 2f32, false),
308            ResamplingFunction::Hanning => ResamplingFilter::new(hanning, 2f32, false),
309            ResamplingFunction::EwaHanning => ResamplingFilter::new_with_window(
310                T::jinc(),
311                ResamplingWindow::new(hanning, 2f32, 0f32, 0f32),
312                1f32,
313                true,
314            ),
315            ResamplingFunction::Welch => ResamplingFilter::new(welch, 2f32, false),
316            ResamplingFunction::Quadric => ResamplingFilter::new(quadric, 2f32, false),
317            ResamplingFunction::EwaQuadric => ResamplingFilter::new(quadric, 2f32, true),
318            ResamplingFunction::Gaussian => ResamplingFilter::new(gaussian, 2f32, false),
319            ResamplingFunction::Sphinx => ResamplingFilter::new(sphinx, 2f32, false),
320            ResamplingFunction::Bartlett => ResamplingFilter::new(bartlett, 2f32, false),
321            ResamplingFunction::Robidoux => ResamplingFilter::new(robidoux, 2f32, false),
322            ResamplingFunction::EwaRobidoux => ResamplingFilter::new(robidoux, 2f32, true),
323            ResamplingFunction::RobidouxSharp => ResamplingFilter::new(robidoux_sharp, 2f32, false),
324            ResamplingFunction::EwaRobidouxSharp => {
325                ResamplingFilter::new(robidoux_sharp, 2f32, true)
326            }
327            ResamplingFunction::Spline16 => {
328                ResamplingFilter::new_with_fixed_kernel(spline16, 2f32, false)
329            }
330            ResamplingFunction::Spline36 => {
331                ResamplingFilter::new_with_fixed_kernel(spline36, 4f32, false)
332            }
333            ResamplingFunction::Spline64 => {
334                ResamplingFilter::new_with_fixed_kernel(spline64, 6f32, false)
335            }
336            ResamplingFunction::Kaiser => ResamplingFilter::new(kaiser, 2f32, false),
337            ResamplingFunction::BartlettHann => ResamplingFilter::new(bartlett_hann, 2f32, false),
338            ResamplingFunction::Box => ResamplingFilter::new(box_weight, 2f32, false),
339            ResamplingFunction::Area => ResamplingFilter::new_with_area(box_weight, 2f32),
340            ResamplingFunction::Bohman => ResamplingFilter::new(bohman, 2f32, false),
341            ResamplingFunction::Lanczos2Jinc => ResamplingFilter::new(lanczos2_jinc, 2f32, false),
342            ResamplingFunction::Lanczos3Jinc => ResamplingFilter::new(lanczos3_jinc, 3f32, false),
343            ResamplingFunction::EwaLanczos3Jinc => ResamplingFilter::new(lanczos3_jinc, 3f32, true),
344            ResamplingFunction::Lanczos4Jinc => ResamplingFilter::new(lanczos4_jinc, 4f32, false),
345            ResamplingFunction::Blackman => ResamplingFilter::new(blackman, 2f32, false),
346            ResamplingFunction::EwaBlackman => ResamplingFilter::new(blackman, 2f32, true),
347            ResamplingFunction::Ginseng => ResamplingFilter::new_with_window(
348                sinc,
349                ResamplingWindow::new(T::jinc(), 3f32, 1f32, 0f32),
350                3f32,
351                false,
352            ),
353            ResamplingFunction::EwaGinseng => ResamplingFilter::new_with_window(
354                sinc,
355                ResamplingWindow::new(T::jinc(), JINC_R3, 1f32, 0f32),
356                3f32,
357                true,
358            ),
359            ResamplingFunction::EwaLanczosSharp => ResamplingFilter::new_with_window(
360                T::jinc(),
361                ResamplingWindow::new(T::jinc(), JINC_R3, 0.9812505837223707f32, 0f32),
362                3f32,
363                true,
364            ),
365            ResamplingFunction::EwaLanczos4Sharpest => ResamplingFilter::new_with_window(
366                T::jinc(),
367                ResamplingWindow::new(T::jinc(), JINC_R4, 0.8845120932605005f32, 0f32),
368                4f32,
369                true,
370            ),
371            ResamplingFunction::EwaLanczosSoft => ResamplingFilter::new_with_window(
372                T::jinc(),
373                ResamplingWindow::new(T::jinc(), JINC_R3, 1.0164667662867047f32, 0f32),
374                3f32,
375                true,
376            ),
377            ResamplingFunction::HaasnSoft => ResamplingFilter::new_with_window(
378                T::jinc(),
379                ResamplingWindow::new(hanning, 3f32, 1.11f32, 0f32),
380                3f32,
381                false,
382            ),
383            ResamplingFunction::Lagrange2 => ResamplingFilter::new(lagrange2, 2f32, false),
384            ResamplingFunction::Lagrange3 => ResamplingFilter::new(lagrange3, 3f32, false),
385            ResamplingFunction::Lanczos6Jinc => ResamplingFilter::new(lanczos6_jinc, 6f32, false),
386            ResamplingFunction::Lanczos6 => ResamplingFilter::new(lanczos6, 6f32, false),
387        }
388    }
389}