![]() |
|
Snippets |
|
I modified some found code (http://vikjavev.no/computing/ump.php?id=35) to add a sharpening feature to sfThumbnail.
Simply add this code to the sfThumbnail class:
/** * 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); } } } }
Then call it like this from your script:
$thumb = new sfThumbnail(160, 120); $thumb->loadFile($original_img); $thumb->unsharpMask(40, 0.5, 3); $thumb->save($img_path, 'image/png');
The three parameters are amount (degree of sharpening from 0-500), radius (pixel radius of blur, from 0-50), and threshold (degree of difference between original and blur, from 0-255). Read the included comments for more info.
It could probably use some better validation and error checking, but it's worked flawlessly for me so far.