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 lineargray +sigmoidal-contrast 7.5 and -sigmoidal-contrast 7.5 -colorspace gray.

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.63E-0230.63370.94240.99631.00000.99870.99121.00000.9975
Hamming1.64E-0230.63960.94150.99610.99141.00000.97830.98760.9893
Cosine1.64E-0230.60980.94100.99600.98390.99320.97120.98590.9836
Welch1.65E-0230.62640.94070.99600.98010.99700.96560.98480.9819
Lanczos1.64E-0230.55070.94130.99600.98370.97970.97460.98530.9808
LanczosRadius1.64E-0230.55070.94130.99600.98370.97970.97460.98530.9808
Hann1.65E-0230.54600.94100.99590.97900.97860.97030.97930.9768
Kaiser1.65E-0230.51610.94120.99590.98040.97180.97290.98030.9764
Bartlett1.65E-0230.48250.94090.99600.97570.96410.96930.98320.9731
Blackman1.66E-0230.38110.94070.99580.96800.94100.96570.97510.9625
Bohman1.66E-0230.35360.94060.99580.96580.93470.96440.97520.9600
Polar_Catrom1.67E-0230.24790.94300.99570.95320.91061.00000.96880.9581
Polar_Lagrange1.70E-0230.34060.94180.99520.92960.93170.98240.94760.9478
Parzen1.67E-0230.24630.93990.99570.95320.91020.95530.97050.9473
Polar_LanczosRadius1.68E-0230.35890.93870.99550.94770.93590.93780.95930.9452
Lanczos2Sharp1.72E-0229.90650.93800.99580.90730.83260.92670.97690.9109
Polar_RobidouxSharp1.72E-0229.94220.93710.99560.90700.84080.91420.96510.9068
Lanczos21.74E-0229.80840.93590.99500.88080.81020.89640.93850.8815
Polar_LanczosSharp1.74E-0230.03700.93390.99420.88080.86240.86830.90100.8781
CubicSpline1.74E-0229.83690.93530.99470.88160.81670.88880.92390.8777
Catrom1.75E-0229.73560.93560.99510.87730.79360.89280.93980.8759
Sinc1.83E-0230.35420.93060.99410.79530.93480.81970.89250.8606
SincFast1.83E-0230.35420.93060.99410.79530.93480.81970.89250.8606
Polar_Cosine1.77E-0229.90920.93200.99380.85400.83330.84000.87760.8512
Polar_Lanczos1.77E-0229.90920.93200.99380.85400.83330.84000.87760.8512
Polar_Welch1.77E-0229.90920.93200.99380.85400.83330.84000.87760.8512
Polar_Bartlett1.78E-0229.79650.93190.99380.84620.80750.83920.87840.8428
Polar_CubicSpline1.81E-0229.68940.93860.99290.80940.78310.93590.83840.8417
Polar_Hann1.78E-0229.80950.93170.99370.84510.81050.83650.87410.8416
Polar_Mitchell1.79E-0229.54180.93260.99440.83590.74940.84930.90800.8356
Polar_Hamming1.79E-0229.71190.93080.99330.83050.78820.82250.85780.8248
Polar_Kaiser1.80E-0229.63550.93050.99330.82480.77080.81920.85740.8180
Polar_Lanczos2Sharp1.82E-0229.45310.93050.99360.80890.72910.81880.86950.8066
Lagrange1.82E-0229.42840.93040.99330.80740.72350.81700.85620.8010
Polar_Robidoux1.84E-0229.27760.92940.99350.78610.68900.80320.86480.7858
Polar_Blackman1.83E-0229.38860.92860.99290.79010.71440.79160.83670.7832
Polar_Bohman1.84E-0229.35910.92840.99290.78590.70770.78870.83630.7796
Polar_Lanczos21.86E-0229.22160.92670.99240.75910.67630.76420.81320.7532
Polar_Parzen1.87E-0229.12670.92630.99240.74910.65460.75830.81110.7433
RobidouxSharp1.92E-0228.84100.92420.99180.70120.58940.72790.78400.7006
Polar_Hermite1.99E-0228.12640.92120.99440.63420.42620.68370.90860.6632
Hermite2.00E-0228.23410.92060.99320.62280.45080.67550.85280.6505
Mitchell1.98E-0228.58390.92050.99060.64510.53070.67370.72980.6448
Robidoux2.01E-0228.42120.91800.98990.60830.49350.63760.69320.6082
Polar_Triangle2.05E-0228.15440.91600.99040.56440.43260.60930.71810.5811
Polar_Box2.12E-0226.88810.91140.99600.49710.14360.54240.98490.5420
Box2.12E-0226.88810.91140.99600.49710.14360.54240.98490.5420
Point2.12E-0226.88810.91140.99600.49710.14360.54240.98490.5420
Polar_Point2.13E-0227.93950.91090.98790.49070.38360.53470.59850.5019
Polar_Sinc2.13E-0227.93950.91090.98790.49070.38360.53470.59850.5019
Polar_SincFast2.13E-0227.93950.91090.98790.49070.38360.53470.59850.5019
Triangle2.13E-0227.93950.91090.98790.49070.38360.53470.59850.5019
Jinc2.14E-0228.08550.90830.98620.48160.41690.49740.51970.4789
Polar_Quadratic2.27E-0227.38190.89970.98410.34060.25630.37390.42140.3481
Polar_Gaussian2.31E-0227.24470.89790.98410.30200.22500.34740.41830.3232
Gaussian2.31E-0227.24350.89790.98410.30160.22470.34720.41790.3229
Quadratic2.33E-0227.17750.89570.98280.28180.20960.31540.35710.2910
Polar_Jinc2.50E-0227.36680.89710.98000.10500.25280.33610.22510.2298
Polar_Cubic2.56E-0226.40180.87730.97650.04650.03250.04960.05890.0469
Polar_Spline2.56E-0226.40180.87730.97650.04650.03250.04960.05890.0469
Cubic2.61E-0226.25930.87390.97530.00000.00000.00000.00000.0000
Spline2.61E-0226.25930.87390.97530.00000.00000.00000.00000.0000

