如何在保留内部透明度的情况下裁剪PNG图像?

3

我需要裁剪一张图片,使得图片周围的透明区域被移除。然而,这张图片还有一些内部的透明区域,在运行以下函数时会被着色:

$image = imagecreatefrompng('path/to/image.png');
$trimmed = imagecropauto($image, IMG_CROP_DEFAULT); 

imagepng($trimmed);

上述代码可以裁剪外部透明区域,但内部透明区域也会受到影响。如何仅裁剪外部区域,类似于 Photoshop 在图像上的裁剪选项?

Expected result

Current result

原始图片可在https://yp-media.s3.amazonaws.com/Designs_Inners_and_Outers/Cushions/Inners/test_design.png找到。

(Stackoverflow 在将图片添加到问题时会进行转换/转换,这会导致透明度丢失。请参见下文:)

Original Image


如果您将图像保存为jpeg格式(在完成裁剪操作后),透明的点应该保持为白色。这种方法对您有效吗? - DarkShade
我已添加了显示预期结果和当前结果的图像。由于需要透明度进行进一步的图像处理,因此我需要保存为png格式。 - dc09
原始图像可在此处访问 - https://yp-media.s3.amazonaws.com/Designs_Inners_and_Outers/Cushions/Inners/test_design.png - dc09
1
我已经添加了原始图像,但在上传到stackoverflow时它会自动转换为jpg格式。 - dc09
1
刚刚查看了https://help.imgur.com/hc/en-us/articles/115000083326-What-files-can-I-upload-Is-there-a-size-limit-,它提到(匿名)超过1MB的图像会被压缩,很可能变成JPG文件。这个问题中的其他PNG文件较小,但原始的`test_design.png` PNG文件大小为1.5MB,因此会被压缩/更改。 - Progman
显示剩余9条评论
1个回答

0

简而言之: 为了减小文件大小,您的PNG图像已使用调色板(例如256种颜色)进行导出。透明度信息可能无法准确保留。这是因为基于调色板的颜色减少技术主要设计用于保留颜色信息而非透明度。为确保透明度得到保留,通常建议使用完全支持颜色的PNG图像(32位RGBA)进行导出,而不是使用有限颜色的调色板。

comparison

你可以尝试使用RGBA格式和下面找到的代码。

免责声明:我正在使用PHP 8.1.6和libPNG:1.6.37在macOS(Intel,Ventura 13.2.1(22D68))上,并且它可以正常工作。

前提条件:

我已确保您的帧首先在Affinity Photo中是100%透明的,我使用了填充工具手动删除了这些区域。我还使用默认设置导出了图像。如果我使用您的图像,最终会得到一个alpha值为229。

reconstruct

在浏览器中显示 / MIME 类型:

当使用 header('Content-Type: image/png') 渲染时,浏览器会为图像添加一些样式和类(浏览器中的默认样式)。我们只请求浏览器渲染孤立的图像,但浏览器会添加一些其他东西使其看起来“好看”-请参见屏幕截图中的 DOM。

例如,在 Firefox 中,您可以看到添加的“透明”类-它添加了一个背景(我已在第一个屏幕截图中禁用此功能)。如果我们将其更改为红色,则可以看到背景现在是红色的,因此框架也是红色的,因为它们是100%透明的。

preview-1 preview-2

那么我们该如何解决这个问题呢?这取决于你想要做什么。基本上,你可以渲染自己的HTML文档,而不是使用“mime-type: image/png”,这样就没有浏览器默认值了。
例如:
<?php

// ini_set("display_errors", 1);
// error_reporting(-1);

$image = imagecreatefrompng('./test-img.png');
imagealphablending($image, true); // Make sure the alpha channel is supported

$trimmed = imagecropauto($image, IMG_CROP_SIDES);

$generatedImagePath = './test-img-generated.png';

if ($trimmed !== false) {
  imagesavealpha($trimmed, true); // Make sure the alpha channel gets exported when you save it
  imagepng($trimmed, $generatedImagePath);
} else {
  echo "Failed to crop the image.";
}

imagedestroy($image);  // Frees memory prior to PHP 8.0
imagedestroy($trimmed); // ...

?>
<!DOCTYPE html>
<html>
<body style="background: transparent; background-image: url('./pattern.jpg')">
  <img src="<?php echo $generatedImagePath; ?>" style="width: 480px; aspect-ratio: 1;">
</body>
</html>

渲染:

prove


1
这仍然会产生与问题中显示的相同输出 - 边缘被修剪,但内部透明区域也被着色。此外,我理解点状图案只是一种表示方式,您的代码和我的初始代码的输出都导致内部透明区域被着色。 - dc09
@F.Müller,你能在你的答案中添加你正在使用的PHP版本和GD版本吗?我尝试了相同的代码,但仍然生成了OP所描述的有问题的图像。 - Progman
@Progman 正如我所说,它是基于浏览器样式的,除非您渲染一个包含它的HTML文档,否则无法更改它。我使用内置的GD库和PHP 8.1版本。 - F. Müller
@Progman,您说的“在我的主机上无法工作”是什么意思?出了什么问题?当您添加两行注释时,它是否会抛出错误?当您右键单击文件并下载它时,在图像编辑器中打开它时是否具有透明度?您尝试过我的更新版本还是旧版本的代码?我使用的是macOS,PHP 8.1.6,GD版本捆绑(2.1.0兼容),libJPEG版本=>9兼容,libPNG版本=>1.6.37。 - F. Müller
@F.Müller,它产生了与OP描述的相同有问题的图像。我测试了两个版本。 - Progman
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接