使用PHP的GDlib imagecopyresampled时可以保留PNG图像透明度吗?

Posted

技术标签:

【中文标题】使用PHP的GDlib imagecopyresampled时可以保留PNG图像透明度吗?【英文标题】:Can PNG image transparency be preserved when using PHP's GDlib imagecopyresampled? 【发布时间】:2010-09-07 03:07:27 【问题描述】:

以下 php 代码 sn-p 使用 GD 将浏览器上传的 PNG 大小调整为 128x128。它工作得很好,除了在我的例子中原始图像中的透明区域被替换为纯黑色。

尽管设置了imagesavealpha,但有些地方不太对劲。

在重新采样的图像中保持透明度的最佳方法是什么?

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );    
imagesavealpha( $targetImage, true );

$targetImage = imagecreatetruecolor( 128, 128 );
imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

【问题讨论】:

【参考方案1】:
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

为我做的。谢谢 ceejayoz。

注意,目标图像需要 alpha 设置,而不是源图像。

编辑: 完整的替换代码。另请参阅下面的答案及其 cmets。这不能保证在任何方面都是完美的,但确实满足了我当时的需求。

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile ); 

$targetImage = imagecreatetruecolor( 128, 128 );   
imagealphablending( $targetImage, false );
imagesavealpha( $targetImage, true );

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

【讨论】:

FIY,这需要在目标图像创建之后。在这种情况下,它将在 imagecreatetruecolor 之后。 这个答案不仅正确且有用,而且特别有用,因为imagecreatefrompng() 的 PHP 文档上的第一条评论(在撰写本文时)建议应该设置 imagealphablending @987654326 @,这显然是错误的。非常感谢。 这个解决方案,在我的情况下,只有当 PNG 有一个“常规”透明区域(如周围的透明区域)时,GD 才能正常工作,如果它有一个复杂的区域,如图像的内部部分具有透明度,它总是失败并放置黑色背景,例如此图像失败:seomofo.com/downloads/new-google-logo-knockoff.png。有人可以试试这个并确认吗? 似乎不适用于某些透明的 png 文件。我尝试从 jpg 创建图像并在其中复制透明 png。正如 aesede 指出的那样,结果是一个黑色方块。 我有一个非常复杂的脚本,它使用多次 imagecopyresampled 进行裁剪、旋转和调整大小。一旦我在每个步骤之前添加了简单线 imagealphablending( $targetImage, false );图像保存阿尔法($targetImage,真); PNG 以它们的方式保持完全透明。工作完美,很容易。【参考方案2】:

你为什么把事情搞得这么复杂?以下是我使用的,到目前为止它已经为我完成了这项工作。

$im = ImageCreateFromPNG($source);
$new_im = imagecreatetruecolor($new_size[0],$new_size[1]);
imagecolortransparent($new_im, imagecolorallocate($new_im, 0, 0, 0));
imagecopyresampled($new_im,$im,0,0,0,0,$new_size[0],$new_size[1],$size[0],$size[1]);

【讨论】:

没用,我仍然得到这张图片的黑色背景:seomofo.com/downloads/new-google-logo-knockoff.png 正在尝试两种解决方案:您的解决方案和上面的解决方案,获得超过 150 票。您的解决方案非常适合 GIF。上述方法最适用于 PNG 文件,而您的解决方案正在失去抗锯齿功能,您可以在创建缩略图时看到最佳效果(看起来像块状、像素化)。【参考方案3】:

我相信这应该可以解决问题:

$srcImage = imagecreatefrompng($uploadTempFile);
imagealphablending($srcImage, false);
imagesavealpha($srcImage, true);

编辑: PHP 文档中有人声称 imagealphablending 应该是真的,而不是假的。 YMMV。

【讨论】:

imagealphablending 与 true 或 false 一起使用,我总是得到黑色背景。 PHP7 - 为我工作 玩过它 (PHP 7.x): PNG: imagealphablending($targetImage, false); // 如果在 PNG 上为 true:黑色背景 GIF: imagealphablending($targetImage, true); // 如果 GIF 为 false:黑色背景【参考方案4】:

可能对某些人有所帮助的补充:

可以在构建图像时切换 imagealphablending。我需要这个的具体情况,我想在透明背景上组合一些半透明的 PNG。

首先将 imagealphablending 设置为 false 并用透明颜色填充新创建的真彩色图像。如果 imagealphablending 为真,则不会发生任何事情,因为透明填充将与黑色默认背景合并并产生黑色。

然后您将 imagealphablending 切换为 true 并将一些 PNG 图像添加到画布,使一些背景可见(即不填满整个图像)。

结果是一个具有透明背景的图像和几个组合的 PNG 图像。

【讨论】:

【参考方案5】:

我已经制作了一个功能,用于使用copyimageresample 调整 JPEG/GIF/PNG 等图像的大小,并且 PNG 图像仍然保持透明度:

$myfile=$_FILES["youimage"];

