Imagemagick Resampling

When doing it with mpv isn't enough...

Introduction

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:

  1. The resampling tests were only done for 1 or 2 test images.
  2. I kept adding meme metrics since people requested them.
  3. I also kept adding meme shaders since people requested them.
  4. I created a bunch of different test cases but didn't really have the motivation to keep all of them up to date.
  5. I originally used Matlab to compute the metrics and Excel to plot the tables, which means updating the page was a major pain in the ass.
  6. I used catmull-rom to downscale the test images.

So, to address these issues:

  1. I chose to use the entire Manga109 dataset this time, which is probably the best widely-used dataset we have for line-art.
  2. I chose to stick to the standard distortion metrics.
  3. I chose to exclude all meme shaders. I'll stick to resampling filters this time.
  4. I chose to only test 2x upsampling and 0.5x downsampling.
  5. I chose to fully automate everything so updating the numbers should be trivial.
  6. I chose to use the box filter to downsample this time. Since I'm downsampling to 0.5x this should be a simple average of 4 pixels, which is the best case scenario and doesn't introduce any blurriness or ringing.

With that out of the way, we can proceed with the real introduction.

Resampling

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.

Ortho_polar_comparison

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).

Upsampling Methodology

As stated before the entire Manga109 dataset will be used in this comparison. This dataset has manga covers that look like this:

Manga109

The dataset is downsampled with:

