Optimizing Image Resizing for Web Uploads
Our popular vertical market application - CondoConduit - allows users to upload their own images to web sites personalized for their homeowner associations.
People are getting these images from lots of different sources - the web, digital cameras, cell-phone cameras, etc - and these users have varying levels of computer competancy.
For a long time, we were blindly resizing these uploaded images to a max of 300 x 300 pixels - which often led to really ugly results. Blech!
All we did was see if either side of the image was over the max size for the page. If so, we took the longer of the two sides and figured out the % reduction to make it the max size, then applied that to both sides and resized the image using the WebConnect ResizeImage function.
Depending upon the size of the original image, the results would really... suck.
I found that when Windows GDI+ resizes an image (reduces the number of pixels), it does 'pixel color averaging' to figure out the color of the resulting 'combined' pixel. As you can imagine, if you resize 6 pixels to 3 pixels, it's pretty sharp (just average 3 pixels together to get the resulting pixel color)... but if you resize 8 pixels to 3 pixels, the color averaging takes place over two adjoining pixels rather than one so the resulting image gets really blurry (I'm sure I'm technically wrong about the exact calculations used to pixel average, but the results support this general concept).
So for awhile I found I would have to manually take each large image and resize it in my image editing software using these general guidelines: Crop first (if possible) to get the image into a multiple of 300 pixels (600, 900, etc), then resize to the final image size and uplod to the site.
I asked around in all the usual places about whether they had an automated VFP/GDI+ routine to fill what seemed to be a common need. Getting no responses, I finally bit the bullet and takled the task myself.
What I decided to do was to look for a common denominator smaller than the desired max image size for both the image width and the height. The result should be as close to the desired image size as possible, but had to be a divisor of both sides.
Being too mathematically challenged to do this in a graceful way, I chose brute force. I just count down from the max size by one until I find a whole number to divide by that works with both sides. I am only willing to go down 50 pixels (so the resulting image is larger than a postage stamp):
************************************************************************
*** Function: IntegerResize
*** Assume:
*** Created: 03/28/2006
*** Revised:
*** Copyright: (c) 2006, Ideate, LLC
************************************************************************
FUNCTION IntegerResize()
LPARAMETERS pcDiskFileName, pnMaxHoriz, pnMaxVert
#IF .F.
LOCAL Request as wwRequest, Response as wwResponse
#ENDIF
pnHoriz = 0
pnVert = 0
pnResolution = 0
pnNewHoriz = 0
pnNewVert = 0
pnClosestCrop = 1000
pnCropHoriz = 0
pnCropVert = 0
plCropFirst = .T.
pnCompression = 0
IF GetImageInfo(lcDiskFileName, @pnHoriz, @pnVert, @pnResolution)
IF pnHoriz > pnMaxHoriz OR pnVert > pnMaxVert
pcTempFileName = ADDBS(JUSTPATH(pcDiskFileName)) + "Delete_Me." + JustExt(pcDiskFileName)
IF FILE(pcTempFileName)
ERASE (pcTempFileName)
ENDIF
RENAME &pcDiskFileName TO &pcTempFileName
IF pnMaxHoriz/pnHoriz > pnMaxVert/pnVert
pcBiggerSide = [Vert]
ELSE
pcBiggerSide = [Horiz]
ENDIF
FOR i = 0 TO 50
pnDenominator = pnMax&pcBiggerSide - i
IF MOD(pnHoriz,pnDenominator) = 0 AND MOD(pnVert,pnDenominator) = 0
* We have a hit
pnNewHoriz = pnHoriz/(pn&pcBiggerSide/pnDenominator)
pnNewVert = pnVert/(pn&pcBiggerSide/pnDenominator)
plCropFirst = .F.
EXIT
ELSE
* keep track of the closest for cropping or padding
IF MOD(pnHoriz,pnDenominator) + MOD(pnVert,pnDenominator) < pnClosestCrop
pnClosestCrop = MOD(pnHoriz,pnDenominator) + MOD(pnVert,pnDenominator)
pnNewHoriz = INT(pnHoriz/(pn&pcBiggerSide/pnDenominator))
pnNewVert = INT(pnVert/(pn&pcBiggerSide/pnDenominator))
pnCropHoriz = MAX(INT(pnHoriz/pnNewHoriz - pnMaxHoriz),0)
pnCropVert = MAX(INT(pnVert/pnNewVert - pnMaxVert),0)
ENDIF
ENDIF
NEXT
IF plCropFirst && we didn't find a value
* Crop and set variables for resizing
*************************************
** TO DO!
*************************************
* PAD images for negative crop values
*************************************
pnLeft = INT(pnCropHoriz/2)
pnTop = INT(pnCropVert/2)
ReadImage(pcTempFileName,pcDiskFileName,pnLeft,pnTop,pnHoriz-pnCropHoriz,pnVert-pnCropVert)
ERASE (pcTempFileName)
RENAME &pcDiskFileName TO &pcTempFileName
ENDIF
* CreateThumbnail(pcTempFileName,pcDiskFileName,pnNewHoriz,pnNewVert)
ResizeImage(pcTempFileName, pcDiskFileName, pnNewHoriz, pnNewVert, pnCompression)
ERASE (pcTempFileName)
ENDIF
ENDIF
ENDFUNC && IntegerResize
Now resized image are crisp and clear - and I can use the time I used to fill resizing my client's images to write a blog!
Blogged with Flock