function ismyimage($myfile) 
    if((($myfile["type"] == "image/gif") || ($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") || ($myfile["type"] == "image/png")) && ($myfile["size"] <= 2097152 /*2mb*/) ) return true; 
    else return false;


function upload_file($myfile)          
    if(ismyimage($myfile)) 
        $information=getimagesize($myfile["tmp_name"]);
        $mywidth=$information[0];
        $myheight=$information[1];

        $newwidth=$mywidth;
        $newheight=$myheight;
        while(($newwidth > 600) || ($newheight > 400 )) 
            $newwidth = $newwidth-ceil($newwidth/100);
            $newheight = $newheight-ceil($newheight/100);
         

        $files=$myfile["name"];

        if($myfile["type"] == "image/gif") 
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromgif($myfile["tmp_name"]);
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagegif($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con)
                return true;
             else 
                return false;
            
         else if(($myfile["type"] == "image/jpg") || ($myfile["type"] == "image/jpeg") ) 
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefromjpeg($myfile["tmp_name"]); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagejpeg($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con)   
                return true;
             else 
                return false;
            
         else if($myfile["type"] == "image/png") 
            $tmp=imagecreatetruecolor($newwidth,$newheight);
            $src=imagecreatefrompng($myfile["tmp_name"]);
            imagealphablending($tmp, false);
            imagesavealpha($tmp,true);
            $transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
            imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent); 
            imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $mywidth, $myheight);
            $con=imagepng($tmp, $files);
            imagedestroy($tmp);
            imagedestroy($src);
            if($con) 
                return true;
             else 
                return false;
            
           
     else
          return false;

【讨论】:

通读所有代码以找出为什么在问题中的代码中保留透明度的原因是相当繁重的。 我跳过了这两行,它仍然有效:$transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent); 这个答案看起来很像***.com/a/279310/470749。【参考方案6】:

我想这可能会成功:

$uploadTempFile = $myField[ 'tmp_name' ]
list( $uploadWidth, $uploadHeight, $uploadType ) 
  = getimagesize( $uploadTempFile );

$srcImage = imagecreatefrompng( $uploadTempFile );

$targetImage = imagecreatetruecolor( 128, 128 );

$transparent = imagecolorallocate($targetImage,0,255,0);
imagecolortransparent($targetImage,$transparent);
imagefilledrectangle($targetImage,0,0,127,127,$transparent);

imagecopyresampled( $targetImage, $srcImage, 
                    0, 0, 
                    0, 0, 
                    128, 128, 
                    $uploadWidth, $uploadHeight );

imagepng(  $targetImage, 'out.png', 9 );

缺点是每 100% 的绿色像素都会去除图像。无论如何,希望它有所帮助:)

【讨论】:

如果您设置为几乎没有图像会使用的极其难看的颜色,它会很有帮助。 接受的答案对我不起作用。不过,将此答案与imagecreate(...) 一起使用是可行的。您创建一个图像,其中填充了您分配的第一种颜色。然后将该颜色设置为透明。如果目标图像的 alphablending 设置为 true,则两个图像将被合并并且透明度正常工作。【参考方案7】:

重新分级保留透明度,然后像其他帖子中所述,是的,必须将 imagesavealpha() 设置为 true,使用 alpha 标志 imagealphablending() 必须设置为 false,否则它不起作用。

我还在您的代码中发现了两个小问题:

    您无需调用getimagesize() 即可获取imagecopyresmapled() 的宽度/高度 $uploadWidth$uploadHeight 应该是-1 的值,因为坐标从0 而不是1 开始,所以它会将它们复制到一个空像素中。将其替换为:imagesx($targetImage) - 1imagesy($targetImage) - 1,相对而言应该这样做:)

【讨论】:

【参考方案8】:

这是我的总测试代码。它对我有用

$imageFileType = pathinfo($_FILES["image"]["name"], PATHINFO_EXTENSION);
$filename = 'test.' . $imageFileType;
move_uploaded_file($_FILES["image"]["tmp_name"], $filename);

$source_image = imagecreatefromjpeg($filename);

$source_imagex = imagesx($source_image);
$source_imagey = imagesy($source_image);

$dest_imagex = 400;
$dest_imagey = 600;
$dest_image = imagecreatetruecolor($dest_imagex, $dest_imagey);

imagecopyresampled($dest_image, $source_image, 0, 0, 0, 0, $dest_imagex, $dest_imagey, $source_imagex, $source_imagey);

imagesavealpha($dest_image, true);
$trans_colour = imagecolorallocatealpha($dest_image, 0, 0, 0, 127);
imagefill($dest_image, 0, 0, $trans_colour);

imagepng($dest_image,"test1.png",1);

【讨论】:

【参考方案9】:

注意源图像的widthheight 值传递给imagecopyresampled 函数。如果它们大于实际源图像大小,则图像区域的其余部分将用黑色填充。

【讨论】:

【参考方案10】:

我结合了 ceejayoz 和 Cheekysoft 的答案,这对我来说是最好的结果。没有 imagealphablending() 和 imagesavealpha() 图像不清晰:

$img3 = imagecreatetruecolor(128, 128);
imagecolortransparent($img3, imagecolorallocate($img3, 0, 0, 0));
imagealphablending( $img3, false );
imagesavealpha( $img3, true );
imagecopyresampled($img3, $srcImage, 0, 0, 0, 0, 128, 128, $uploadWidth, $uploadHeight);
imagepng($img3, 'filename.png', 9);

【讨论】:

以上是关于使用PHP的GDlib imagecopyresampled时可以保留PNG图像透明度吗?的主要内容,如果未能解决你的问题,请参考以下文章

CentOS:在PHP安装中启用GD支持

使用 gd 将两个 images.png 合并到 output.png

图像的 RGB 颜色平均值

如何使用 Strawberry Perl 安装 GD 库

如何使用 dlib 跟踪 ROI 内的对象?

使用 dlib 进行地标预测的准确性