Sigmoid Light Upsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
LanczosSharp1.52E-0231.34790.94600.99651.00000.99481.00001.00000.9987
Hamming1.52E-0231.37300.94540.99630.99671.00000.99130.98860.9942
Cosine1.53E-0231.33390.94480.99620.98820.99190.98200.98660.9872
Welch1.53E-0231.35360.94450.99620.98640.99600.97750.98550.9863
Lanczos1.53E-0231.26390.94490.99620.98480.97740.98410.98590.9831
LanczosRadius1.53E-0231.26390.94490.99620.98480.97740.98410.98590.9831
Hann1.53E-0231.26390.94480.99610.98280.97740.98150.98000.9804
Kaiser1.53E-0231.22700.94490.99610.98200.96980.98340.98110.9791
Bartlett1.54E-0231.17870.94470.99620.97700.95980.98000.98370.9751
Blackman1.55E-0231.06560.94430.99600.96500.93630.97400.97570.9628
Bohman1.55E-0231.03170.94410.99600.96170.92930.97210.97580.9597
Polar_LanczosRadius1.56E-0231.05920.94240.99570.94960.93500.94690.96200.9484
Parzen1.57E-0230.90410.94350.99590.94630.90290.96200.97110.9456
Polar_Catrom1.59E-0230.70840.94580.99550.92580.86240.99600.95520.9348
Polar_Lagrange1.60E-0230.82710.94480.99510.91140.88690.98130.93550.9288
Lanczos2Sharp1.62E-0230.47320.94120.99600.88950.81360.92840.97630.9020
Polar_RobidouxSharp1.62E-0230.53260.94040.99580.89190.82590.91690.96660.9003
Polar_LanczosSharp1.63E-0230.72770.93790.99450.88650.86640.87970.90640.8847
Lanczos21.64E-0230.39410.93940.99520.86800.79730.90120.93930.8764
CubicSpline1.64E-0230.45030.93900.99490.87200.80890.89530.92610.8756
Catrom1.65E-0230.30510.93900.99520.86110.77880.89580.94040.8690
Polar_Cosine1.65E-0230.59380.93610.99400.86080.83860.85250.88400.8590
Polar_Lanczos1.65E-0230.59380.93610.99400.86080.83860.85250.88400.8590
Polar_Welch1.65E-0230.59380.93610.99400.86080.83860.85250.88400.8590
Sinc1.71E-0230.85800.93500.99390.79710.89330.83610.87990.8516
SincFast1.71E-0230.85800.93500.99390.79710.89330.83610.87990.8516
Polar_Bartlett1.66E-0230.45170.93590.99400.84950.80920.84900.88350.8478
Polar_Hann1.66E-0230.47040.93580.99390.84680.81310.84780.88060.8471
Polar_Hamming1.68E-0230.36590.93480.99360.83350.79140.83360.86370.8305
Polar_Mitchell1.69E-0230.09760.93610.99460.82050.73580.85230.91010.8297
Polar_Kaiser1.69E-0230.27110.93450.99360.82350.77180.82950.86330.8220
Polar_CubicSpline1.74E-0229.97450.94120.99240.77280.71030.92790.80900.8050
Polar_Lanczos2Sharp1.71E-0230.02440.93420.99380.79810.72070.82470.87300.8041
Lagrange1.71E-0230.00570.93420.99350.79720.71680.82420.85940.7994
Polar_Blackman1.73E-0229.97970.93260.99310.78340.71140.79990.84190.7841
Polar_Bohman1.73E-0229.94310.93230.99310.77820.70380.79660.84140.7800
Polar_Robidoux1.74E-0229.80870.93300.99370.77060.67600.80650.86690.7800
Polar_Lanczos21.76E-0229.78910.93070.99260.75060.67190.77210.81830.7532
Polar_Parzen1.77E-0229.67140.93020.99250.73790.64760.76450.81510.7413
RobidouxSharp1.82E-0229.32760.92790.99190.68510.57630.73120.78480.6944
Mitchell1.87E-0229.04740.92430.99070.62950.51830.67680.73060.6388
Polar_Hermite1.93E-0228.35000.92310.99440.56880.37390.65910.90310.6262
Hermite1.93E-0228.52340.92300.99330.57580.40980.65850.84820.6231
Robidoux1.91E-0228.87030.92180.99000.59300.48160.64060.69400.6023
Polar_Triangle1.96E-0228.51610.91930.99050.53800.40830.60300.71730.5666
Triangle2.02E-0228.33040.91470.98790.47630.36980.53460.59770.4946
Polar_Point2.02E-0228.33040.91470.98790.47630.36980.53460.59770.4946
Polar_Sinc2.02E-0228.33040.91470.98790.47630.36980.53460.59770.4946
Polar_SincFast2.02E-0228.33040.91470.98790.47630.36980.53460.59770.4946
Jinc2.02E-0228.54960.91300.98630.48160.41520.50960.52420.4826
Polar_Box2.12E-0226.88810.91140.99600.37360.07110.48550.97700.4768
Box2.12E-0226.88810.91140.99600.37360.07110.48550.97700.4768
Point2.12E-0226.88810.91140.99600.37360.07110.48550.97700.4768
Polar_Quadratic2.16E-0227.74030.90390.98410.33050.24760.37410.42110.3433
Polar_Gaussian2.20E-0227.57550.90190.98400.29210.21350.34430.41640.3166
Gaussian2.20E-0227.57420.90190.98400.29170.21320.34400.41610.3162
Quadratic2.22E-0227.51980.89990.98280.27360.20190.31520.35650.2868
Polar_Jinc2.34E-0227.67580.90400.98000.14220.23420.37580.22750.2449
Polar_Cubic2.44E-0226.69820.88210.97640.04520.03170.04980.05930.0465
Polar_Spline2.44E-0226.69820.88210.97640.04520.03170.04980.05930.0465
Cubic2.48E-0226.54490.87870.97510.00000.00000.00000.00000.0000
Spline2.48E-0226.54490.87870.97510.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 brought 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 lineargray and -colorspace gray.

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.83E-0343.37910.99710.99981.00001.00001.00001.00001.0000
Lanczos24.68E-0341.66520.99590.99980.95220.89710.98070.99280.9557
Catrom4.76E-0341.52160.99580.99980.94780.88840.97930.99330.9522
Polar_RobidouxSharp4.95E-0341.27620.99560.99970.93720.87370.97660.98590.9434
Polar_Hermite4.92E-0340.63000.99580.99980.93880.83490.97910.99140.9360
Parzen5.11E-0340.99160.99540.99970.92790.85660.97220.98550.9356
Polar_Mitchell5.27E-0340.71770.99480.99970.91900.84020.96340.98960.9280
Bohman5.39E-0340.56480.99500.99970.91210.83100.96580.98180.9227
Blackman5.50E-0340.40830.99480.99960.90610.82160.96310.98050.9178
CubicSpline5.65E-0340.08950.99450.99960.89780.80240.95770.98090.9097
Bartlett5.89E-0340.00990.99410.99960.88380.79760.95210.97880.9031
Polar_Robidoux5.79E-0339.74330.99380.99970.89000.78160.94710.98510.9010
LanczosSharp5.89E-0339.74290.99440.99960.88420.78160.95670.97190.8986
Kaiser6.00E-0339.70900.99400.99960.87780.77960.95030.97400.8954
Polar_Lanczos2Sharp5.95E-0339.64860.99350.99970.88080.77590.94290.98200.8954
Lanczos6.11E-0339.46250.99390.99950.87160.76480.94920.96970.8888
LanczosRadius6.11E-0339.46250.99390.99950.87160.76480.94920.96970.8888
Hann6.23E-0339.42930.99360.99960.86470.76280.94380.97170.8857
Polar_LanczosRadius6.25E-0339.34820.99340.99950.86360.75790.94130.96810.8827
Hermite6.13E-0338.75080.99380.99960.87070.72200.94640.97350.8782
Lagrange6.31E-0339.02700.99300.99960.86040.73860.93390.97740.8776
Cosine6.50E-0338.99780.99330.99950.84970.73690.93840.96540.8726
Hamming6.54E-0338.98300.99320.99950.84740.73600.93650.96490.8712
Welch6.74E-0338.73850.99280.99950.83640.72130.93140.96300.8630
Polar_Bohman6.78E-0338.52740.99190.99960.83380.70860.91560.97200.8575
Polar_Blackman6.82E-0338.49970.99180.99960.83180.70690.91410.97140.8561
Polar_Parzen6.82E-0338.31590.99170.99960.83160.69590.91270.97270.8532
Polar_Lanczos26.93E-0338.27230.99150.99950.82530.69330.90950.97040.8496
Polar_Kaiser7.02E-0338.36640.99140.99950.82030.69890.90890.96580.8485
Polar_Bartlett7.12E-0338.44050.99130.99950.81480.70340.90730.96650.8480
RobidouxSharp6.87E-0337.91520.99170.99950.82910.67180.91320.96760.8454
Polar_LanczosSharp7.22E-0338.20440.99130.99940.80910.68920.90660.95750.8406
Polar_Hamming7.26E-0338.20590.99090.99950.80680.68930.90080.96390.8402
Polar_Hann7.33E-0337.95770.99100.99940.80290.67440.90210.95370.8333
Polar_Cosine7.62E-0337.75190.99040.99940.78650.66200.89140.95250.8231
Polar_Lanczos7.62E-0337.75190.99040.99940.78650.66200.89140.95250.8231
Polar_Welch7.62E-0337.75190.99040.99940.78650.66200.89140.95250.8231
Mitchell7.65E-0336.90610.98990.99940.78500.61120.88430.95250.8082
Polar_Triangle7.97E-0336.46020.98940.99920.76710.58440.87590.94020.7919
Robidoux8.17E-0336.30700.98870.99930.75580.57520.86360.94120.7840
Triangle9.53E-0334.92120.98530.99890.67870.49200.80910.90150.7203
Jinc1.07E-0234.42460.98180.99870.61040.46220.75320.88590.6779
Polar_Catrom1.15E-0233.58640.98630.99770.56770.41180.82550.78760.6482
Polar_Lagrange1.18E-0233.43840.98540.99750.55130.40300.81030.75970.6311
Polar_Quadratic1.17E-0233.15670.97810.99820.55510.38600.69310.83540.6174
Polar_Gaussian1.21E-0232.92200.97730.99810.53480.37190.67990.81990.6016
Gaussian1.21E-0232.91910.97730.99810.53450.37180.67970.81970.6014
Quadratic1.25E-0232.61610.97550.99790.51160.35360.65010.80670.5805
Sinc1.42E-0232.52480.97720.99650.41770.34810.67780.66610.5274
SincFast1.42E-0232.52480.97720.99650.41770.34810.67780.66610.5274
Polar_Jinc1.52E-0232.01750.97170.99510.35770.31760.58840.52100.4462
Polar_CubicSpline1.62E-0230.68920.97530.99550.30460.23780.64680.55970.4372
Polar_Cubic1.56E-0230.75260.96240.99660.33930.24160.43820.67590.4237
Polar_Spline1.56E-0230.75260.96240.99660.33930.24160.43820.67590.4237
Cubic1.61E-0230.45260.95990.99630.30740.22360.39760.64560.3935
Spline1.61E-0230.45260.95990.99630.30740.22360.39760.64560.3935
Point2.16E-0226.72920.93530.99000.00000.00000.00000.00000.0000

