Skip to main content

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