2015-03-20 15 views
10

Ich verwende PHP Imagick, um Bilder zur Laufzeit zu skalieren. Die Website verfügt über eine Funktion zum Hochladen von Bildern, und wir können nicht darauf vertrauen, dass der Benutzer webfreundliche JPEGs verwendet, da während der mass-import viele 5MB bis 5MB Bilder und sogar ein paar so groß wie 13MB sind. Jedes Bild wird zu einem Thumbnail (200x200), und wenn von der Listenansicht in die Detailansicht gewechselt wird, wird nur ein Bild angezeigt, so dass die Leistung keine große Sache ist, obwohl wir sie nicht vollständig wegwerfen können. Hier ist, was wir tun, so weit:PHP Imagick JPEG Optimierung

$iMagick = new Imagick($file); 
$iMagick->setImageResolution(72,72); 
$iMagick->resampleImage(72,72,imagick::FILTER_UNDEFINED,1); 
$geometry = $iMagick->getImageGeometry(); 
if ($geometry['height'] > 1920 || $geometry['width'] > 1080) { 
    $iMagick->scaleImage(1920, 0); 
    if($geometry['height'] > $resizeHeight) { 
     $iMagick->scaleImage(0, 1080); 
    } 
} 
$iMagick->setImageCompression(Imagick::COMPRESSION_JPEG); 
$iMagick->setImageCompressionQuality($compression); 
$iMagick->writeImage($file); 
$Imagick->clear(); 

Randbemerkung: Ich habe erkannt, den Fehler in meiner bedingte Logik hier etwa Höhe/Breite, so ignorieren, dass für den Augenblick. Ich werde die Frage bald bearbeiten, um die korrekten Zustandsvergleiche zu berücksichtigen. Für diejenigen, die es nicht gefangen haben, skaliert es alle Bilder auf 1920 weit, auch wenn es die Höhe skalieren soll, dann nach der Skalierung wird es wieder auf 1080 height verkleinert.

Das Bild, das ich zum Testen verwendet habe, beginnt als 3MB 2398×2400 Bild. Skalierung auf 1079x1080 ergibt ein 1.5MB Bild, dann JPEG Kompression bei Qualität 70 bringt es auf 750KB. Mit kraken.io konnte das Image auf knapp 60KB komprimiert werden. Gibt es weitere Dinge, die ich mit diesem Skript tun kann, da es zur Laufzeit das Bild optimieren muss?

Ich habe Vorschläge gelesen, libjpeg zu verwenden, die installiert ist, aber ich kann keine Dokumentation finden, welche Funktionen es aktiviert, oder ob es eine Möglichkeit gibt, Imagick zu zwingen, es spezifisch zu verwenden. Ich bin mir nicht einmal sicher, ob Imagick es schon benutzt.

Edit: Lösung

function itm_optimizeImage($file, $compression = 70, $maxDimensions = ['width' => null, 'height' => null]) { 
    $save = false; 
    $fi = new finfo(FILEINFO_MIME); 
    $mime = explode(';', $fi->file($file)); 
    switch ($mime[0]) { 
     // possible to optimize other image types in the future 
     case 'image/jpeg': 
      try { 
       $iMagick = new Imagick($file); 
       if ($iMagick->getImageCompressionQuality() > $compression) { 
        $file = !itm_compressJPEG($file, $compression, $maxDimensions, $iMagick); 
       } 
      } 
      catch (Exception $e) { 
       error_log(__FUNCTION__ . " $path/$file failed: " . $e->getMessage()); 
       return false; 
      } 
      if ($file) { 
       $pathParts = pathinfo($file); 
       rename($file, $pathParts['dirname'] . '/' . $pathParts['filename'] . '.large.' . $pathParts['extension']); 
       $iMagick->writeImage($file); 
      } 
      $iMagick->clear(); 
      break; 
    } 

    return $file; 
} 

function itm_compressJPEG($file, $compression = 70, $maxDimensions = ['width' => null, 'height' => null], &$iMagick = null) { 
    try { 
     $iMagickCreated = true; 
     if ($iMagick) $iMagickCreated = false; 
     else $iMagick = new Imagick($file); 

     $iMagick->setImageResolution(72,72); 
     $iMagick->resampleImage(72,72,imagick::FILTER_UNDEFINED,1); 
     $geometry = $iMagick->getImageGeometry(); 
     if (($geometry['width']/$maxDimensions['width']) > ($geometry['height']/$maxDimensions['height'])) { 
      // scale by width 
      $iMagick->scaleImage($maxDimensions['width'], 0); 
     } else { 
      // scale by height 
      $iMagick->scaleImage(0, $maxDimensions['height']); 
     } 
     $iMagick->setImageCompression(Imagick::COMPRESSION_JPEG); 
     $iMagick->setImageCompressionQuality($compression); 
     $iMagick->setImageFormat('jpg'); 
     $iMagick->stripImage(); 

     if ($iMagickCreated) { 
      $pathParts = pathinfo($file); 
      rename($file, $pathParts['dirname'] . '/' . $pathParts['filename'] . '.large.' . $pathParts['extension']); 
      $iMagick->writeImage($file); 
      $Imagick->clear(); 
     } 
     return $file; 
    } 
    catch (Exception $e) { 
     error_log(__FUNCTION__ . " $path/$file failed: " . $e->getMessage()); 
     return false; 
    } 
} 
+0

60KB für ein 1079x1080 Bild scheint ziemlich anständig. Ich glaube nicht, dass Sie es viel kleiner machen können, ohne die Pixelabmessungen zu verkleinern oder die Qualität ernsthaft zu senken. – GolezTrol

+0

Das Problem ist, dass ich mit meinem Skript nicht annähernd so weit komme. Es ist bei ~ 750KB im Moment, die 60KB war ein Vergleich (was das kraken.io Tool auf dem gleichen Bild konnte). Ich sehe 60KB nicht als genaues Ziel, aber unter 200KB wäre nett. – MaKR

Antwort

9

die setImageCompressionQuality auf einen Wert von 70 Stellen und fügen diese beiden Zeilen nach.

$image->setImageFormat("jpg"); 
$image->stripImage(); 

Dies wird die Größe der Bilder drastisch reduzieren.

+1

Ich hatte vorher stripImage() benutzt und es hatte keine Wirkung. Das Hinzufügen von setImageFormat ('jpg') ohne stripImage() reduziert die Größe um ~ 20KB mehr (wir haben versucht, exif/Metadaten zu speichern, aber es ist sekundär zur Leistung). Mit beiden zusammen ergab das Endergebnis dieses Testbildes 178 KB, was die perfekte Kombination von Qualität und Komprimierung darstellt. Vielen Dank! – MaKR

+1

Hat jemand eine Erklärung für das seltsame Verhalten von @MaKR beschrieben? Warum sollte setImageFormat ('jpg') überhaupt benötigt werden, wenn der Dateiname mit .jpg endet? Warum sollte setImageCompression() überhaupt benötigt werden? Gibt es eine Erklärung für all diesen Imagick-Unsinn? – matteo

+0

Ausgezeichnete Lösung ... Ich komprimierte 5MB Bilder nur bis 788KB, mit diesen zusätzlichen Methoden sind die Dateien in zweistelligen KB-Größen. –