From 1a4d30995c9cfe42b6ee6126c15873fde1131106 Mon Sep 17 00:00:00 2001 From: "Michael J. Rubinsky" Date: Mon, 27 Sep 2010 12:01:10 -0400 Subject: [PATCH] First stab at a Center of Edginess smart crop. Currently not working, but the basic algorithm is there, and still needs further porting to the Im driver. --- .../lib/Horde/Image/Effect/Imagick/SmartCrop.php | 160 +++++++++++++++++++++ framework/Image/package.xml | 2 + 2 files changed, 162 insertions(+) create mode 100644 framework/Image/lib/Horde/Image/Effect/Imagick/SmartCrop.php diff --git a/framework/Image/lib/Horde/Image/Effect/Imagick/SmartCrop.php b/framework/Image/lib/Horde/Image/Effect/Imagick/SmartCrop.php new file mode 100644 index 000000000..5eea96685 --- /dev/null +++ b/framework/Image/lib/Horde/Image/Effect/Imagick/SmartCrop.php @@ -0,0 +1,160 @@ + + * http://jueseph.com/2010/06/opticrop-usage-and-implementation/ + * + * See the enclosed file COPYING for license information (LGPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html. + * + * @author Michael J. Rubinsky + * @package Horde_Image + */ +class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect +{ + /** + * Valid parameters: + *
+     *    width    - Target width
+     *    height   - Target height
+     * 
+ * + * @var array + */ + protected $_params = array(); + + public function apply() + { + $this->_params = new Horde_Support_Array($this->_params); + + // Existing geometry + $geometry = $this->_image->getDimensions(); + $w0 = $geometry['width']; + $h0 = $geometry['height']; + + // @TODO: Parameterize these + $r = 1; // radius of edge filter + $nk = 9; // scale count: number of crop sizes to try + $gamma = 0.2; // edge normalization parameter -- see documentation + + // Target AR + $ar = $this->_params->width / $this->_params->height; + + // Existing AR + $ar0 = $w0/$h0; + + // Compute COE + $img = $this->_image->imagick->clone(); + $img->edgeImage($r); + $img->modulateImage(100,0,100); + $img->blackThresholdImage("#0f0f0f"); + + // Get a 1x1 iterator (only way to get a single pixel's info without + // iterating the entire row. + $xcenter = $ycenter = $sum = 0; + $n = 100000; + for ($k = 0; $k < $n; $k++) { + $i = mt_rand(0, $w0 - 1); + $j = mt_rand(0, $h0 - 1); + // A single pixel iterator! + $itr = $img->getPixelRegionIterator($i, $j, 1, 1); + foreach ($itr as $row => $pixels) { + foreach ($pixels as $col => $pixel) { + $val = $pixel->getColor(); + } + } + + $sum += $val; + $xcenter += ($i + 1) * $val; + $ycenter += ($j + 1) * $val; + } + + $xcenter /= $sum; + $ycenter /= $sum; + + // crop source img to target AR + if ($w0/$h0 > $ar) { + // source AR wider than target + // crop width to target AR + $wcrop0 = round($ar * $h0); + $hcrop0 = $h0; + } else { + // crop height to target AR + $wcrop0 = $w0; + $hcrop0 = round($w0 / $ar); + } + + // crop parameters for all scales and translations + $params = array(); + + // crop at different scales + $hgap = $hcrop0 - $h; + $hinc = ($nk == 1) ? 0 : $hgap / ($nk - 1); + $wgap = $wcrop0 - $w; + $winc = ($nk == 1) ? 0 : $wgap / ($nk - 1); + + // find window with highest normalized edginess + $n = 10000; + $maxbetanorm = 0; + $maxfile = ''; + $maxparam = array('w' => 0, + 'h' => 0, + 'x' => 0, + 'y' => 0); + + for ($k = 0; $k < $nk; $k++) { + $hcrop = round($hcrop0 - $k * $hinc); + $wcrop = round($wcrop0 - $k * $winc); + $xcrop = $xcenter - $wcrop / 2; + $ycrop = $ycenter - $hcrop / 2; + if ($xcrop < 0) { + $xcrop = 0; + } + if ($xcrop + $wcrop > $w0) { + $xcrop = $w0 - $wcrop; + } + if ($ycrop < 0) { + $ycrop = 0; + } + if ($ycrop+$hcrop > $h0) { + $ycrop = $h0 - $hcrop; + } + + $beta = 0; + for ($c = 0; $c < $n; $c++) { + $i = mt_rand(0, $wcrop - 1); + $j = mt_rand(0, $hcrop - 1); + $itr = $img->getPixelRegionIterator($xcrop + $i, $ycrop + $j, 1, 1); + foreach ($itr as $row => $pixels) { + foreach ($pixels as $col => $pixel) { + $val = $pixel->getColor(); + } + } + $beta += $val & 0xFF; + } + + $area = $wcrop * $hcrop; + $betanorm = $beta / ($n * pow($area, $gamma - 1)); + + // best image found, save the params + if ($betanorm > $maxbetanorm) { + $maxbetanorm = $betanorm; + $maxparam['w'] = $wcrop; + $maxparam['h'] = $hcrop; + $maxparam['x'] = $xcrop; + $maxparam['y'] = $ycrop; + } + } + + // Crop to best + $this->_image->imagick->cropImage($maxparam['w'], + $maxparam['h'], + $maxparam['x'], + $maxparam['y']); + $this->_image->imagick->scaleImage($w, $h); + $img->destroy(); + } + +} \ No newline at end of file diff --git a/framework/Image/package.xml b/framework/Image/package.xml index c890172f3..99ca97d1c 100644 --- a/framework/Image/package.xml +++ b/framework/Image/package.xml @@ -70,6 +70,7 @@ Initial Horde 4 package + @@ -174,6 +175,7 @@ Initial Horde 4 package + -- 2.11.0