Linear Light Downsampling

FilterMAEPSNRSSIMMS-SSIMMAE (N)PSNR (N)SSIM (N)MS-SSIM (N)Mean
Lanczos2Sharp4.95E-0338.82160.99540.99951.00001.00001.00001.00001.0000
Polar_Hermite5.57E-0338.39540.99440.99930.96340.96610.98340.98730.9751
Lanczos25.87E-0337.77690.99390.99940.94570.91700.97590.99070.9573
Catrom5.93E-0337.78530.99390.99940.94210.91760.97500.99220.9567
Polar_Mitchell6.41E-0337.45010.99290.99940.91340.89100.95920.99050.9385
Polar_Robidoux6.74E-0337.34140.99210.99940.89360.88240.94610.99100.9283
Polar_RobidouxSharp6.51E-0336.40560.99280.99900.90750.80800.95750.96260.9089
Polar_Lanczos2Sharp7.14E-0336.65430.99130.99920.86990.82780.93440.98050.9032
Hermite6.89E-0336.61450.99180.99900.88490.82460.94120.95670.9018
Parzen6.76E-0336.11560.99200.99910.89270.78490.94570.96510.8971
CubicSpline7.15E-0336.03030.99150.99900.86960.77820.93730.96330.8871
Lagrange7.55E-0336.11520.99050.99920.84570.78490.92080.97290.8811
RobidouxSharp7.65E-0336.16550.98980.99910.83960.78890.90900.96660.8760
Bohman7.19E-0335.55780.99110.99890.86690.74060.93000.95490.8731
Polar_Parzen7.89E-0336.04570.98940.99920.82520.77940.90360.97390.8706
Blackman7.34E-0335.38330.99070.99890.85780.72680.92430.95150.8651
Polar_Lanczos28.17E-0335.66400.98890.99910.80880.74910.89520.96650.8549
Polar_Bohman8.11E-0335.59600.98910.99910.81230.74370.89800.96500.8547
Polar_Blackman8.18E-0335.50970.98890.99900.80810.73680.89480.96360.8509
Bartlett7.99E-0334.86450.98900.99880.81960.68550.89700.94540.8369
Mitchell8.43E-0335.25220.98760.99880.79340.71630.87460.94300.8318
Kaiser8.08E-0334.58620.98900.99870.81410.66340.89620.93440.8270
LanczosSharp7.98E-0334.40370.98930.99850.81980.64890.90160.92320.8234
Polar_Kaiser8.71E-0334.74270.98750.99880.77640.67580.87280.94430.8174
Lanczos8.22E-0334.29420.98870.99850.80540.64020.89200.92190.8149
LanczosRadius8.22E-0334.29420.98870.99850.80540.64020.89200.92190.8149
Polar_LanczosRadius8.34E-0334.33070.98840.99850.79850.64310.88650.92280.8127
Hann8.38E-0334.32470.98810.99860.79590.64260.88250.92950.8126
Polar_Triangle8.78E-0334.66910.98660.99840.77220.67000.85870.90970.8026
Robidoux8.96E-0334.68110.98610.99860.76190.67090.84970.92500.8019
Polar_Bartlett9.04E-0334.48840.98630.99880.75680.65560.85410.93970.8016
Polar_Hamming9.12E-0334.44430.98620.99870.75210.65210.85190.93890.7988
Cosine8.76E-0333.83810.98730.99840.77370.60400.86910.91150.7896
Hamming8.87E-0333.78150.98710.99840.76710.59950.86600.90970.7856
Polar_Hann9.30E-0333.85520.98610.99840.74160.60530.85060.91010.7769
Welch9.07E-0333.60270.98640.99830.75500.58520.85480.90610.7753
Polar_LanczosSharp9.35E-0333.79940.98570.99840.73840.60090.84360.91290.7739
Polar_Cosine9.77E-0333.54660.98450.99840.71330.58080.82440.90690.7564
Polar_Lanczos9.77E-0333.54660.98450.99840.71330.58080.82440.90690.7564
Polar_Welch9.77E-0333.54660.98450.99840.71330.58080.82440.90690.7564
Triangle1.04E-0233.19580.98160.99770.67320.55290.77740.85100.7136
Jinc1.23E-0232.51820.97580.99740.56180.49910.68530.82500.6428
Polar_Quadratic1.28E-0231.53340.97300.99650.53280.42080.63930.75430.5868
Polar_Gaussian1.33E-0231.21250.97150.99610.50290.39530.61640.72260.5593
Gaussian1.33E-0231.20910.97150.99610.50250.39500.61610.72230.5590
Quadratic1.37E-0230.99590.96960.99600.48170.37810.58620.71040.5391
Polar_Catrom1.55E-0228.57100.97170.99270.36970.18540.62000.44320.4046
Polar_Lagrange1.59E-0228.47660.97040.99230.34950.17790.59790.41190.3843
Polar_Cubic1.69E-0229.29000.95450.99380.29170.24250.34320.53580.3533
Polar_Spline1.69E-0229.29000.95450.99380.29170.24250.34320.53580.3533
Cubic1.75E-0229.00000.95150.99330.25520.21950.29520.49400.3160
Spline1.75E-0229.00000.95150.99330.25520.21950.29520.49400.3160
Polar_Jinc1.84E-0229.06150.95690.98960.19750.22430.38110.18810.2477
Sinc1.94E-0227.56610.95490.99000.14050.10550.34910.22450.2049
SincFast1.94E-0227.56610.95490.99000.14050.10550.34910.22450.2049
Polar_CubicSpline2.12E-0226.23860.95340.98730.03130.00000.32600.00000.0893
Point2.18E-0226.53230.93310.98900.00000.02330.00000.13840.0404

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.