magick mogrify -filter box -resize 50% -path low_res inputs/*.png

The dataset is then brought back up with the following command for orthogonal resampling:

magick mogrify -filter {resampling_filter} -resize 200% -path high_res low_res/*.png

For polar resampling, -resize 200% is replaced by -distort resize 200%.

Likewise, for the results in sigmoid light, the resampling arguments are wrapped by -colorspace rgb +sigmoidal-contrast 7.5 and -sigmoidal-contrast 7.5 -colorspace srgb.

The {resampling_filter} argument is replaced by all available resampling filters: ['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 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 after grayscale conversion and in double-precision floating-point for accuracy. Metrics are normalised between [0, 1] and then averaged together.

Please note that some of those filters are just aliases to other filters or aliases to Sinc/Jinc with different windows.

LanczosRadius is a Jinc-windowed Jinc squished to have its third zero crossing at 3 instead of 3.2383154841662362. Likewise, LanczosSharp and Lanczos2Sharp are also just squished Jinc-windowed Jincs.

Upsampling Results

Gamma Light Upsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
LanczosSharp1.66E-0230.49060.93610.99591.00001.00000.97211.00000.9930
Hamming1.67E-0230.49070.93510.99570.99081.00000.95770.98660.9838
Cosine1.68E-0230.46300.93460.99560.98350.99370.95110.98490.9783
Welch1.68E-0230.47800.93420.99560.97960.99710.94570.98380.9765
Lanczos1.68E-0230.40680.93480.99560.98350.98080.95440.98440.9758
LanczosRadius1.68E-0230.40680.93480.99560.98350.98080.95440.98440.9758
Hann1.69E-0230.39790.93440.99550.97760.97880.94930.97830.9710
Kaiser1.68E-0230.37030.93460.99550.97920.97250.95210.97940.9708
Bartlett1.69E-0230.34230.93450.99560.97520.96610.94970.98290.9685
Polar_Catrom1.70E-0230.15150.93820.99550.96740.92261.00000.97780.9669
Blackman1.70E-0230.24020.93420.99540.96700.94280.94540.97430.9574
Bohman1.70E-0230.21490.93410.99540.96500.93710.94450.97450.9553
Polar_Lagrange1.72E-0230.23420.93650.99500.94540.94150.97730.95500.9548
Parzen1.71E-0230.11030.93350.99530.95260.91320.93610.97000.9430
Polar_LanczosRadius1.72E-0230.22030.93210.99500.94890.93830.91730.95550.9400
Lanczos2Sharp1.76E-0229.78040.93180.99550.90820.83790.91360.97840.9095
Polar_RobidouxSharp1.75E-0229.82050.93070.99520.90970.84710.89880.96350.9048
Lanczos21.78E-0229.67400.92930.99460.88040.81360.88030.93830.8782
CubicSpline1.78E-0229.70580.92860.99420.88230.82090.87000.92150.8737
Catrom1.79E-0229.61030.92910.99460.87770.79910.87700.93940.8733
Polar_LanczosSharp1.78E-0229.89850.92670.99360.88240.86490.84510.89350.8715
Sinc1.85E-0230.20010.92470.99380.81360.93370.81780.90040.8664
SincFast1.85E-0230.20010.92470.99380.81360.93370.81780.90040.8664
Polar_CubicSpline1.83E-0229.61750.93420.99280.83520.80070.94550.85670.8595
Polar_Cosine1.81E-0229.77070.92460.99310.85580.83570.81620.86880.8441
Polar_Lanczos1.81E-0229.77070.92460.99310.85580.83570.81620.86880.8441
Polar_Welch1.81E-0229.77070.92460.99310.85580.83570.81620.86880.8441
Polar_Bartlett1.82E-0229.66260.92460.99310.84700.81100.81600.87080.8362
Polar_Hann1.82E-0229.67370.92440.99300.84740.81360.81360.86600.8351
Polar_Mitchell1.83E-0229.41690.92590.99390.83650.75500.83310.90530.8325
Polar_Hamming1.83E-0229.57310.92330.99270.83050.79060.79840.84930.8172
Polar_Kaiser1.84E-0229.50040.92310.99270.82520.77400.79640.84950.8113
Polar_Lanczos2Sharp1.86E-0229.32070.92340.99300.80850.73300.80040.86520.8018
Lagrange1.86E-0229.29530.92320.99270.80630.72720.79750.85240.7959
Polar_Robidoux1.88E-0229.15070.92250.99290.78530.69420.78700.86170.7820
Polar_Blackman1.87E-0229.25630.92120.99230.78980.71830.77050.82990.7771
Polar_Bohman1.88E-0229.22780.92110.99220.78570.71180.76810.82980.7739
Polar_Lanczos21.91E-0229.08690.91930.99170.75810.67970.74400.80670.7471
Polar_Parzen1.92E-0228.99630.91890.99170.74760.65900.73930.80550.7379
RobidouxSharp1.97E-0228.70980.91690.99120.69790.59360.71240.78190.6965
Polar_Hermite2.03E-0228.00230.91520.99400.63170.43220.68930.91230.6664
Hermite2.04E-0228.11220.91420.99280.62100.45730.67540.85540.6523
Mitchell2.02E-0228.45080.91300.99000.64110.53450.65870.72740.6404
Robidoux2.06E-0228.28690.91030.98920.60370.49710.62300.69070.6036
Polar_Triangle2.10E-0228.02630.90870.98980.56100.43770.60110.71620.5790
Box2.18E-0226.73770.90700.99580.48570.14360.57800.99380.5503
Point2.18E-0226.73770.90700.99580.48570.14360.57800.99380.5503
Polar_Box2.18E-0226.73770.90700.99580.48570.14360.57800.99380.5503
Triangle2.18E-0227.80280.90300.98720.48660.38670.52420.59750.4987
Polar_Point2.18E-0227.80280.90300.98720.48660.38670.52420.59750.4987
Polar_Sinc2.18E-0227.80280.90300.98720.48660.38670.52420.59750.4987
Polar_SincFast2.18E-0227.80280.90300.98720.48660.38670.52420.59750.4987
Jinc2.19E-0227.93250.89930.98530.47440.41620.47350.50920.4683
Polar_Quadratic2.33E-0227.23960.89130.98330.33650.25810.36490.41920.3447
Polar_Gaussian2.36E-0227.10530.88960.98330.30030.22750.34200.41740.3218
Gaussian2.36E-0227.10400.88960.98330.29990.22720.34170.41710.3215
Quadratic2.39E-0227.03390.88710.98200.27860.21120.30790.35560.2883
Polar_Jinc2.56E-0227.16550.88830.97930.10900.24120.32400.23420.2271
Polar_Cubic2.62E-0226.25220.86790.97550.04570.03280.04770.05770.0460
Polar_Spline2.62E-0226.25220.86790.97550.04570.03280.04770.05770.0460
Cubic2.66E-0226.10830.86430.97420.00000.00000.00000.00000.0000
Spline2.66E-0226.10830.86430.97420.00000.00000.00000.00000.0000

Sigmoid Light Upsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
LanczosSharp1.56E-0231.18100.93950.99611.00000.99720.98661.00000.9960
Hamming1.56E-0231.19420.93870.99580.99461.00000.97560.98780.9895
Cosine1.57E-0231.15850.93810.99580.98690.99260.96640.98550.9828
Welch1.57E-0231.17290.93770.99570.98440.99560.96140.98430.9814
Lanczos1.57E-0231.09740.93830.99580.98450.97980.96930.98510.9797
LanczosRadius1.57E-0231.09740.93830.99580.98450.97980.96930.98510.9797
Hann1.58E-0231.09000.93800.99560.98090.97820.96530.97890.9758
Kaiser1.58E-0231.05940.93820.99570.98080.97190.96810.98030.9753
Bartlett1.58E-0231.02040.93800.99570.97700.96370.96570.98320.9724
Blackman1.59E-0230.91130.93760.99550.96540.94090.96010.97500.9604
Bohman1.59E-0230.88150.93750.99550.96260.93470.95870.97520.9578
Polar_LanczosRadius1.60E-0230.90070.93560.99520.95070.93870.93160.95840.9449
Parzen1.61E-0230.76090.93690.99540.94800.90950.94980.97060.9445
Polar_Catrom1.62E-0230.55250.94050.99520.93240.86601.00000.95820.9391
Polar_Lagrange1.64E-0230.64730.93900.99470.91760.88580.97900.93730.9299
Lanczos2Sharp1.66E-0230.34830.93500.99560.89470.82330.92330.97760.9047
Polar_RobidouxSharp1.66E-0230.41090.93400.99530.89780.83640.90910.96520.9021
Polar_LanczosSharp1.67E-0230.57380.93050.99380.88750.87040.86080.89960.8796
Lanczos21.68E-0230.25960.93280.99470.87110.80480.89260.93910.8769
CubicSpline1.68E-0230.32150.93210.99440.87570.81770.88350.92410.8752
Catrom1.69E-0230.18550.93240.99470.86580.78930.88770.93990.8707
Polar_Cosine1.69E-0230.44100.92850.99330.86170.84270.83250.87620.8533
Polar_Lanczos1.69E-0230.44100.92850.99330.86170.84270.83250.87620.8533
Polar_Welch1.69E-0230.44100.92850.99330.86170.84270.83250.87620.8533
Polar_Bartlett1.70E-0230.31100.92830.99330.85090.81550.82990.87620.8431
Polar_Hann1.70E-0230.32600.92830.99330.84910.81870.82990.87370.8428
Sinc1.76E-0230.55640.92770.99340.79360.86680.82110.87980.8403
SincFast1.76E-0230.55640.92770.99340.79360.86680.82110.87980.8403
Polar_Mitchell1.73E-0229.97810.92930.99400.82500.74600.84340.90750.8305
Polar_Hamming1.72E-0230.22010.92710.99290.83370.79650.81370.85600.8250
Polar_Kaiser1.73E-0230.13380.92700.99290.82510.77850.81190.85660.8180
Polar_CubicSpline1.77E-0229.79620.93580.99190.77960.70800.93510.81420.8092
Polar_Lanczos2Sharp1.75E-0229.89580.92710.99320.80090.72880.81290.86910.8029
Lagrange1.75E-0229.87900.92700.99290.79940.72530.81120.85590.7980
Polar_Blackman1.77E-0229.85190.92510.99240.78560.71960.78470.83590.7815
Polar_Robidoux1.78E-0229.68910.92590.99300.77420.68560.79700.86370.7801
Polar_Bohman1.77E-0229.81720.92490.99240.78080.71240.78200.83570.7777
Polar_Lanczos21.80E-0229.66010.92310.99190.75230.67960.75780.81270.7506
Polar_Parzen1.81E-0229.54910.92270.99180.74010.65640.75160.80990.7395
RobidouxSharp1.86E-0229.20630.92060.99120.68690.58470.72180.78200.6938
Mitchell1.92E-0228.92460.91670.99000.63040.52590.66730.72720.6377
Polar_Hermite1.98E-0228.23240.91710.99400.56800.38130.67300.90510.6318
Hermite1.97E-0228.40970.91650.99270.57730.41830.66550.84850.6274
Robidoux1.95E-0228.74650.91410.98910.59330.48870.63110.69030.6008
Polar_Triangle2.00E-0228.39750.91190.98970.53950.41580.60070.71390.5675
Polar_Point2.07E-0228.20360.90670.98700.47660.37530.52820.59460.4937
Polar_Sinc2.07E-0228.20360.90670.98700.47660.37530.52820.59460.4937
Polar_SincFast2.07E-0228.20360.90670.98700.47660.37530.52820.59460.4937
Triangle2.07E-0228.20360.90670.98700.47660.37530.52820.59460.4937
Box2.18E-0226.73770.90700.99580.36090.06910.53270.98690.4874
Point2.18E-0226.73770.90700.99580.36090.06910.53270.98690.4874
Polar_Box2.18E-0226.73770.90700.99580.36090.06910.53270.98690.4874
Jinc2.07E-0228.39480.90360.98510.47460.41520.48540.51230.4719
Polar_Quadratic2.21E-0227.60900.89520.98300.32970.25110.36760.41720.3414
Polar_Gaussian2.24E-0227.44670.89330.98290.29310.21720.34110.41300.3161
Gaussian2.25E-0227.44540.89330.98290.29280.21690.34080.41270.3158
Quadratic2.26E-0227.38740.89110.98150.27300.20480.30980.35320.2852
Polar_Jinc2.39E-0227.43710.89480.97880.14100.21520.36240.23020.2372
Polar_Cubic2.49E-0226.56150.87230.97490.04480.03230.04820.05790.0458
Polar_Spline2.49E-0226.56150.87230.97490.04480.03230.04820.05790.0458
Cubic2.53E-0226.40700.86890.97360.00000.00000.00000.00000.0000
Spline2.53E-0226.40700.86890.97360.00000.00000.00000.00000.0000

Upsampling Commentary

Unsurprisingly, the Lanczos family is at the top. Lanczos is widely considered to be one of the best resampling filters, so this makes sense.

There are a few things worth talking about. The first one is that orthogonal LanczosSharp ended up on top of the table, a filter preset that is not meant to be used as an orthogonal filter. If you want to give it a try on mpv, you can use --scale=lanczos --scale-blur=0.9812505644269356. Keep in mind, however, that this blur value is arbitrary nonsense in this context. It is meant to be used with Polar_Lanczos.

The second is that there are some Lanczos variants on top of the normal Lanczos: Hamming, Cosine and Welch. For the last two all you gotta do is set --scale=lanczos --scale-window=window, but for the first you also have to set --scale-radius=4. These are just Sinc filters with different windows, so naturally they all look very similar.

The third is that Polar_Catrom is the best filter with only 2 lobes. For the orthogonal contenders, Lanczos2 beats Catrom, and Lanczos2Sharp beats them both. To use Polar_Catrom on mpv, you can use --scale=ewa_robidoux --scale-param1=0.0 --scale-param2=0.5. For Lanczos2 you can simply do --scale=lanczos --scale-radius=2, and for Lanczos2Sharp add --scale-blur=0.9549963639785485.

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 --scale=ewa_lanczos --scale-blur=0.9264075766146068.

As expected, upsampling in sigmoid light helps and makes all metrics go up. This is admittedly a mathematical gimmick, but it's hard to argue against the numbers.

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 scored, but you can make "custom" filters that may score higher using the expert controls.

Downsampling Methodology

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 -filter box -resize 50% -path box_ref inputs/*.png

The dataset is then downsampled with the following command for orthogonal resampling:

magick mogrify -filter {resampling_filter} -resize 50% -path low_res box_ref/*.png

For polar downsampling, -resize 50% is replaced by -distort resize 50%.

Likewise, for the results in linear light, the resampling arguments are wrapped by -colorspace rgb and -colorspace srgb.

The {resampling_filter} argument is replaced with all available resampling filters. Everything else about the process stays the same, so we can proceed to the results.

Downsampling Results

Gamma Light Downsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
Lanczos2Sharp3.93E-0343.21920.99660.99981.00001.00001.00001.00001.0000
Lanczos24.81E-0341.49040.99530.99970.95180.89610.97870.99170.9546
Catrom4.89E-0341.34720.99520.99970.94750.88750.97750.99260.9513
Polar_RobidouxSharp5.06E-0341.12550.99510.99970.93840.87420.97570.98620.9436
Parzen5.20E-0340.88560.99470.99960.93030.85970.97090.98470.9364
Polar_Hermite5.08E-0340.42620.99540.99970.93690.83210.98050.99330.9357
Polar_Mitchell5.42E-0340.52010.99400.99970.91820.83780.96000.98830.9261
Bohman5.47E-0340.47820.99430.99960.91550.83530.96460.98110.9241
Blackman5.58E-0340.32600.99420.99960.90980.82610.96180.97960.9193
CubicSpline5.78E-0339.93770.99370.99960.89860.80280.95460.97970.9089
Bartlett5.94E-0339.95330.99350.99960.88980.80370.95120.97780.9056
LanczosSharp5.93E-0339.69890.99390.99950.89030.78840.95710.97190.9020
Polar_Robidoux5.97E-0339.53640.99290.99960.88810.77870.94260.98310.8981
Kaiser6.06E-0339.65110.99330.99950.88320.78560.94930.97300.8978
Polar_Lanczos2Sharp6.12E-0339.45150.99260.99960.87990.77360.93780.97960.8927
Lanczos6.17E-0339.40960.99330.99950.87730.77100.94870.96920.8916
LanczosRadius6.17E-0339.40960.99330.99950.87730.77100.94870.96920.8916
Hann6.29E-0339.37920.99290.99950.87090.76920.94280.97060.8884
Polar_LanczosRadius6.34E-0339.25420.99270.99950.86790.76170.93960.96750.8842
Hermite6.33E-0338.54750.99320.99950.86860.71920.94680.97500.8774
Cosine6.54E-0338.95810.99260.99940.85670.74390.93810.96480.8759
Lagrange6.49E-0338.84470.99200.99950.85950.73710.92840.97470.8749
Hamming6.58E-0338.94370.99250.99940.85480.74300.93600.96420.8745
Welch6.77E-0338.70510.99220.99940.84410.72870.93110.96220.8665
Polar_Bohman6.97E-0338.34270.99070.99950.83350.70690.90860.96890.8545
Polar_Blackman7.00E-0338.31710.99060.99950.83160.70540.90690.96820.8530
Polar_Parzen7.03E-0338.11830.99050.99950.83000.69340.90570.96940.8496
Polar_Bartlett7.25E-0338.30340.99020.99940.81810.70460.90100.96350.8468
Polar_Kaiser7.18E-0338.20860.99020.99940.82180.69890.90170.96270.8463
Polar_Lanczos27.13E-0338.07970.99030.99950.82440.69110.90200.96690.8461
RobidouxSharp7.11E-0337.70870.99070.99940.82550.66880.90800.96520.8419
Polar_LanczosSharp7.34E-0338.09220.99020.99930.81320.69190.90150.95540.8405
Polar_Hamming7.40E-0338.05950.98970.99940.80950.68990.89330.96040.8383
Polar_Hann7.47E-0337.82500.98990.99930.80570.67580.89590.95150.8322
Polar_Cosine7.75E-0337.63680.98920.99930.79080.66450.88510.94980.8225
Polar_Lanczos7.75E-0337.63680.98920.99930.79080.66450.88510.94980.8225
Polar_Welch7.75E-0337.63680.98920.99930.79080.66450.88510.94980.8225
Mitchell7.92E-0336.70080.98870.99930.78100.60820.87840.95000.8044
Polar_Triangle8.24E-0336.25060.98830.99920.76340.58120.87230.94010.7892
Robidoux8.46E-0336.10250.98740.99920.75160.57230.85740.93880.7800
Triangle9.85E-0334.71550.98390.99880.67510.48890.80410.90060.7172
Jinc1.11E-0234.22120.97960.99860.60880.45920.73830.88050.6717
Polar_Catrom1.14E-0233.56530.98650.99780.59270.41980.84380.80460.6652
Polar_Lagrange1.16E-0233.42410.98540.99750.57660.41130.82810.77850.6486
Polar_Quadratic1.21E-0232.95530.97620.99810.55240.38310.68660.83490.6143
Polar_Gaussian1.24E-0232.72220.97550.99800.53350.36910.67550.82120.5998
Gaussian1.24E-0232.71930.97550.99800.53330.36890.67530.82090.5996
Quadratic1.29E-0232.41570.97350.99780.50970.35070.64410.80700.5779
Sinc1.40E-0232.52080.97670.99660.44850.35700.69430.68540.5463
SincFast1.40E-0232.52080.97670.99660.44850.35700.69430.68540.5463
Polar_CubicSpline1.58E-0230.72090.97590.99570.34610.24880.68070.59410.4675
Polar_Jinc1.55E-0231.73270.96970.99510.36290.30960.58600.53810.4492
Polar_Cubic1.60E-0230.55580.95970.99650.33940.23890.43250.67870.4224
Polar_Spline1.60E-0230.55580.95970.99650.33940.23890.43250.67870.4224
Cubic1.65E-0230.25560.95710.99620.30820.22090.39290.64950.3929
Spline1.65E-0230.25560.95710.99620.30820.22090.39290.64950.3929
Point2.22E-0226.58070.93150.98960.00000.00000.00000.00000.0000

Linear Light Downsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
Lanczos2Sharp5.22E-0338.13830.99480.99941.00001.00001.00001.00001.0000
Polar_Hermite5.85E-0337.92580.99380.99930.96320.98190.98610.98740.9797
Lanczos26.17E-0337.15230.99300.99930.94430.91620.97370.98830.9556
Catrom6.24E-0337.14180.99300.99930.94030.91530.97250.99000.9545
Polar_Mitchell6.75E-0336.82620.99180.99930.91090.88850.95520.98750.9355
Polar_Robidoux7.10E-0336.77300.99090.99930.88990.88390.94120.98780.9257
Polar_RobidouxSharp6.79E-0335.85530.99190.99890.90840.80590.95660.95820.9073
Hermite7.23E-0336.15280.99100.99890.88260.83120.94240.95110.9018
Polar_Lanczos2Sharp7.50E-0336.08300.99010.99910.86690.82530.92840.97530.8990
Parzen7.03E-0335.65040.99120.99900.89450.78850.94500.96200.8975
CubicSpline7.46E-0335.50800.99040.99890.86930.77640.93380.95730.8842
Lagrange7.92E-0335.57190.98910.99900.84240.78180.91410.96610.8761
RobidouxSharp8.05E-0335.73460.98850.99900.83460.79570.90410.96060.8737
Bohman7.46E-0335.13050.99010.99890.86930.74430.92920.95100.8735
Polar_Parzen8.29E-0335.52740.98790.99900.82090.77810.89580.96710.8655
Blackman7.61E-0334.96800.98970.99880.86030.73050.92350.94730.8654
Polar_Bohman8.48E-0335.09700.98760.99890.80980.74150.89010.95720.8497
Polar_Lanczos28.56E-0335.14780.98730.99890.80510.74580.88660.95820.8489
Polar_Blackman8.54E-0335.02370.98730.99890.80590.73520.88690.95580.8460
Bartlett8.30E-0334.41120.98770.99870.82010.68320.89270.93890.8337
Mitchell8.85E-0334.85390.98620.99870.78780.72080.86930.93360.8279
Kaiser8.35E-0334.21640.98790.99860.81740.66660.89510.92870.8270
LanczosSharp8.20E-0334.08120.98850.99850.82600.65510.90400.91840.8259
Lanczos8.46E-0333.97400.98780.99850.81080.64600.89310.91620.8165
LanczosRadius8.46E-0333.97400.98780.99850.81080.64600.89310.91620.8165
Polar_Kaiser9.05E-0334.34050.98600.99870.77630.67720.86560.93580.8137
Polar_LanczosRadius8.62E-0333.97960.98730.99850.80170.64650.88580.91680.8127
Hann8.66E-0333.95200.98690.99860.79910.64420.88060.92270.8116
Polar_Triangle9.21E-0334.24150.98530.99830.76670.66880.85560.89540.7966
Robidoux9.40E-0334.29060.98450.99840.75600.67290.84410.91290.7965
Polar_Bartlett9.42E-0334.03680.98450.99860.75440.65140.84350.92880.7945
Polar_Hamming9.49E-0334.02530.98440.99860.75050.65040.84180.92830.7927
Cosine9.00E-0333.52860.98620.99840.77930.60820.86940.90430.7903
Hamming9.13E-0333.47120.98590.99830.77190.60330.86520.90190.7856
Polar_Hann9.57E-0333.56630.98470.99830.74580.61140.84720.90090.7763
Welch9.33E-0333.28820.98520.99830.76030.58770.85410.89770.7750
Polar_LanczosSharp9.66E-0333.47700.98420.99830.74080.60380.83850.90360.7717
Polar_Cosine1.01E-0233.24180.98280.99830.71570.58380.81800.89620.7534
Polar_Lanczos1.01E-0233.24180.98280.99830.71570.58380.81800.89620.7534
Polar_Welch1.01E-0233.24180.98280.99830.71570.58380.81800.89620.7534
Triangle1.09E-0232.77320.97980.99750.66540.54400.77230.82650.7021
Jinc1.30E-0231.95180.97220.99700.54280.47410.65560.78040.6132
Polar_Quadratic1.34E-0231.11540.97060.99620.52290.40300.63160.71410.5679
Polar_Gaussian1.39E-0230.77780.96920.99580.49270.37430.60990.67760.5386
Gaussian1.39E-0230.77430.96920.99580.49230.37400.60960.67710.5383
Quadratic1.43E-0230.57420.96710.99570.47100.35700.57840.66330.5174
Polar_Catrom1.52E-0228.63430.97290.99310.41660.19210.66700.43260.4271
Polar_Lagrange1.55E-0228.60930.97170.99280.39980.19000.64760.40500.4106
Polar_Cubic1.76E-0228.87980.95110.99350.27940.21300.33420.46270.3223
Polar_Spline1.76E-0228.87980.95110.99350.27940.21300.33420.46270.3223
Cubic1.82E-0228.58820.94800.99290.24260.18820.28660.41470.2830
Spline1.82E-0228.58820.94800.99290.24260.18820.28660.41470.2830
Sinc1.91E-0227.68820.95420.99040.18970.11170.38050.18280.2162
SincFast1.91E-0227.68820.95420.99040.18970.11170.38050.18280.2162
Polar_Jinc1.96E-0228.14790.95090.98830.16020.15080.33120.00000.1606
Polar_CubicSpline2.05E-0226.46060.95640.98840.10980.00740.41450.00090.1331
Point2.23E-0226.37400.92920.98860.00000.00000.00000.01910.0048

Downsampling Commentary

The results were relatively predictable.

Catrom is known to be good at downsampling, and since Lanczos2 is just a slightly sharper Catrom, it ends up as the best orthogonal filter that isn't a meme.

For polar filters, Polar_RobidouxSharp is our top scorer in gamma light but Polar_Hermite takes the lead in linear light. I'd personally favour Polar_Hermite between the two, as it's faster and doesn't ring.

Both Lanczos (orthogonal Sinc-Sinc) and Polar_Lanczos (polar Jinc-Jinc) ended up with mediocre scores. That's a good thing because it shows the methodology can actually punish filters that are too ringy.

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.

Outro

Nicolas Robidoux has a personal page with some recommendations, if you want a more qualitative approach.