Notes on Photoshop’s image resize algorithms

This is an analysis of Adobe Photoshop CS5.1’s image resizing algorithms, done with the help of my ResampleScope utility.

Anything on this page could be wrong. It’s easy to make mistakes in this kind of analysis.

Methodology

I used ResampleScope’s standard test images. For upscaling, I resized a 15×15 image to 555×15. For downscaling, I resized a 555×275 image to several different widths. All processing was done in Photoshop’s RGB mode.

I did a lot of other things too. This document only contains the final results.

Summary

Photoshop offers five algorithms, to which it gives the following names:

  1. Nearest Neighbor
  2. Bilinear
  3. Bicubic
  4. Bicubic Smoother
  5. Bicubic Sharper

This is what I’ve figured out about them so far. Some of the parameters may not be exactly right, but they’re pretty close.

Let SRCN = the width or height of the source image, in pixels.
Let DSTN = the width or height of the target image, in pixels.
Let SF = DSTN/SRCN. SF is the scale factor.

Nearest neighbor
If SF = (any?) Nearest neighbor, with image features shifted left (or up) by approximately 1/36th of a source pixel.
Bilinear
If SF > 1 Linear interpolation; i.e. a triangle filter.
If 0.5 ≤ SF < 1 Simple linear interpolation; i.e. a triangle filter with blur=SF.
If ? ≤ SF < 0.5 Box filter, with image features shifted right (or down) by 1/2 of a source pixel.
Bicubic
If SF ≥ 0.25 Cubic with B=0, C=0.75.
If ? ≤ SF < 0.25 Step 1: Use box filter (as defined above in Bilinear) to scale to 4×DSTN.
Step 2: Cubic with B=0, C=0.75.
Bicubic Smoother
If SF > 1 Cubic with B=0, C=0.625, blur=1.15.
If 0.25 ≤ SF < 1 Cubic with B=0, C=1.125−0.5×SF, blur=1.15.
If ? ≤ SF < 0.25 Step 1: Use box filter (as defined above in Bilinear) to scale to 4×DSTN.
Step 2: Cubic with B=0, C=1, blur=1.15.
Bicubic Sharper
If SF > 1 Cubic with B=0, C=1, blur=1.05.
If 0.25 ≤ SF < 1 Cubic with B=0, C=2.6−1.6×SF, blur=1.05.
If ? ≤ SF < 0.25 Step 1: Use box filter (as defined above in Bilinear) to scale to 4×DSTN.
Step 2: Cubic with B=0, C=2.2, blur=1.05.

Note: Large downscales are harder to analyze, and I haven’t looked much at scale factors below about 0.2. It seems likely that the formulas I’ve given for the Bicubic algorithms don’t account for sufficiently small scale factors.

Note: Photoshop resizes the X dimension, then the Y dimension. If there are multiple steps, it does all the X steps, then all the Y steps (X1 X2 Y1 Y2, not X1 Y1 X2 Y2). The order makes a difference because of clamping (see next note).

Note: Photoshop does what I call “intermediate clamping”. It does not appear to support intermediate pixel values that are outside the displayable range (usually that means 0 to 255). The cubic filters often produce such values, so if you’re trying to emulate what Photoshop does, make sure to clamp the pixels to the displayable range after each application of a cubic filter. Storing the temporary image in a PNG file is one way to do that.

Note: If a dimension of the image is not being changed at all (SF=1), Photoshop does not apply the algorithm to that dimension. It simply leaves the pixels the same. This makes a difference with the Smoother and Sharper algorithms.

Note: Box filters are very sensitive to slight implementation differences, which adds to the difficulty of emulating Photoshop’s algorithms.

Example

This web site looks at how Photoshop resizes a pathological “rings” image. The Bicubic Sharper filter produces the following result:

How closely can we reproduce that using other software?

Imitating with ImageMagick

With ImageMagick, I don’t know how to make it apply the cubic filter to just one dimension. So, this will be less than perfect.

The source image is 1000×1000 pixels, and the target image is 200×200 pixels. SF = 200/1000 = 0.2, which is less than 0.25, so we need to use a box filter, then a cubic filter. 200 times 4 is 800, so we first resize it to 800 pixels, then to 200.

This might not be the best way to do the box filter, but it’s the best I could figure out.

convert Rings1.gif \
 -filter box -define filter:blur=0.707 \
 +distort SRT "0,0 0.8,1.0 0 0.3999,0.0" -crop 800x1000+0+0 \
 +distort SRT "0,0 1.0,0.8 0 0.0,0.3999" -crop 800x800+0+0 \
 -transpose \
 -filter cubic -define filter:b=0 -define filter:c=2.2 -define filter:blur=1.05 \
 -resize 200x200 \
 -transpose \
 im-bicubicsharp.png

The result:

Photoshop ImageMagick Differences

Note that whether ImageMagick was compiled with HDRI support may have an effect on the results.

Imitating with ImageWorsener

ImageWorsener is an image scaling utility I wrote, in part to help see how other applications scale images. (It’s not really noteworthy that it does well on this test. Since it’s my program, I can cheat, and make sure it does well.)

It can only scale each dimension once per invocation, so I have to use temporary files, and run it multiple times, to emulate what Photoshop does.

ImageWorsener’s box filter may do rounding differently than Photoshop’s, so to be safe, I fudged it a bit, and translated the image by 0.3999 instead of 0.4.

# x, box
imagew Rings1.gif temp1.png -nogamma -depth 16 -w 800 -h 1000 \
  -filterx box -filtery nearest -translate 0.3999,0

# x, cubic
imagew temp1.png temp2.png -nogamma -depth 16 -w 200 -h 1000 \
  -filterx cubic0,2.2 -blurx 1.05 -filtery nearest

# y, box
imagew temp2.png temp3.png -nogamma -depth 16 -w 200 -h 800 \
  -filterx nearest -filtery box -translate 0,0.3999

# y, cubic
imagew temp3.png iw-bicubicsharp.png -nogamma -w 200 -h 200 \
  -filterx nearest -filtery cubic0,2.2 -blury 1.05

(The second and third steps cannot be combined, because ImageWorsener resizes the Y dimension first, and the order of operations must not be changed.)

The result:

Photoshop ImageWorsener Differences

The “Differences” image was made with ImageMagick’s compare utility. Any pixels that differ at all are marked in red. There’s some room for improvement, but I think most of the differences are just rounding errors. More investigation is needed to figure out how Photoshop handles pixels near the edges of the image.


This page is by Jason Summers, 12/2011. (Back to ResampleScope main page)