Tweak and fix the smart crop effect. This now works under Imagick
authorMichael J. Rubinsky <mrubinsk@horde.org>
Tue, 28 Sep 2010 16:28:58 +0000 (12:28 -0400)
committerMichael J. Rubinsky <mrubinsk@horde.org>
Sat, 2 Oct 2010 20:02:09 +0000 (16:02 -0400)
framework/Image/lib/Horde/Image/Effect.php
framework/Image/lib/Horde/Image/Effect/Imagick/SmartCrop.php
framework/Image/package.xml
framework/Image/tests/im.php
framework/Image/tests/runtest.php

index cea8ea0..e6fd37c 100644 (file)
@@ -58,7 +58,7 @@ class Horde_Image_Effect
         $this->_logger = $logger;
     }
 
-    public function factory($type, $driver, $params)
+    static public function factory($type, $driver, $params)
     {
         if (is_array($type)) {
             list($app, $type) = $type;
@@ -86,9 +86,11 @@ class Horde_Image_Effect
                 @include_once $path;
             }
         }
+        
         if (class_exists($class)) {
             $effect = new $class($params);
         } else {
+            $params['logger']->err(sprintf("Horde_Image_Effect %s for %s driver not found.", $type, $driver));
             throw new Horde_Image_Exception(sprintf("Horde_Image_Effect %s for %s driver not found.", $type, $driver));
         }
 
index 5eea966..7e5ed42 100644 (file)
@@ -27,6 +27,7 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
 
     public function apply()
     {
+        mt_srand(1);
         $this->_params = new Horde_Support_Array($this->_params);
        
         // Existing geometry
@@ -34,6 +35,9 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
         $w0 = $geometry['width'];
         $h0 = $geometry['height'];
 
+        $w = $this->_params->width;
+        $h = $this->_params->height;
+        
         // @TODO: Parameterize these
         $r = 1;         // radius of edge filter
         $nk = 9;        // scale count: number of crop sizes to try
@@ -43,7 +47,12 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
         $ar = $this->_params->width / $this->_params->height;
 
         // Existing AR
-        $ar0 = $w0/$h0;
+        $ar0 = $w0 / $h0;
+
+        $this->_logger->debug(sprintf("SmartCrop: %d x %d => %d x %d ", $w0, $h0, $w, $h));
+        $this->_logger->debug('OAR: ' . $ar0);
+        $this->_logger->debug('TAR: ' . $ar);
+
 
         // Compute COE
         $img = $this->_image->imagick->clone();
@@ -51,31 +60,24 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
         $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();
-                }
-            }
-
+            $pixel = $img->getImagePixelColor($i, $j);
+            $val = $pixel->getColor();
+            $val = $val['b'];
             $sum += $val;
-            $xcenter += ($i + 1) * $val;
-            $ycenter += ($j + 1) * $val;
+            $xcenter = $xcenter + ($i + 1) * $val;
+            $ycenter = $ycenter + ($j + 1) * $val;
         }
-
         $xcenter /= $sum;
         $ycenter /= $sum;
-
+        $this->_logger->debug('COE: ' . $xcenter . 'x' . $ycenter);
+        
         // crop source img to target AR
-        if ($w0/$h0 > $ar) {
+        if ($w0 / $h0 > $ar) {
             // source AR wider than target
             // crop width to target AR
             $wcrop0 = round($ar * $h0);
@@ -121,18 +123,15 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
             if ($ycrop+$hcrop > $h0) {
                 $ycrop = $h0 - $hcrop;
             }
+            $this->_logger->debug("crop: $wcrop, $hcrop, $xcrop, $ycrop");
 
             $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;
+                $pixel = $img->getImagePixelColor($xcrop + $i, $ycrop + $j);
+                $val = $pixel->getColor();
+                $beta += $val['b'];// & 0xFF;
             }
 
             $area = $wcrop * $hcrop;
@@ -140,6 +139,7 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
 
             // best image found, save the params
             if ($betanorm > $maxbetanorm) {
+                $this->_logger->debug('Found best');
                 $maxbetanorm = $betanorm;
                 $maxparam['w'] = $wcrop;
                 $maxparam['h'] = $hcrop;
@@ -148,6 +148,7 @@ class Horde_Image_Effect_Imagick_SmartCrop extends Horde_Image_Effect
             }
         }
 
