Some of you may be familiar with the blog post I made about mpv's scaling filters a while ago, but it was never really meant to be shared as much as it did. That page was originally the result of an assignment that I had during my undergrad, when I was formally studying digital image processing for the first time. Naturally, it was full of mistakes and the results weren't particularly scientific. I got a lot of feedback and the page ended up evolving in an organic way, but it still has some fundamental issues that can not be fixed without a major change in the methodology.
From the top of my head I can enumerate the following problems:
So, to address these issues:
With that out of the way, we can proceed with the real introduction.
Resampling is the process of changing the number of samples of a discrete signal to obtain a new discrete representation of the underlying continuous signal. This definition comes from the idea of having a sensor of some kind producing an analog continuous-time voltage/current variance which is then periodically sampled and quantised into predefined amplitude levels so we can store it in bits/bytes.
The easiest and most classic way of resampling to a higher sample rate is via linear interpolation, if you want to find a value between two points you can simply draw a line between them. Linear interpolation can be done in a cartesian plane, through both axis, creating what we call "bilinear" interpolation. Bilinear interpolation is the simplest interpolation algorithm, the easiest to compute and probably the most widespread one.
But can something as simple as just drawing a line between 2 points give us good results? Sometimes it does, sometimes it doesn't. It really depends on the signal. Instead of taking 2 points and drawing a line, we could take more than 2 points and draw a higher-degree curve. The shape of the curve depends on the weights used in the calculation, and these weights depend on the chosen filter. The number of input samples in the calculation depends on the length/radius/support of the filter. If you want to understand how this is actually done, I suggest simply reading this explanation.
In short, the most common way of resampling images is treating each row/column as an independent 1-D signal and simply going over all of them until you have resampled the entire image. This means you have to choose a dimension to resample first but this is pretty much inconsequential to the end result. The resampling algorithm itself is pretty simple, for each output sample you simply centralise the filter on top of it and see which input samples end up inside of the window after you compute their equivalent positions, then you multiply these inputs by their corresponding weights depending on their distance to the centre.
There's a different method, usually called polar/cylindrical/elliptical resampling, that does the operation in the 2-D domain directly. The only difference here is that all samples that fit inside the 2-D filter will now be weighted simultaenously, which may drastically change how some filters behave since we're only calling the filter once per output pixel rather than twice as before.
In this page I'll include results for both orthogonal and polar resampling. It's important to note that polar resampling implementations are generally slower and most filters weren't really designed to be used in this way (the most "famous" exception being polar lanczos, since we replace the sinc function with its 2-D "equivalent", the jinc/sombrero function).
As stated before the entire Manga109 dataset will be used in this comparison. This dataset has manga covers that look like this:
The dataset is downscaled with:
magick mogrify -colorspace rgb -filter box -resize 50% -colorspace srgb -path low_res inputs/*.png
The dataset is then brought back up with the following command for orthogonal resampling:
magick mogrify -colorspace RGB +sigmoidal-contrast 7.5 -filter {resampling_filter} -resize 200% -sigmoidal-contrast 7.5 -colorspace sRGB -path high_res low_res/*.png
And the following command for polar resampling:
magick mogrify -colorspace RGB +sigmoidal-contrast 7.5 -filter {resampling_filter} -distort Resize 200% -sigmoidal-contrast 7.5 -colorspace sRGB -path high_res low_res/*.png
The
['Bartlett',
'Blackman',
'Bohman',
'Box',
'Catrom',
'Cosine',
'Cubic',
'Gaussian',
'Hamming',
'Hann',
'Hermite',
'Jinc',
'Kaiser',
'Lagrange',
'Lanczos',
'Lanczos2',
'Lanczos2Sharp',
'LanczosRadius',
'LanczosSharp',
'Mitchell',
'Parzen',
'Point',
'Quadratic',
'Robidoux',
'RobidouxSharp',
'Sinc',
'SincFast',
'Spline',
'CubicSpline',
'Triangle',
'Welch']
For a much better explanation of these filters, please check this page from Imagemagick.
The result is then evaluated with MAE, PSNR, SSIM and MS-SSIM. All calculations are done with all three RGB channels and in double-precision floating-point for accuracy. Metrics are normalised between [0, 1] and then averaged together with a normal arithmetic mean.
Please note that some of those filters are just aliases to other filters or aliases to sinc/jinc with different windows.
LanczosRadius is supposed to be a jinc-windowed jinc sharpened to have its third zero crossing at 3 instead of 3.2383154841662362.
LanczosSharp and Lanczos2Sharp are both supposed to be sharper jinc-windowed jincs since the original filters ended up too soft when compared to their sinc-windowed sinc orthogonal equivalents.
Filter | MAE | PSNR | SSIM | MS-SSIM | MAE (N) | PSNR (N) | SSIM (N) | MS-SSIM (N) | Mean |
LanczosSharp | 0.0163 | 30.3992 | 0.9391 | 0.9952 | 1.0000 | 0.9973 | 0.9716 | 0.9944 | 0.9908 |
Polar_Catrom | 0.0164 | 30.2800 | 0.9411 | 0.9953 | 0.9860 | 0.9704 | 1.0000 | 1.0000 | 0.9891 |
Hamming | 0.0163 | 30.4113 | 0.9384 | 0.9950 | 0.9932 | 1.0000 | 0.9621 | 0.9826 | 0.9845 |
Cosine | 0.0164 | 30.3786 | 0.9378 | 0.9950 | 0.9859 | 0.9926 | 0.9536 | 0.9796 | 0.9779 |
Polar_Lagrange | 0.0166 | 30.3581 | 0.9396 | 0.9949 | 0.9686 | 0.9880 | 0.9784 | 0.9766 | 0.9779 |
Welch | 0.0164 | 30.3915 | 0.9376 | 0.9949 | 0.9834 | 0.9955 | 0.9497 | 0.9781 | 0.9767 |
Lanczos | 0.0164 | 30.3221 | 0.9379 | 0.9949 | 0.9837 | 0.9799 | 0.9550 | 0.9791 | 0.9744 |
LanczosRadius | 0.0164 | 30.3221 | 0.9379 | 0.9949 | 0.9837 | 0.9799 | 0.9550 | 0.9791 | 0.9744 |
Hann | 0.0165 | 30.2987 | 0.9377 | 0.9948 | 0.9779 | 0.9746 | 0.9515 | 0.9716 | 0.9689 |
Kaiser | 0.0165 | 30.2733 | 0.9378 | 0.9948 | 0.9784 | 0.9688 | 0.9533 | 0.9731 | 0.9684 |
Bartlett | 0.0165 | 30.2282 | 0.9377 | 0.9949 | 0.9764 | 0.9586 | 0.9520 | 0.9752 | 0.9656 |
Blackman | 0.0166 | 30.1354 | 0.9372 | 0.9947 | 0.9636 | 0.9377 | 0.9440 | 0.9667 | 0.9530 |
Bohman | 0.0166 | 30.1085 | 0.9371 | 0.9947 | 0.9613 | 0.9316 | 0.9425 | 0.9667 | 0.9505 |
Polar_LanczosRadius | 0.0167 | 30.1668 | 0.9355 | 0.9946 | 0.9536 | 0.9448 | 0.9205 | 0.9570 | 0.9440 |
Parzen | 0.0168 | 29.9988 | 0.9364 | 0.9946 | 0.9473 | 0.9068 | 0.9329 | 0.9612 | 0.9371 |
Polar_RobidouxSharp | 0.0172 | 29.7304 | 0.9337 | 0.9946 | 0.9070 | 0.8462 | 0.8951 | 0.9598 | 0.9020 |
Lanczos2Sharp | 0.0172 | 29.6361 | 0.9344 | 0.9947 | 0.8993 | 0.8249 | 0.9048 | 0.9644 | 0.8984 |
Polar_CubicSpline | 0.0176 | 29.7738 | 0.9369 | 0.9933 | 0.8587 | 0.8560 | 0.9408 | 0.8811 | 0.8842 |
Sinc | 0.0179 | 30.2249 | 0.9297 | 0.9937 | 0.8327 | 0.9579 | 0.8377 | 0.9046 | 0.8832 |
SincFast | 0.0179 | 30.2249 | 0.9297 | 0.9937 | 0.8327 | 0.9579 | 0.8377 | 0.9046 | 0.8832 |
Polar_LanczosSharp | 0.0173 | 29.8697 | 0.9308 | 0.9936 | 0.8888 | 0.8777 | 0.8534 | 0.8982 | 0.8795 |
CubicSpline | 0.0175 | 29.6101 | 0.9318 | 0.9938 | 0.8765 | 0.8190 | 0.8676 | 0.9129 | 0.8690 |
Lanczos2 | 0.0175 | 29.5422 | 0.9322 | 0.9940 | 0.8708 | 0.8037 | 0.8741 | 0.9248 | 0.8683 |
Catrom | 0.0175 | 29.4788 | 0.9320 | 0.9940 | 0.8687 | 0.7894 | 0.8705 | 0.9261 | 0.8637 |
Polar_Cosine | 0.0176 | 29.7521 | 0.9289 | 0.9932 | 0.8627 | 0.8511 | 0.8266 | 0.8749 | 0.8538 |
Polar_Lanczos | 0.0176 | 29.7521 | 0.9289 | 0.9932 | 0.8627 | 0.8511 | 0.8266 | 0.8749 | 0.8538 |
Polar_Welch | 0.0176 | 29.7521 | 0.9289 | 0.9932 | 0.8627 | 0.8511 | 0.8266 | 0.8749 | 0.8538 |
Polar_Hann | 0.0177 | 29.6535 | 0.9285 | 0.9931 | 0.8511 | 0.8288 | 0.8214 | 0.8713 | 0.8432 |
Polar_Bartlett | 0.0177 | 29.6043 | 0.9287 | 0.9931 | 0.8513 | 0.8177 | 0.8231 | 0.8712 | 0.8408 |
Polar_Mitchell | 0.0179 | 29.2988 | 0.9290 | 0.9935 | 0.8295 | 0.7487 | 0.8283 | 0.8956 | 0.8255 |
Polar_Hamming | 0.0179 | 29.5229 | 0.9275 | 0.9928 | 0.8322 | 0.7993 | 0.8062 | 0.8509 | 0.8222 |
Polar_Kaiser | 0.0180 | 29.4493 | 0.9272 | 0.9927 | 0.8248 | 0.7827 | 0.8022 | 0.8506 | 0.8151 |
Polar_Lanczos2Sharp | 0.0182 | 29.2163 | 0.9269 | 0.9929 | 0.8015 | 0.7301 | 0.7981 | 0.8571 | 0.7967 |
Lagrange | 0.0182 | 29.1906 | 0.9267 | 0.9926 | 0.7976 | 0.7243 | 0.7959 | 0.8413 | 0.7898 |
Polar_Blackman | 0.0184 | 29.1789 | 0.9251 | 0.9923 | 0.7852 | 0.7216 | 0.7728 | 0.8260 | 0.7764 |
Polar_Robidoux | 0.0184 | 29.0213 | 0.9258 | 0.9927 | 0.7766 | 0.6860 | 0.7818 | 0.8489 | 0.7733 |
Polar_Bohman | 0.0184 | 29.1470 | 0.9249 | 0.9923 | 0.7808 | 0.7144 | 0.7698 | 0.8252 | 0.7725 |
Polar_Lanczos2 | 0.0187 | 28.9992 | 0.9232 | 0.9919 | 0.7511 | 0.6810 | 0.7450 | 0.8007 | 0.7445 |
Polar_Parzen | 0.0188 | 28.8898 | 0.9227 | 0.9918 | 0.7397 | 0.6563 | 0.7383 | 0.7960 | 0.7326 |
RobidouxSharp | 0.0193 | 28.5601 | 0.9204 | 0.9912 | 0.6857 | 0.5818 | 0.7054 | 0.7614 | 0.6836 |
Polar_Hermite | 0.0202 | 27.7627 | 0.9180 | 0.9933 | 0.5937 | 0.4017 | 0.6713 | 0.8806 | 0.6369 |
Mitchell | 0.0199 | 28.3003 | 0.9166 | 0.9903 | 0.6288 | 0.5232 | 0.6517 | 0.7060 | 0.6274 |
Hermite | 0.0202 | 27.8860 | 0.9170 | 0.9923 | 0.5953 | 0.4296 | 0.6571 | 0.8217 | 0.6259 |
Robidoux | 0.0202 | 28.1364 | 0.9141 | 0.9896 | 0.5916 | 0.4861 | 0.6161 | 0.6689 | 0.5907 |
Polar_Triangle | 0.0207 | 27.8375 | 0.9122 | 0.9900 | 0.5479 | 0.4186 | 0.5888 | 0.6903 | 0.5614 |
Box | 0.0220 | 26.5260 | 0.9105 | 0.9950 | 0.4101 | 0.1224 | 0.5649 | 0.9801 | 0.5194 |
Point | 0.0220 | 26.5260 | 0.9105 | 0.9950 | 0.4101 | 0.1224 | 0.5649 | 0.9801 | 0.5194 |
Polar_Box | 0.0220 | 26.5260 | 0.9105 | 0.9950 | 0.4101 | 0.1224 | 0.5649 | 0.9801 | 0.5194 |
Polar_Point | 0.0213 | 27.6369 | 0.9069 | 0.9880 | 0.4762 | 0.3733 | 0.5140 | 0.5715 | 0.4838 |
Polar_Sinc | 0.0213 | 27.6369 | 0.9069 | 0.9880 | 0.4762 | 0.3733 | 0.5140 | 0.5715 | 0.4838 |
Polar_SincFast | 0.0213 | 27.6369 | 0.9069 | 0.9880 | 0.4762 | 0.3733 | 0.5140 | 0.5715 | 0.4838 |
Triangle | 0.0213 | 27.6369 | 0.9069 | 0.9880 | 0.4762 | 0.3733 | 0.5140 | 0.5715 | 0.4838 |
Jinc | 0.0214 | 27.8115 | 0.9049 | 0.9868 | 0.4681 | 0.4127 | 0.4854 | 0.5026 | 0.4672 |
Polar_Quadratic | 0.0228 | 27.0915 | 0.8960 | 0.9850 | 0.3285 | 0.2501 | 0.3577 | 0.3992 | 0.3339 |
Polar_Gaussian | 0.0231 | 26.9436 | 0.8941 | 0.9849 | 0.2939 | 0.2167 | 0.3308 | 0.3926 | 0.3085 |
Gaussian | 0.0231 | 26.9424 | 0.8940 | 0.9849 | 0.2936 | 0.2164 | 0.3305 | 0.3923 | 0.3082 |
Quadratic | 0.0233 | 26.8872 | 0.8920 | 0.9840 | 0.2718 | 0.2040 | 0.3009 | 0.3367 | 0.2783 |
Polar_Jinc | 0.0247 | 26.9115 | 0.8953 | 0.9820 | 0.1285 | 0.2095 | 0.3489 | 0.2231 | 0.2275 |
Polar_Cubic | 0.0255 | 26.1276 | 0.8741 | 0.9792 | 0.0446 | 0.0324 | 0.0472 | 0.0553 | 0.0449 |
Polar_Spline | 0.0255 | 26.1276 | 0.8741 | 0.9792 | 0.0446 | 0.0324 | 0.0472 | 0.0553 | 0.0449 |
Cubic | 0.0260 | 25.9843 | 0.8708 | 0.9782 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 |
Spline | 0.0260 | 25.9843 | 0.8708 | 0.9782 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 |
We started this little adventure to provide a bit more robustness to the numbers compared to my previous comparison done using mpv, and there are a few surprises in the results. The first one is that
orthogonal LanczosSharp ended up on top of the table, an option that shouldn't even be in the comparison. If you want to give it a try on mpv, you can use
The second surprise is that Polar_Catrom is at the second place with the best scores for both SSIM and MS-SSIM. Polar_Catrom is an extremely sharp filter, albeit with too much aliasing and ringing.
Since it only rings once (when it overshoots), it's trivial to remove all ringing if we just clip the output of each pixel to the limits set by the surrounding input pixels.
I have actually written a shader for it, but you can also just use libplacebo's native AR now.
If you want to give the filter a try on mpv, you can use
The third surprise is that there are some Lanczos variants on top of the baseline Lanczos: Hamming, Cosine and Welch. For the last two all you gotta do is set
Finally, it's also a little bit funny to see Lanczos2 beating Catmull_Rom in the "orthogonal filters with radius = 2" category. And it's even funnier that Lanczos2Sharp beat them both, another filter that shouldn't even be here.
To use Lanczos2 on mpv you can simply do
You might have also noticed that Polar_LanczosRadius is on top of Polar_LanczosSharp. Polar_LanczosRadius is sharpened to have its third zero crossing exactly at 3,
which corresponds to a blur factor of 0.9264075766146068 (3.0/3.2383154841662362). To try it on mpv you can use
Overall it looks like the filters were more or less ranked from sharpest to softest. This makes perfect sense as the main problem with image upsampling is unintended blurriness. PSNR is calculated using the MSE so it's overly sensitive to outliers, which naturally end up being the sharp transitions. SSIM was also literally designed to give good scores to distorted images that have similarish structures, which also favours sharpness. MAE is a little more neutral than PSNR which is why we generally use it as our target when training distortion-based CNNs, so it should punish filters that are too sharp in detriment to ringing, aliasing and blocking. That's the main reason why most of those CNNs end up generating images that look like oil painting. MS-SSIM was added to neutralise stupidly minor differences as we wouldn't be able to see them anyway from normal viewing distances.
As usual, the filters are ranked based on full-reference distortion metrics that may not always correlate with the human perception of quality, and your personal preference is entirely subjective.
Please also keep in mind that only the named filters were "benchmarked", but you can make "custom" filters that score even higher using the expert controls.
The problem with downsampling evaluation is that we do not have a "ground truth" image, making it impossible for us to use the standard full-reference quality metrics. However, in the previous section I already made the concession that, at a 0.5x scaling ratio, the box filter is as good as it gets without producing any artifacts.
If we make the leap of faith that resampling filters will more or less keep their character regardless of scaling factor, we can use the output of box as the reference and compare other filters against it. So, in short, we want to find a filter that, at any scaling factor, will behave similarly to box at 0.5x. I'm calling this a leap of faith because this hypothesis most likely falls apart with extreme scaling factors, but as long as you keep it close to 0.5x it probably makes sense.
With that said, now we can proceed to the actual methodology. The reference is created with:
magick mogrify -colorspace RGB -filter box -resize 50% -colorspace sRGB -path box_ref inputs/*.png
The dataset is then brought downscaled with the following command for orthogonal resampling:
magick mogrify -colorspace RGB -filter {resampling_filter} -resize 50% -colorspace sRGB -path low_res box_ref/*.png
And the following command for polar resampling:
magick mogrify -colorspace RGB -filter {resampling_filter} -distort Resize 50% -colorspace sRGB -path low_res box_ref/*.png
The
Filter | MAE | PSNR | SSIM | MS-SSIM | MAE (N) | PSNR (N) | SSIM (N) | MS-SSIM (N) | Mean | ||
Lanczos2Sharp | 0.0052 | 38.1382 | 0.9941 | 0.9993 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | ||
Polar_Hermite | 0.0058 | 37.9258 | 0.9937 | 0.9992 | 0.9632 | 0.9819 | 0.9936 | 0.9917 | 0.9826 | ||
Lanczos2 | 0.0062 | 37.1522 | 0.9924 | 0.9992 | 0.9443 | 0.9162 | 0.9718 | 0.9844 | 0.9542 | ||
Catrom | 0.0062 | 37.1418 | 0.9923 | 0.9992 | 0.9403 | 0.9153 | 0.9704 | 0.9859 | 0.9530 | ||
Polar_Mitchell | 0.0067 | 36.8261 | 0.9913 | 0.9992 | 0.9109 | 0.8885 | 0.9544 | 0.9830 | 0.9342 | ||
Polar_Robidoux | 0.0071 | 36.7730 | 0.9905 | 0.9992 | 0.8899 | 0.8839 | 0.9428 | 0.9815 | 0.9246 | ||
Polar_RobidouxSharp | 0.0068 | 35.8553 | 0.9911 | 0.9989 | 0.9084 | 0.8059 | 0.9514 | 0.9572 | 0.9057 | ||
Hermite | 0.0072 | 36.1529 | 0.9909 | 0.9989 | 0.8826 | 0.8312 | 0.9487 | 0.9525 | 0.9037 | ||
Polar_Lanczos2Sharp | 0.0075 | 36.0829 | 0.9895 | 0.9990 | 0.8669 | 0.8253 | 0.9267 | 0.9674 | 0.8966 | ||
Parzen | 0.0070 | 35.6504 | 0.9901 | 0.9989 | 0.8945 | 0.7885 | 0.9355 | 0.9541 | 0.8931 | ||
CubicSpline | 0.0075 | 35.5080 | 0.9895 | 0.9989 | 0.8693 | 0.7764 | 0.9271 | 0.9495 | 0.8806 | ||
RobidouxSharp | 0.0081 | 35.7346 | 0.9883 | 0.9989 | 0.8346 | 0.7957 | 0.9074 | 0.9515 | 0.8723 | ||
Lagrange | 0.0079 | 35.5718 | 0.9885 | 0.9989 | 0.8424 | 0.7818 | 0.9097 | 0.9545 | 0.8721 | ||
Bohman | 0.0075 | 35.1306 | 0.9889 | 0.9988 | 0.8693 | 0.7443 | 0.9169 | 0.9404 | 0.8677 | ||
Polar_Parzen | 0.0083 | 35.5274 | 0.9875 | 0.9989 | 0.8209 | 0.7781 | 0.8953 | 0.9557 | 0.8625 | ||
Blackman | 0.0076 | 34.9680 | 0.9885 | 0.9987 | 0.8603 | 0.7305 | 0.9104 | 0.9359 | 0.8593 | ||
Polar_Bohman | 0.0085 | 35.0970 | 0.9870 | 0.9988 | 0.8098 | 0.7415 | 0.8860 | 0.9451 | 0.8456 | ||
Polar_Lanczos2 | 0.0086 | 35.1477 | 0.9868 | 0.9988 | 0.8051 | 0.7458 | 0.8837 | 0.9454 | 0.8450 | ||
Polar_Blackman | 0.0085 | 35.0236 | 0.9867 | 0.9988 | 0.8059 | 0.7352 | 0.8822 | 0.9434 | 0.8417 | ||
Mitchell | 0.0089 | 34.8539 | 0.9862 | 0.9986 | 0.7878 | 0.7208 | 0.8735 | 0.9231 | 0.8263 | ||
Bartlett | 0.0083 | 34.4112 | 0.9863 | 0.9986 | 0.8201 | 0.6832 | 0.8749 | 0.9213 | 0.8249 | ||
LanczosSharp | 0.0082 | 34.0812 | 0.9871 | 0.9985 | 0.8260 | 0.6551 | 0.8888 | 0.9062 | 0.8190 | ||
Kaiser | 0.0083 | 34.2164 | 0.9865 | 0.9985 | 0.8174 | 0.6666 | 0.8785 | 0.9132 | 0.8189 | ||
Lanczos | 0.0085 | 33.9740 | 0.9864 | 0.9984 | 0.8108 | 0.6460 | 0.8771 | 0.9019 | 0.8090 | ||
LanczosRadius | 0.0085 | 33.9740 | 0.9864 | 0.9984 | 0.8108 | 0.6460 | 0.8771 | 0.9019 | 0.8090 | ||
Polar_Kaiser | 0.0091 | 34.3405 | 0.9850 | 0.9986 | 0.7763 | 0.6772 | 0.8552 | 0.9209 | 0.8074 | ||
Polar_LanczosRadius | 0.0086 | 33.9796 | 0.9860 | 0.9985 | 0.8017 | 0.6465 | 0.8706 | 0.9045 | 0.8058 | ||
Hann | 0.0087 | 33.9521 | 0.9855 | 0.9985 | 0.7991 | 0.6442 | 0.8628 | 0.9039 | 0.8025 | ||
Polar_Triangle | 0.0092 | 34.2415 | 0.9855 | 0.9983 | 0.7667 | 0.6688 | 0.8620 | 0.8905 | 0.7970 | ||
Robidoux | 0.0094 | 34.2906 | 0.9846 | 0.9984 | 0.7560 | 0.6729 | 0.8488 | 0.9015 | 0.7948 | ||
Polar_Bartlett | 0.0094 | 34.0368 | 0.9834 | 0.9985 | 0.7544 | 0.6514 | 0.8287 | 0.9081 | 0.7857 | ||
Polar_Hamming | 0.0095 | 34.0253 | 0.9833 | 0.9985 | 0.7505 | 0.6504 | 0.8279 | 0.9084 | 0.7843 | ||
Cosine | 0.0090 | 33.5286 | 0.9848 | 0.9983 | 0.7793 | 0.6082 | 0.8513 | 0.8854 | 0.7810 | ||
Hamming | 0.0091 | 33.4712 | 0.9845 | 0.9983 | 0.7719 | 0.6033 | 0.8461 | 0.8848 | 0.7765 | ||
Polar_Hann | 0.0096 | 33.5662 | 0.9837 | 0.9983 | 0.7458 | 0.6114 | 0.8343 | 0.8869 | 0.7696 | ||
Welch | 0.0093 | 33.2882 | 0.9838 | 0.9982 | 0.7603 | 0.5877 | 0.8351 | 0.8757 | 0.7647 | ||
Polar_LanczosSharp | 0.0097 | 33.4770 | 0.9829 | 0.9983 | 0.7408 | 0.6038 | 0.8220 | 0.8849 | 0.7629 | ||
Polar_Cosine | 0.0101 | 33.2418 | 0.9816 | 0.9982 | 0.7157 | 0.5838 | 0.8010 | 0.8751 | 0.7439 | ||
Polar_Lanczos | 0.0101 | 33.2418 | 0.9816 | 0.9982 | 0.7157 | 0.5838 | 0.8010 | 0.8751 | 0.7439 | ||
Polar_Welch | 0.0101 | 33.2418 | 0.9816 | 0.9982 | 0.7157 | 0.5838 | 0.8010 | 0.8751 | 0.7439 | ||
Triangle | 0.0109 | 32.7732 | 0.9802 | 0.9977 | 0.6654 | 0.5440 | 0.7781 | 0.8165 | 0.7010 | ||
Jinc | 0.0130 | 31.9518 | 0.9720 | 0.9972 | 0.5428 | 0.4741 | 0.6486 | 0.7583 | 0.6060 | ||
Polar_Quadratic | 0.0134 | 31.1154 | 0.9713 | 0.9966 | 0.5229 | 0.4030 | 0.6371 | 0.6968 | 0.5649 | ||
Polar_Gaussian | 0.0139 | 30.7778 | 0.9699 | 0.9963 | 0.4927 | 0.3743 | 0.6139 | 0.6610 | 0.5355 | ||
Gaussian | 0.0139 | 30.7743 | 0.9698 | 0.9963 | 0.4923 | 0.3740 | 0.6136 | 0.6606 | 0.5352 | ||
Quadratic | 0.0143 | 30.5742 | 0.9680 | 0.9961 | 0.4710 | 0.3570 | 0.5837 | 0.6441 | 0.5139 | ||
Polar_Catrom | 0.0152 | 28.6343 | 0.9718 | 0.9945 | 0.4166 | 0.1921 | 0.6454 | 0.4575 | 0.4279 | ||
Polar_Lagrange | 0.0155 | 28.6092 | 0.9706 | 0.9942 | 0.3998 | 0.1900 | 0.6252 | 0.4303 | 0.4113 | ||
Polar_Cubic | 0.0176 | 28.8798 | 0.9528 | 0.9942 | 0.2794 | 0.2130 | 0.3432 | 0.4332 | 0.3172 | ||
Polar_Spline | 0.0176 | 28.8798 | 0.9528 | 0.9942 | 0.2794 | 0.2130 | 0.3432 | 0.4332 | 0.3172 | ||
Cubic | 0.0182 | 28.5882 | 0.9499 | 0.9938 | 0.2426 | 0.1882 | 0.2965 | 0.3843 | 0.2779 | ||
Spline | 0.0182 | 28.5882 | 0.9499 | 0.9938 | 0.2426 | 0.1882 | 0.2965 | 0.3843 | 0.2779 | ||
Sinc | 0.0191 | 27.6882 | 0.9545 | 0.9922 | 0.1896 | 0.1117 | 0.3702 | 0.2120 | 0.2209 | ||
SincFast | 0.0191 | 27.6882 | 0.9545 | 0.9922 | 0.1896 | 0.1117 | 0.3702 | 0.2120 | 0.2209 | ||
Polar_Jinc | 0.0196 | 28.1480 | 0.9518 | 0.9910 | 0.1603 | 0.1508 | 0.3266 | 0.0684 | 0.1765 | ||
Polar_CubicSpline | 0.0205 | 26.4606 | 0.9557 | 0.9908 | 0.1098 | 0.0074 | 0.3895 | 0.0525 | 0.1398 | ||
Point | 0.0223 | 26.3740 | 0.9313 | 0.9903 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 |
The results were relatively predictable, but there are a few things worth mentioning. The first one is that this methodology seems to really like filters with only 1 or 2 lobes.
Lanczos2Sharp (
It's a bit funny to see Polar_Hermite (
It's also interesting to see that Polar_Robidoux wasn't the best polar BC-Spline, considering the filter was specifically designed to be good at
Both Lanczos (orthogonal sinc-sinc) and Polar_Lanczos (polar jinc-jinc) were pretty mediocre at best. That's a good thing because it shows the methodology can actually punish filters that are too sharp. Polar_Catrom, the sharpest filter we have in the comparison, actually scored as one of the worst filters.
I think this test solidifies what was already common knowledge. BC-Splines are very good at downsampling, and pretty much all of them are near the top.
Nicolas Robidoux has a personal page with some recommendations, if you want a more qualitative approach.