带有透明PNG的imagetruecolortopalette的PHP错误结果
Posted
技术标签:
【中文标题】带有透明PNG的imagetruecolortopalette的PHP错误结果【英文标题】:PHP wrong result for imagetruecolortopalette with PNG with transparency 【发布时间】:2020-01-19 09:01:20 【问题描述】:我正在尝试编写一个 php 脚本来调整 PNG 图像的大小,然后将其转换为 PNG-8 位模式。因此生成的文件的大小会更小,但不会有太多的质量损失。
调整大小效果很好,还保留了图像透明度:
问题是当我将图像转换为 8 位时:
imagetruecolortopalette($resizedImg, true, 255);
imagealphablending($resizedImg, false);
$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
imagesavealpha($resizedImg, true);
生成的图像是这样的,图像周围和内部都有一点透明度:
如果我设置 256 种颜色而不是 255 种颜色:
imagetruecolortopalette($resizedImg, true, 256);
图像将具有黑色背景:
此图像也会出现类似的结果(注意 255 色的情况是半透明度):
原文: 255色: 256色:
完整函数代码:
function resizePng($originalPath, $xImgNew='', $yImgNew='', $newPath='')
if(!trim($originalPath) || !$xyOriginalPath = getimagesize("$originalPath")) return false;
list($xImg, $yImg) = $xyOriginalPath;
if(!$originalImg = imagecreatefrompng($originalPath)) return false;
if(!$resizedImg = imagecreatetruecolor($xImgNew, $yImgNew)) return false;
// preserve alpha
imagealphablending($resizedImg, false);
$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
imagesavealpha($resizedImg, true);
// copy content from originalImg to resizedImg
if(!imagecopyresampled($resizedImg, $originalImg, 0, 0, 0, 0, $xImgNew, $yImgNew, $xImg, $yImg)) return false;
// PNG-8 bit conversion
imagetruecolortopalette($resizedImg, true, 255);
// preserve alpha
imagealphablending($resizedImg, false);
$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
imagesavealpha($resizedImg, true);
if(!imagepng($resizedImg, ($newPath) ?: null, 8)) return false;
return true;
我尝试了什么:
https://***.com/a/8144620/2342558
// PNG-8 bit conversion
imagetruecolortopalette($resizedImg, true, 255);
imagesavealpha($resizedImg, true);
imagecolortransparent($resizedImg, imagecolorat($resizedImg,0,0));
// preserve alpha
imagealphablending($resizedImg, false);
$transparent = imagecolorallocatealpha($resizedImg, 255, 255, 255, 127);
if(!imagefill($resizedImg, 0, 0, $transparent)) return false;
imagesavealpha($resizedImg, true);
if(!imagepng($resizedImg, ($newPath) ?: null, 8)) return false;
结果:
还有:https://***.com/a/55402802/2342558
没有任何改变。
还有:其他 SO 帖子和一些在网络上
同样不调整图像大小(删除imagecopyresampled
并调整变量名称)结果是相同的。
我怎样才能让它工作并理解这种奇怪行为的原因?
phpinfo()
中的一些信息:
7.0.33
GD
捆绑(2.1.0 兼容)
PNG Support
已启用
libPNG
1.5.13.
编辑:
在 GIMP v.2.8.22 中,我可以使用以下属性为 Web 保存图像:
PNG-8
256 colors palette
Dither: Floyd-Steinberg / Floyd-Steinberg 2 / positioned
它会生成与原始图像几乎相同的缩小图像。
pngquant、tinypng 和许多其他工具也做同样的工作,但 我需要用 PHP 来做。
编辑 2:
很遗憾,我无法使用 ImageMagick,因为我的代码在未安装的共享主机中。
编辑 3:
在phpinfo()
中导致imagemagick
模块未安装。
编辑 4:
让我对你的回复做一些测试,也许只有 PHP 的解决方案。
编辑 5:
这些是我对你的答案的尝试。
注意:我放置了一个底层网格以更好地显示 alpha。
Thomas Huijzer's answer:
企鹅身上有明显的色带,但鸭子还可以(尽管有时色调较暗)。
EPB's answer:
只有当图像只有像素已经完全透明时,它才能很好地工作(例如鸭子)。
Mark Setchell's answer:
它使所有带有 alpha 的像素完全透明,如果这个 alpha 非常低,请查看企鹅下方的阴影。鸭子边缘的一些像素也被转换为黑色像素或全透明像素。
【问题讨论】:
该图像中有明显超过 256 种颜色,更不用说具有部分透明像素的抗锯齿边缘,大大增加了所需的调色板条目数量。 PNG-8 将不能够包含此图像。 @NiettheDarkAbsol 为什么 pngquant 和 GIMP 可以将它们转换为 8 位? 因为他们使用算法来确定哪些颜色要保留,哪些颜色与另一种颜色足够接近,可以丢弃。放大并转换为 GIMP 中的索引,您会看到清晰的条带。 如果 gimp 能如你所愿,使用它的batch mode 转换你的 1000 多个文件,你就完成了。 两件事...首先,您可以使用system()
从 PHP 中"shell out" 到 GIMP。其次,您的半透明 255 鸭子是由左上角的泛光填充引起的,但鸭子停止流动,因为它接触边缘并且泛光仅填充与左上角匹配的白色像素而不是鸭色像素。解决方案是在图像周围添加一个 1 像素宽的边框,其颜色与左上角像素的颜色相同,这样洪水填充就可以一直流动。
【参考方案1】:
您可以在 ImageMagick 中轻松做到这一点,它在 Linux 上分发,可用于 Windows 和 Mac OSX。除了命令行之外,还有许多 API。以下是如何在 ImageMagick 命令行中执行此操作。
输入:
convert image.png PNG8:result1.png
PNG8:表示 256 色和二进制透明度。这意味着要么完全透明,要么不透明。这会导致边缘周围出现混叠(阶梯状)。如果您愿意设置背景颜色来代替透明度,那么您可以在结果中保持平滑(抗锯齿)轮廓。所以对于白色背景。
convert image.png -background white -flatten PNG8:result2.png
ImageMagick 由 PHP Imagick 运行。所以你应该可以用 PHP Imagick 做到这一点。或者您可以从 PHP exec() 调用 ImageMagick 命令行。
【讨论】:
感谢您的回复,我可以使用 ImageMagick 应用抖动“Floyd-Steinberg 2”吗? Pngquant 可以将每个像素减少到 8 位保留 alpha,同时还具有罕见的明显条带 见imagemagick.org/Usage/quantize/#dither。convert image.png -dither FloydSteinberg -colors XX PNG8:dither_floyd.png
,其中 XX imagemagick.org/Usage/formats/#png_non-im
不幸的是,我无法使用 ImageMagick,因为我的代码在未安装的共享主机中。无论如何感谢您的努力。我可以在不安装服务器的情况下使用 ImageMagick 吗?
如果在 Linux 服务器上,请咨询托管服务提供商。 ImageMagick 通常已随 Linux 一起安装。
@user2342558 实际上有一些共享主机确实提供 imagemagick 作为 PHP 扩展,请检查您的 phpinfo()
以查看它是否已启用。如果没有,您可以随时询问技术支持是否已配置。【参考方案2】:
我不认为这是奇怪的行为。
PHP 文档没有说明这一点,但我猜imagefill()
在大多数其他应用程序中的工作方式与大多数其他应用程序一样:通过使用与填充开始处的像素相同的颜色填充连接的像素(0, 0)
。
因为您首先将托盘设置为 255 像素(或 256),所以您将所有黑暗区域转换为黑色并失去所有透明度。 然后,当您从左上角开始填充填充时,所有连接的像素(也在企鹅和鸭子内部)将变得透明。
我认为没有 ImageMagick 的唯一方法是遍历调整大小图像的所有像素并手动将像素颜色设置为有限的托盘。
前段时间我写了一个小脚本来减少 PNG 的颜色,同时保留完整的 alpha 信息 (1)。 这将减少 PNG 文件使用的托盘,从而减少文件大小。如果生成的 PNG 仍然超过 8 位,这并不重要。无论如何,一个小托盘会减小文件大小。
(1)https://bitbucket.org/thuijzer/pngreduce/
编辑:我刚刚使用您调整大小的 PNG(具有透明度)作为脚本的输入,并将其从 12 kB 转换为仅使用 32 种颜色的 7 kB 文件:
Reduced to 62.28% of original, 12.1kB to 7.54kB
编辑 2:我更新了我的脚本并添加了可选的 Floyd–Steinberg 抖动。 每个通道有 16 种颜色的结果:
Reduced to 66.94% of original, 12.1kB to 8.1kB
请注意,抖动也会影响文件大小,因为当相邻像素具有不同颜色时,压缩 PNG 会“更难”。
【讨论】:
谢谢,我试过你的代码。它减小了保留 alpha 的文件大小,但如果我使用大图像(例如 300x300 像素),它会创建可见的大带状区域(例如在腹部和脚下)。您的脚本没有使用imagetruecolortopalette
方法,那么如何应用抖动过滤器?
好吧,我想最好先调整图像大小,然后再更换托盘。我的脚本不会抖动,但imagetruecolorpalette
不会。但也许你可以从我的剧本中找到一些灵感。如果没有 ImageMagick 之类的东西,您的选择就会受到限制,并且应该手动完成诸如抖动之类的事情。
是的,条纹总是减少颜色的结果。例如,我更新了我的脚本并添加了抖动。
我看到了抖动的结果,但它导致头部比原来的像素化更多。你能只在有色带的地方应用抖动吗?
好吧,我不认为这对于 Floyd-Steinberg 方法是可能的。但也许你可以实现更好的抖动算法。但是减少颜色(量化)总是会导致条带化。因此,您必须使其视觉上更令人愉悦的唯一选择是抖动。有很多方法可以做到这一点,因此您必须选择最适合您的方法。但我认为首先调整图像大小然后减少颜色会得到最好的结果。【参考方案3】:
更新答案
我有更多的时间来编写完整的代码来回答你 - 我已经大大简化了你所拥有的东西,它似乎可以满足我认为你现在想要的!
#!/usr/bin/php -f
<?php
function extractAlpha($im)
// Ensure input image is truecolour, not palette
if(!imageistruecolor($im))
printf("DEBUG: Converting input image to truecolour\n");
imagepalettetotruecolor($im);
// Get width and height
$w = imagesx($im);
$h = imagesy($im);
// Allocate a new greyscale, palette (non-alpha!) image to hold the alpha layer, since it only needs to hold alpha values 0..127
$alpha = imagecreate($w,$h);
// Create a palette for 0..127
for($i=0;$i<128;$i++)
imagecolorallocate($alpha,$i,$i,$i);
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Get current color
$rgba = imagecolorat($im, $x, $y);
// $r = ($rgba >> 16) & 0xff;
// $g = ($rgba >> 8) & 0xff;
// $b = $rgba & 0xf;
$a = ($rgba & 0x7F000000) >> 24;
imagesetpixel($alpha,$x,$y,$a);
//printf("DEBUG: alpha[%d,%d] = %d\n",$x,$y,$a);
return $alpha;
function applyAlpha($im,$alpha)
// If output image is truecolour
// iterate over pixels getting current color and just replacing alpha component
// else (palettised)
// // find a transparent colour in the palette
// if not successful
// allocate transparent colour in palette
// iterate over pixels replacing transparent ones with allocated transparent colour
// Get width and height
$w = imagesx($im);
$h = imagesy($im);
// Ensure all the lovely new alpha we create will be saved when written to PNG
imagealphablending($im, false);
imagesavealpha($im, true);
// If output image is truecolour, we can set alpha 0..127
if(imageistruecolor($im))
printf("DEBUG: Target image is truecolour\n");
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Get current color
$rgba = imagecolorat($im, $x, $y);
// Get alpha
$a = imagecolorat($alpha,$x,$y);
// printf("DEBUG: Setting alpha[%d,%d] = %d\n",$x,$y,$a);
$new = ($rgba & 0xffffff) | ($a<<24);
imagesetpixel($im,$x,$y,$new);
else
printf("DEBUG: Target image is palettised\n");
// Must be palette image, get index of a fully transparent color
$transp = -1;
for($index=0;$index<imagecolorstotal($im);$index++)
$c = imagecolorsforindex($im,$index);
if($c["alpha"]==127)
$transp = $index;
printf("DEBUG: Found a transparent colour at index %d\n",$index);
// If we didn't find a transparent colour in the palette, allocate one
$transp = imagecolorallocatealpha($im,0,0,0,127);
// Scan image replacing all pixels that are transparent in the original copied alpha channel with the index of a transparent pixel in current palette
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Essentially we are thresholding the alpha here. If it was more than 50% transparent in original it will become fully trasnparent now
$grey = imagecolorat($alpha,$x,$y) & 0xFF;
if($grey>64)
//printf("DEBUG: Replacing transparency at %d,%d\n",$x,$y);
imagesetpixel($im,$x,$y,$transp);
return $im;
// Set new width and height
$wNew = 300;
$hNew = 400;
// Open input image and get dimensions
$src = imagecreatefrompng('tux.png');
$w = imagesx($src);
$h = imagesy($src);
// Extract the alpha and save as greyscale for inspection
$alpha = extractAlpha($src);
// Resize alpha to match resized source image
$alpha = imagescale($alpha,$wNew,$hNew,IMG_NEAREST_NEIGHBOUR);
imagepng($alpha,'alpha.png');
// Resize original image
$resizedImg = imagecreatetruecolor($wNew, $hNew);
imagecopyresampled($resizedImg, $src, 0, 0, 0, 0, $wNew, $hNew, $w, $h);
// Palettise
imagetruecolortopalette($resizedImg, true, 250);
// Apply extracted alpha and save
$res = applyAlpha($resizedImg,$alpha);
imagepng($res,'result.png');
?>
结果
提取的 alpha 通道:
原答案
我创建了一个 PHP 函数来从图像中提取 Alpha 通道,然后将该 Alpha 通道应用到另一个图像。
如果您将复制的 Alpha 通道应用于真彩色图像,它将允许具有 7 位分辨率的平滑 Alpha,即高达 127。如果您将复制的 Alpha 应用于调色图像,它将阈值为 50% (您可以更改它)以便输出图像具有二进制(开/关)alpha。
所以,我从这张图片中提取了 alpha - 你可以希望看到中间有一个 alpha 渐变/渐变。
并将复制的 alpha 应用于此图像。
第二张图片是真彩色的,alpha 是这样的:
在第二张图像被调色的地方,alpha 是这样的:
代码应该是不言自明的。取消注释 printf()
包含 DEBUG:
的语句以获得大量输出:
#!/usr/bin/php -f
<?php
// Make test images with ImageMagick as follows:
// convert -size 200x100 xc:magenta \( -size 80x180 gradient: -rotate 90 -bordercolor white -border 10 \) -compose copyopacity -composite png32:image1.png
// convert -size 200x100 xc:blue image2.png # Makes palettised image
// or
// convert -size 200x100 xc:blue PNG24:image2.png # Makes truecolour image
function extractAlpha($im)
// Ensure input image is truecolour, not palette
if(!imageistruecolor($im))
printf("DEBUG: Converting input image to truecolour\n");
imagepalettetotruecolor($im);
// Get width and height
$w = imagesx($im);
$h = imagesy($im);
// Allocate a new greyscale, palette (non-alpha!) image to hold the alpha layer, since it only needs to hold alpha values 0..127
$alpha = imagecreate($w,$h);
// Create a palette for 0..127
for($i=0;$i<128;$i++)
imagecolorallocate($alpha,$i,$i,$i);
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Get current color
$rgba = imagecolorat($im, $x, $y);
// $r = ($rgba >> 16) & 0xff;
// $g = ($rgba >> 8) & 0xff;
// $b = $rgba & 0xf;
$a = ($rgba & 0x7F000000) >> 24;
imagesetpixel($alpha,$x,$y,$a);
//printf("DEBUG: alpha[%d,%d] = %d\n",$x,$y,$a);
return $alpha;
function applyAlpha($im,$alpha)
// If image is truecolour
// iterate over pixels getting current color and just replacing alpha component
// else (palettised)
// allocate a transparent black in the palette
// if not successful
// find any other transparent colour in palette
// iterate over pixels replacing transparent ones with allocated transparent colour
// We expect the alpha image to be non-truecolour, i.e. palette-based - check!
if(imageistruecolor($alpha))
printf("ERROR: Alpha image is truecolour, not palette-based as expected\n");
// Get width and height
$w = imagesx($im);
$h = imagesy($im);
// Ensure all the lovely new alpha we create will be saved when written to PNG
imagealphablending($im, false);
imagesavealpha($im, true);
if(imageistruecolor($im))
printf("DEBUG: Target image is truecolour\n");
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Get current color
$rgba = imagecolorat($im, $x, $y);
// Get alpha
$a = imagecolorat($alpha,$x,$y);
// printf("DEBUG: Setting alpha[%d,%d] = %d\n",$x,$y,$a);
$new = ($rgba & 0xffffff) | ($a<<24);
imagesetpixel($im,$x,$y,$new);
else
printf("DEBUG: Target image is palettised\n");
// Must be palette image, get index of a fully transparent color
$trans = imagecolorallocatealpha($im,0,0,0,127);
if($trans===FALSE)
printf("ERROR: Failed to allocate a transparent colour in palette. Either pass image with fewer colours, or look through palette and re-use some other index with alpha=127\n");
else
// Scan image replacing all pixels that are transparent in the original copied alpha channel with the index of a transparent pixel in current palette
for ($x = 0; $x < $w; $x++)
for ($y = 0; $y < $h; $y++)
// Essentially we are thresholding the alpha here. If it was more than 50% transparent in original it will become fully trasnparent now
if (imagecolorat($alpha,$x,$y) > 64)
imagesetpixel($im,$x,$y,$trans);
//printf("DEBUG: Setting alpha[%d,%d]=%d\n",$x,$y,$trans);
return $im;
// Open images to copy alpha from and to
$src = imagecreatefrompng('image1.png');
$dst = imagecreatefrompng('image2.png');
// Extract the alpha and save as greyscale for inspection
$alpha = extractAlpha($src);
imagepng($alpha,'alpha.png');
// Apply extracted alpha to second image and save
$res = applyAlpha($dst,$alpha);
imagepng($res,'result.png');
?>
这是提取的 alpha 层,只是为了好玩。请注意,它实际上是代表 Alpha 通道的灰度图像 - 它本身没有任何 Alpha 分量。
关键字:PHP、gd、图像、图像处理、alpha、alpha 层、提取 alpha、复制 alpha、应用 alpha、替换 alpha。
【讨论】:
我试过你的脚本,但它不起作用。当 $dst 由truecolor your script work fine, but id $dts is made of
imagetruecolortopalette` 组成时,整个 img 将是透明的。我错过了什么吗?您能否使用我的代码发布企鹅的示例结果?谢谢
我有点迷失在你问题中所有不起作用的代码示例中,我看不出你是如何尝试我的代码的。我只能说,如果您已经为输出托盘化图像中的任何透明颜色分配了索引,只需将我的代码中$trans = imagecolorallocatealpha($im,0,0,0,127);
的行更改为$trans = <INDEX OF YOUR TRANSPARENT COLOUR>
,这应该就是您所需要的。
我有更多时间为您做一个完整的答案 - 请再看看。
您好,感谢您的帮助。我尝试了您更新的答案,但是,在您的图像预览中,您的脚本使所有带有 alpha 的像素完全透明,如果这个 alpha 非常低,请查看企鹅下方的阴影。鸭子边缘的一些像素也被转换为黑色像素或全透明像素。所以这不是我需要的。只有当图像周围只有 alpha 并且这个 alpha 是总的时,它才起作用,例如鸭子。
如果你把我代码第87行的阈值改成70
,就会在企鹅下得到更大的透明阴影。它的范围为 0..127,我随意将其设置为 64,因为这是一半 - 您可以将其更改为您想要的任何值。【参考方案4】:
到目前为止,除了在 PHP/GD 中重新实现 pngquant,我还没有找到一种方法完全,我认为这是可能的。 (也就是说,还要量化 alpha 通道。我也无法让 GD 以预期的方式可靠地抖动 alpha。)
但是,以下可能是一个有用的中间立场。 (对于您或其他坚持使用 GD 的人。)resize 函数接受无光泽颜色作为背景,然后将透明(或非常接近透明)的像素设置为
透明索引。有一个阈值来设置要考虑多少 alpha。 ($alphaThreshold
的值越低,所提供的哑光颜色就越少,但会逐渐移除更多原始的 alpha 透明部分。)
function resizePng2($originalPath, $xImgNew='', $yImgNew='', $newPath='', $backgroundMatte = [255,255,255], $alphaThreshold = 120)
if(!trim($originalPath) || !$xyOriginalPath = getimagesize("$originalPath")) return false;
list($xImg, $yImg) = $xyOriginalPath;
if(!$originalImg = imagecreatefrompng($originalPath)) return false;
if(!$resizedImg = imagecreatetruecolor($xImgNew, $yImgNew)) return false;
if(!$refResizedImg = imagecreatetruecolor($xImgNew, $yImgNew)) return false;
//Fill our resize target with the matte color.
imagealphablending($resizedImg, true);
$matte = imagecolorallocatealpha($resizedImg, $backgroundMatte[0], $backgroundMatte[1], $backgroundMatte[2], 0);
if(!imagefill($resizedImg, 0, 0, $matte)) return false;
imagesavealpha($resizedImg, true);
// copy content from originalImg to resizedImg
if(!imagecopyresampled($resizedImg, $originalImg, 0, 0, 0, 0, $xImgNew, $yImgNew, $xImg, $yImg)) return false;
//Copy to our reference.
$refTransparent = imagecolorallocatealpha($refResizedImg, 0, 0, 0, 127);
if(!imagefill($refResizedImg, 0, 0, $refTransparent)) return false;
if(!imagecopyresampled($refResizedImg, $originalImg, 0, 0, 0, 0, $xImgNew, $yImgNew, $xImg, $yImg)) return false;
// PNG-8 bit conversion (Not the greatest, but it does have basic dithering)
imagetruecolortopalette($resizedImg, true, 255);
//Allocate our transparent index.
imagealphablending($resizedImg, true);
$transparent = imagecolorallocatealpha($resizedImg, 0,0,0,127);
//Set the pixels in the output image to transparent where they were transparent
//(or nearly so) in our original image. Set $alphaThreshold lower to adjust affect.
for($x = 0; $x < $xImgNew; $x++)
for($y = 0; $y < $yImgNew; $y++)
$alpha = (imagecolorat($refResizedImg, $x, $y) >> 24);
if($alpha >= $alphaThreshold)
imagesetpixel($resizedImg, $x, $y, $transparent);
if(!imagepng($resizedImg, ($newPath) ?: null, 8)) return false;
return true;
所以这里是一个白色背景和绿色背景的例子。左边的企鹅有一个白色的哑光。右边的企鹅有一个绿色的哑光。
这是我的测试企鹅的输出:
附录:如果你想要部分 alpha 透明的像素,但只有 GD 怎么办。您需要自己处理量化/抖动。因此,举个例子:我通过构建现有的抖动库并将其与我自己的基本量化器配对来尝试它。 (我不会在生产中使用它。在撰写本文时,代码有点混乱且未经测试,而且我还没有改进抖动部分来处理更大的调色板,所以它非常慢。[编辑:我添加了一个图层缓存因此不再是这种情况,它现在可用于大多数用例。])
https://github.com/b65sol/gd-indexed-color-converter
// create an image
$image = imagecreatefrompng('76457185_p0.png');
// create a gd indexed color converter
$converter = new GDIndexedColorConverter();
// the color palette produced by the quantizer phase.
// Could manually add additional colors here.
$palette = $converter->quantize($image, 128, 5);
// THIS IS VERY SLOW! Need to speed up closestColor matching.
// Perhaps with a quadtree.
// convert the image to indexed color mode
$new_image = $converter->convertToIndexedColor($image, $palette, 0.2);
// save the new image
imagepng($new_image, 'example_indexed_color_alpha.png', 8);
这是一个在索引图像中保留 alpha 透明度的示例:
【讨论】:
感谢您的回复。您提供了两个带有哑光背景的示例,您的函数是否适用于透明背景? 完全透明的部分是。这就是折衷的解决方案,就像我们在早期为保存网络 gif 所做的那样。我将添加白色哑光企鹅作为测试。 我确实提到这是一个折衷的解决方案。 XD 问题是 GD 的量化器不够健壮。具体来说,如果您想单独保持源图像 GD 的部分透明度,则不会这样做。一种选择可能是抖动 Alpha 通道。 (阴影会变成抖动的黑点,边缘会很锐利/块状。)另一个是创建自己的支持部分不透明度的量化器,并自己进行抖动,因为我认为 GD 会支持输出这样的调色板,但工作量很大。 :) 是的。我同意你的看法。 PHP 的 GD 需要一些改进。 我添加了一个编辑。不幸的是,我现在没有时间让它更快/更好,但您可以将其用作您自己解决方案的跳板。【参考方案5】:正如您在https://www.php.net/manual/en/function.imagetruecolortopalette.php 中看到的那样:
这并不像预期的那样有效。通常最好 而是简单地生成一个真彩色输出图像,这保证了 最高的输出质量。
您可以使用 ImageMagick:https://www.php.net/manual/en/imagick.affinetransformimage.php
【讨论】:
引用很明显。 8 位的精度和质量不如 24 位。然后在 Imagick 的页面中有一条评论说“这个方法似乎没有被正确实现 - 它对图像没有任何影响。” 你有没有关于 ImageMagick 的链接来解释如何做我正在寻找的东西? @user2342558 convert image.png -background white -flatten PNG8:result2.png 如 fmw42 所说。为什么要低点!!!以上是关于带有透明PNG的imagetruecolortopalette的PHP错误结果的主要内容,如果未能解决你的问题,请参考以下文章
带有透明PNG的imagetruecolortopalette的PHP错误结果