+        $this->_logger->debug('Cropping');
         // Crop to best
         $this->_image->imagick->cropImage($maxparam['w'],
                                           $maxparam['h'],
index 99ca97d..1ac7f4e 100644 (file)
@@ -175,7 +175,7 @@ Initial Horde 4 package
    <install as="Horde/Image/Effect/Imagick/TextWatermark.php" name="lib/Horde/Image/Effect/Imagick/TextWatermark.php" />
    <install as="Horde/Image/Effect/Imagick/Unsharpmask.php" name="lib/Horde/Image/Effect/Imagick/Unsharpmask.php" />
    <install as="Horde/Image/Effect/Imagick/LiquidResize.php" name="lib/Horde/Image/Effect/Imagick/LiquidResize.php" />
-   <isntall as="Horde/Image/Effect/Imagick/SmartCrop.php" name="lib/Horde/Image/Effect/Imagick/SmartCrop.php" />
+   <install as="Horde/Image/Effect/Imagick/SmartCrop.php" name="lib/Horde/Image/Effect/Imagick/SmartCrop.php" />
    <install as="Horde/Image/Exif/Base.php" name="lib/Horde/Image/Exif/Base.php" />
    <install as="Horde/Image/Exif/Bundled.php" name="lib/Horde/Image/Exif/Bundled.php" />
    <install as="Horde/Image/Exif/Exiftool.php" name="lib/Horde/Image/Exif/Exiftool.php" />
index 20a3dd3..81e312a 100644 (file)
@@ -15,18 +15,34 @@ Horde_Registry::appInit('horde', array('authentication' => 'none'));
 // profiling.
 $driver = Horde_Util::getFormData('driver', 'Im');
 $test = Horde_Util::getFormData('test');
+
+// Don't use horde config since we might be configured for Imagick only.
 $convert = trim(`which convert`);
 $identify = trim(`which identify`);
+
 $handler = new Horde_Log_Handler_Stream(fopen('/tmp/imagetest.log','a+'));
 $logger = new Horde_Log_Logger($handler);
 
 switch ($test) {
+case 'smart':
+    $time = xdebug_time_index();
+    $image = getImageObject(array('filename' => 'img4.jpg'));
+    $image->addEffect('SmartCrop', array('width' => 100, 'height' => 100));
+    $image->display();
+    $time = xdebug_time_index() - $time;
+    $memory = xdebug_peak_memory_usage();
+    logThis($test, $time, $memory);
+    exit;
+
 case 'liquid':
     $time = xdebug_time_index();
     $image = getImageObject(array('filename' => 'img4.jpg'));
     $image->addEffect('LiquidResize', array('ratio' => true, 'width' => 612, 'height' => 340, 'delta_x' => 3, 'rigidity' => 0));
     $image->display();
-    break;
+    $time = xdebug_time_index() - $time;
+    $memory = xdebug_peak_memory_usage();
+    logThis($test, $time, $memory);
+    exit;
 
 case 'multipage':
     $time = xdebug_time_index();
@@ -40,7 +56,9 @@ case 'multipage':
             $first = false;
         }
     }
-    logThis($test, $time, xdebug_peak_memory_usage());
+    $time = xdebug_time_index() - $time;
+    $memory = xdebug_peak_memory_usage();
+    logThis($test, $time, $memory);
 
 case 'testInitialState':
     // Solid blue background color - basically tests initial state of the
@@ -465,9 +483,10 @@ function getImageObject($params = array())
 
     $context = array('tmpdir' => Horde::getTempDir(),
                      'convert' => $GLOBALS['convert'],
-                     'logger' => $GLOBALS['logger'],
+                     'logger' => $GLOBALS['injector']->getInstance('Horde_Log_Logger'),
                      'identify' => $GLOBALS['identify']);
     $params['context'] = $context;
+
     return Horde_Image::factory($GLOBALS['driver'], $params);
 }
 
index 169b8de..9718e0c 100644 (file)
@@ -35,6 +35,7 @@ $allTests = array(
     'testResize' => 'Test resize method.',
     'multipage' => 'Test Multipage tiffs',
     'liquid' => 'Test Seam Carving',
+    'smart' => 'Test Smart Crop (Center of Edginess)'
 );
 ?>
 <html>