From time to time people will ask (either here, or cf-talk, or wherever) about the speed of ColdFusion 8's image resizing. Typically they are asking because the speed can be a bit slow. My normal response is to remind people that Adobe provided numerous ways to resize, ranging from very slow and high quality options to much quicker and less quality options. I've been meaning to write a script that would compare these resize options and I finally got a chance too this morning.

Before I go into the script, I have to apologize. I normally tell people about the resize options (called interpolation algorithms) and then point out that Adobe ships 17 choices. However I was wrong. The options are grouped into a list of values with generic terms like highPerformance or highestQuality and named ones like lanczos and hanning (which sound like Star Trek names if you ask me).

Here is where things get a bit weird. You would think there would be a one to one relation between the generic and named options. However there are more named options than generic ones. My assumption is that Adobe mapped some of the generic options to one of the named options. I created the following list would uses both the generic and named options, and should represent a move from highest quality to highest performance.

  • highestQuality
  • lanczos
  • highquality
  • mitchell
  • mediumPerformance
  • quadratic
  • mediumquality
  • hamming
  • hanning
  • hermite
  • highPerformance
  • blackman
  • bessel
  • highestPerformance
  • nearest
  • bicubic
  • bilinear

My script would go through each of these, perform a resize and see how long it took, how big the file was, and output a link to the image so I could see the quality.

In my first test using a 300k or so JPG, the highest quality resize took a huge amunt of time - close to 4 minutes. In my second test, using a larger image, it took a lot less time. The docs do say that resizing performance/result will depend on the source image, which isn't surprising, but it bares repeating I think.

Something else I noticed was that - both with the super slow to resize image and the not so slow image - I was not able to visibly tell a difference. It may be that I don't have an artist's eyes. It may also be that when I saved the image, I always used the highest quality setting (so in other words, a range of options for resizing, but always the best for saving). Based on what I saw though - at least for images to be shown on the web - I see no reason to not use the quickest resize. Of course 2 tests isn't very scientific, but it's something to keep in mind. ColdFusion defaults to the highest performance. I'd probably not use that default in my applications.

I've included the script I used below. All you have to do is modify the sourceImage line to test yourself. Here is the results from my last test:

MethodSizeTime (Seconds)
highestQuality425Kb1.703
lanczos425Kb1.703
highquality392Kb1.187
mitchell392Kb1.188
mediumPerformance392Kb1.203
quadratic367Kb0.938
mediumquality390Kb0.704
hamming390Kb0.688
hanning398Kb0.687
hermite397Kb0.703
highPerformance390Kb0.687
blackman407Kb0.687
bessel385Kb1.828
highestPerformance433Kb0.031
nearest435Kb0.016
bicubic433Kb0.032
bilinear414Kb0.031

Notice that there seems to be no real difference in file size. At most 50k or so separates them. Also notice that bessel is oddly slow compared to the algorithms near it. That may be one to avoid.

In case you want to see the images themselves (I felt they were too big to include on this blog entry), I've linked the original, highest quality, and highest performance images:

<cfscript> /** * Will take a number returned from a File.Filesize, calculate the number in terms of Bytes/Kilobytes/Megabytes and return the result. * v2 by Haikal Saadh * * @param number Size in bytes of the file. (Required) * @return Returns a string. * @author Kyle Morgan (admin@kylemorgan.com) * @version 2, August 7, 2006 */ function fncFileSize(size) { if ((size gte 1024) and (size lt 1048576)) { return round(size / 1024) & "Kb"; } else if (size gte 1048576) { return decimalFormat(size/1048576) & "Mb"; } else { return "#size# b"; } } </cfscript>

<cfset methods = "highestQuality,lanczos,highquality,mitchell,mediumPerformance,quadratic,mediumquality,hamming,hanning,hermite,highPerformance,blackman,bessel,highestPerformance,nearest,bicubic,bilinear">

<cfset results = queryNew("method,size,time")>

<cfset sourceImage = expandPath("./DSC00014.jpg")>

<cfset finfo = getFileInfo(sourceImage)> <cfset img = imageRead(sourceImage)> <cfset iinfo = imageInfo(img)>

<cfdump var="#iinfo#" label="File Size in Bytes: #finfo.size#">

<cfimage action="writeToBrowser" source="#sourceImage#">

<hr/>

<cfloop index="m" list="#methods#"> <cfoutput> <h2>Resize Method: #m#</h2> <cfset newImage = duplicate(img)> <cfset timer = getTickCount()> <cfset imageScaleToFit(newImage, 700, 700, m)> <cfset total = getTickCount() - timer> <cfset filename = m & "" & getFileFromPath(sourceImage)> <cfset imageWrite(newImage,expandPath(filename),1)> <cfset finfo = getFileInfo(expandPath(filename))> <cfoutput><p>#fncFileSize(finfo.size)# bytes at #total/1000# seconds</p></cfoutput> <img src="./#m##getFileFromPath(sourceImage)#"> </cfoutput> <cfset queryAddRow(results)> <cfset querySetCell(results, "method", m)> <cfset querySetCell(results, "size", fncFileSize(finfo.size))> <cfset querySetCell(results, "time", total/1000)> <cfflush> </cfloop>

<cftable query="results" border colHeaders htmlTable> <cfcol header="Method" text="#method#"> <cfcol header="Size" text="#size#"> <cfcol header="Time (Seconds)" text="#time#"> </cftable>