2017-04-17 11 views
1

Ich habe eine Funktion, die ich benutze, um Benutzer hochgeladene Bilder zu akzeptieren, skalieren proportional zu einer haben eine maximale Breite/Höhe von 4.000px, und erzeugen auch 400px und 800px Thumbnails. Es muss in der Lage sein, transparente PNGs zu handhaben und einen weißen Hintergrund aufzutragen.PHP GD Größenanpassung verursacht Artefakte

Mein aktueller Code macht all dies, jedoch fügt es unerwünschte Artefakte hinzu, die nicht typisch für JPEGs sind. Sie sind vertikale Streifen und sehen in der Nähe wie ein ausgewaschener Barcode aus (400% Zoom Screenshot beigefügt). Dies tritt sogar beim ursprünglichen Bild auf, wenn es in der Größe hochgeladen wird, auf die es skaliert wird. Es scheint bei transparenten PNGs noch häufiger zu sein, aber auch bei weißen Regionen von JPEGs. JPEGs mit Qualität wird 80.

400% Zoom of Originally All-White Area

function resize_image($file, $w, $h, $strict = false, $crop = false, $path = null, $thumbnail = false) 
{ 
    // Check for Valid Image + Calculate Ratio 
    list($width, $height) = getimagesize($file); 

    if (empty($width) || empty($height)) 
    { 
     echo json_encode(['result' => 'error', 'error' => 'file_format_invalid']); 
     http_response_code(405); 
     exit; 
    } 

    $r = $width/$height; 

    if (!$strict) 
    { 
     $w = min($w, $width); 
     $h = min($h, $height); 
    } 

    $wTa = min($w, 400); 
    $hTa = min($h, 400); 

    $wTb = min($w, 800); 
    $hTb = min($h, 800); 

    // Apply Crop Constraint 
    if ($crop) 
    { 
     if ($width > $height) 
     { 
      $width = ceil($width - ($width * abs($r - $w/$h))); 
      $widthTa = ceil($width - ($width * abs($r - $wTa/$hTa))); 
      $widthTb = ceil($width - ($width * abs($r - $wTb/$hTb))); 
     } 

     else 
     { 
      $height = ceil($height - ($height * abs($r - $w/$h))); 
      $heightTa = ceil($height - ($height * abs($r - $wTa/$hTa))); 
      $heightTb = ceil($height - ($height * abs($r - $wTa/$hTb))); 
     } 

     $newWidth = $w; 
     $newHeight = $h; 
    } 

    else 
    { 
     if ($w/$h > $r || $r < 1) 
     { 
      $newWidth = $h * $r; 
      $newWidthTa = $hTa * $r; 
      $newWidthTb = $hTb * $r; 

      $newHeight = $h; 
      $newHeightTa = $hTa; 
      $newHeightTb = $hTb; 
     } 

     else 
     { 
      $newHeight = $w/$r; 
      $newHeightTa = $wTa/$r; 
      $newHeightTb = $wTb/$r; 

      $newWidth = $w; 
      $newWidthTa = $wTa; 
      $newWidthTb = $wTb; 
     } 
    } 

    // Create, Resample + Return Image 
    $src = imagecreatefromstring(file_get_contents($file)); 
    $dst = imagecreatetruecolor($newWidth, $newHeight); 
    $fff = imagecolorallocate($dst, 255, 255, 255); 

    imagefill($dst, 0, 0, $fff); 
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height); 

    if (!is_null($path)) 
    { 
     imagejpeg($dst, $path, 80); 

     if ($thumbnail) 
     { 
      $dstThumbA = imagecreatetruecolor($newWidthTa, $newHeightTa); 
      $dstThumbB = imagecreatetruecolor($newWidthTb, $newHeightTb); 

      $fffThumbA = imagecolorallocate($dstThumbA, 255, 255, 255); 
      $fffThumbB = imagecolorallocate($dstThumbB, 255, 255, 255); 

      imagefill($dstThumbA, 0, 0, $fffThumbA); 
      imagefill($dstThumbB, 0, 0, $fffThumbB); 

      imagecopyresampled($dstThumbA, $src, 0, 0, 0, 0, $newWidthTa, $newHeightTa, $width, $height); 
      imagecopyresampled($dstThumbB, $src, 0, 0, 0, 0, $newWidthTb, $newHeightTb, $width, $height); 

      imagejpeg($dstThumbA, str_replace('.jpg', '-thumb.jpg', $path), 80); 
      imagejpeg($dstThumbB, str_replace('.jpg', '[email protected]', $path), 80); 
     } 
    } 

    return $dst; 
} 
+1

Ich möchte sagen versuchen mit 'imagecopyresized' anstelle von' imagecopyresampled' - vielleicht hilft dies auch http://stackoverflow.com/questions/23200823/gd-quality-issue-with-transparent -png || http://stackoverflow.com/questions/23993901/imagecopyresampled-introduces-artifacts-in-transparent-png –

+0

Hmm, guter Gedanke, 'imagecopyresized' funktioniert und ohne diese Artefakte, obwohl es zu viel Aliasing eingeführt, um akzeptabel zu sein. Mir ist aufgefallen, dass die Symptome bei der Produktion auf PHP 7.1.3 unter Ubuntu auftreten, nicht aber bei PHP 7.0.1 unter Windows in der Entwicklung. Vielleicht config-bezogen? –

Antwort

0

folgerte ich gespeichert, dass die Frage an die Umgebung spezifisch ist (tritt auf PHP 7.1.3 unter Ubuntu, aber nicht unter PHP 7.0.1 unter Windows) . Die Neuinstallation von php7.1 und php7.1-gd hatte keine Auswirkungen.

Schließlich habe ich mich entschlossen mit ImageMagick die Kugel und neu schreiben zu beißen, in einem viel prägnanten Codeblock resultierenden:

$magickSource = new Imagick(); 
$magickSource->readImageBlob(file_get_contents($file)); 
$magickSource = $magickSource->flattenImages(); 

$magickFull = clone $magickSource; 
$magickFull->resizeImage(min($originalWidth, $newWidth), min($originalHeight, $newHeight), Imagick::FILTER_LANCZOS, 1, true); 
$magickFull->setImageCompression(Imagick::COMPRESSION_JPEG); 
$magickFull->setImageCompressionQuality(75); 
$magickFull->stripImage(); 
$magickFull->writeImage($path); 
$magickFull->destroy(); 

if ($thumbnail) 
{ 
    $magickThumb = clone $magickSource; 
    $magickThumb->resizeImage(min($originalWidth, 400), min($originalHeight, 400), Imagick::FILTER_LANCZOS, 1, true); 
    $magickThumb->setImageCompression(Imagick::COMPRESSION_JPEG); 
    $magickThumb->setImageCompressionQuality(75); 
    $magickThumb->stripImage(); 
    $magickThumb->writeImage(str_replace('.jpg', '-thumb.jpg', $path)); 
    $magickThumb->destroy(); 
} 

Die Aufrufe von setImageCompression(), setImageCompressionQuality und stripImage() eine 53% ige Reduktion der kombinierten Dateigrößen ergaben . (Source)