I modified some code I found to add sharpening to sfThumbnail. There may be better code out there and this probably lacks the necessary parameter validation and error checking, but it works well for me!
Originally found at: http://vikjavev.no/computing/ump.php?id=35
/**
* Unsharp Mask for PHP
* Unsharp mask algorithm by Torstein Hønsi
* Please leave this notice.
* @author Torstein Hønsi <thoensi@netcom.no>
* @author Modified for use with Symfony by Dave Furfero <furf@furf.com>
* @version 2.0, 2003-06
*
* WARNING! Due to a known bug in PHP 4.3.2 this script is not working
* well in this version. The sharpened images get too dark. The bug is
* fixed in version 4.3.3.
*
* From version 2 (July 17 2006) the script uses the imageconvolution
* function in PHP version >= 5.1, which improves the performance
* considerably.
*
* Unsharp masking is a traditional darkroom technique that has proven
* very suitable for digital imaging. The principle of unsharp masking
* is to create a blurred copy of the image and compare it to the
* underlying original. The difference in colour values between the
* two images is greatest for the pixels near sharp edges. When this
* difference is subtracted from the original image, the edges will be
* accentuated.
*
* Amount simply says how much of the effect you want. 100 is
* 'normal'
*
* Radius is the radius of the blurring circle of the mask.
*
* Threshold is the least difference in colour values that is allowed
* between the original and the mask. In practice this means that
* low-contrast areas of the picture are left unrendered whereas edges
* are treated normally. This is good for pictures of e.g. skin or
* blue skies.
*
* Any suggenstions for improvement of the algorithm, expecially
* regarding the speed and the roundoff errors in the Gaussian blur
* process, are welcome.
*
* @param int amount (0-500)
* @param float radius (0-50)
* @param int threshold (0-255)
* @return void
* @access public
*/
public function unsharpMask ($amount, $radius, $threshold)
{
// Attempt to calibrate the parameters to Photoshop:
$amount = min($amount, 500) * 0.016;
$radius = min($radius, 50) * 2;
$radius = abs(round($radius)); // Only integers make sense
$threshold = min($threshold, 255);
if ($amount == 0 || $radius == 0) {
return;
}
$w = $this->getThumbWidth();
$h = $this->getThumbHeight();
$imgCanvas = imagecreatetruecolor($w, $h);
$imgCanvas2 = imagecreatetruecolor($w, $h);
$imgBlur = imagecreatetruecolor($w, $h);
$imgBlur2 = imagecreatetruecolor($w, $h);
imagecopy($imgCanvas, $this->thumb, 0, 0, 0, 0, $w, $h);
imagecopy($imgCanvas2, $this->thumb, 0, 0, 0, 0, $w, $h);
// Gaussian blur matrix:
// 1 2 1
// 2 4 2
// 1 2 1
imagecopy($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h); // background
for ($i = 0; $i < $radius; $i++) {
if (function_exists('imageconvolution')) { // PHP >= 5.1
$matrix = array(
array(1, 2, 1),
array(2, 4, 2),
array(1, 2, 1)
);
imageconvolution($imgCanvas, $matrix, 16, 0);
} else {
// Move copies of the image around one pixel at the time
// and merge them with weight according to the matrix.
// The same matrix is simply repeated for higher radii.
imagecopy($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1);
imagecopymerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50);
imagecopymerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333);
imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25);
imagecopymerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333);
imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25);
imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20 );
imagecopymerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667);
imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50);
imagecopy($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
// During the loop above the blurred copy darkens,
// possibly due to a roundoff error. Therefore the sharp
// picture has to go through the same loop to produce a
// similar image for comparison. This is not a good
// thing, as processing time increases heavily.
imagecopy($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
imagecopy($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
}
}
// Calculate the difference between the blurred pixels
// and the original and set the pixels
for ($x = 0; $x < $w; $x++) {
for ($y = 0; $y < $h; $y++) {
$rgbOrig = imageColorAt($imgCanvas2, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = imageColorAt($imgCanvas, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
$bBlur = ($rgbBlur & 0xFF);
// When the masked pixels differ less from the original
// than the threshold specifies, they are set to their original value.
$rNew = (abs($rOrig - $rBlur) >= $threshold)
? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig))
: $rOrig;
$gNew = (abs($gOrig - $gBlur) >= $threshold)
? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig))
: $gOrig;
$bNew = (abs($bOrig - $bBlur) >= $threshold)
? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig))
: $bOrig;
if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
$pixCol = imageColorAllocate($this->thumb, $rNew, $gNew, $bNew);
imageSetPixel($this->thumb, $x, $y, $pixCol);
}
}
}
}