简体   繁体   中英

imagecopyresampled() introduces artifacts in transparent PNG

I'm adding a transparent PNG watermark to an image. The watermark is high resolution so before overlaying it in the original image I resize it. This seems to introduce some artifacts in the watermark that I haven't found a way to avoid.

Original image: 在此处输入图片说明

Resized image (look at the horizontal line-like "dirt" between letters"): 在此处输入图片说明

Zoom of resized image (not transparent) to clarify what I mean by the "dirt" between the letters. The area between "s" and "t" I've cleaned with the select tool and delete (open in new tab to see full size where it's more clear): 在此处输入图片说明

Here's the code I'm using:

function resizeImage($image_filename, $out_filename, $width, $height){
  // Get image info
  $image_info = @getimagesize($image_filename);
  if ($image_info == false) return false;
  $org_width = $image_info[0];
  $org_height = $image_info[1];
  $image_type = $image_info[2];

  // Open image
  if ($image_type == IMAGETYPE_JPEG) $org_image = @imagecreatefromjpeg($image_filename);
  else if ($image_type == IMAGETYPE_GIF) $org_image = @imagecreatefromgif($image_filename);
  else if ($image_type == IMAGETYPE_PNG) $org_image = @imagecreatefrompng($image_filename);
  else return false;

  // Open stream for resized image
  $resized_image = @imagecreatetruecolor($width, $height);
  if ($resized_image == false) return false;

  // Handle transparency in PNGs
  if ($image_type == IMAGETYPE_PNG){
    $transparent = imagecolorallocatealpha($resized_image, 255, 255, 255, 127);
    imagefilledrectangle($resized_image, 0, 0, $width, $height, $transparent);
    imagealphablending($resized_image, false);
    imagesavealpha($resized_image, true);
  }

  // Resize
  $resize_result = @imagecopyresampled($resized_image, $org_image, 0, 0, 0, 0, $width, $height, $org_width, $org_height);

  // Free original image
  @imagedestroy($org_image);

  // Save
  if ($image_type == IMAGETYPE_JPEG) $save_result = imagejpeg($resized_image, $out_filename, 90); // 90 = compression
  else if ($image_type == IMAGETYPE_GIF) $save_result = imagegif($resized_image, $out_filename);
  else if ($image_type == IMAGETYPE_PNG) $save_result =  imagepng($resized_image, $out_filename, 0);

  // Free resized image
  if ($resize_result) @imagedestroy($resized_image);

  return ($resize_result && $save_result);
}

Any idea on what is causing the artifacts?

As I wrote in my comment in response to Søren Løvborgs answer it looks like it's simply [an issue with GD/ imagecopyresampled()][1] that can not be easily avoided. GD Quality Issue with Transparent PNGs has the same issue.

It's possible to use Søren Løvborgs suggested workaround, just keep in mind that it may introduce noticable quality reduction due to resizing the original image twice.

I suggest using a photo editor to resize the watermark before overlaying instead. This isn't as flexible, but it will keep the image quality and not add any noise.

This does not seem to occur on black backgrounds, so another hack would be to invert the image before resizing and do it again after

imagefilter($org_image, IMG_FILTER_NEGATE);
imagecopyresampled($resized_image, $org_image, 0, 0, 0, 0, $width, $height, $org_width, $org_height);
imagefilter($resized_image, IMG_FILTER_NEGATE);

Okay, first of all, here's your "dirty" test image with the RGB channels (above) and alpha channel (below) separated:

脏图像分成通道

I've replaced pure white ( #FFFFFF )/100% transparent with blue to highlight the speckles.

As you can see, GD adds speckles to both the RGB channels and the alpha channel.

Without having examined the GD code, I'm guessing that it's caused by a rounding error, causing some pixels to be off by 1 or 2.

I notice that you have an embedded color profile in the source PNG. Try removing that; the artefacts may be caused by GD doing color correction. (In Photoshop, you should use Save for Web.)

Otherwise, I can't suggest a way to solve this without fixing the GD code, however, I can suggest a dirty workaround, which might work:

Instead of resizing the watermark to the target image size, then overlaying it on the target image, you can resize the target image to the watermark size, overlay the watermark, drop the alpha channel and then resize to the old target image size. This way you avoid resizing any image while it has an alpha channel, which may avoid visible artifacts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM