【问题标题】:Php - replace base color of transparent png imagephp - 替换透明png图像的基色
【发布时间】:2013-07-18 00:20:13
【问题描述】:

我进行了很多搜索,但只找到了几个解决方案(在 google 和 stackoverflow 上,所以请不要将此标记为重复,除非确实存在重复的问题),但问题是硬性问题。是否有任何适当的方法来改变基色,比如具有透明背景的黑色形状 png 图像,但要保留软边缘?

这是一个示例图像:

我希望它看起来像这样:

但我找到的解决方案给了我这个:

由于我将在我的本地主机上使用它,仅供个人使用,任何可以帮助实现此目的的 php 库都将受到赞赏。

更新:

这是给我第三张图片的函数:

function LoadPNG($imgname)
{
    $im = imagecreatefrompng ($imgname);
    imagetruecolortopalette($im,false, 255);
    $index = imagecolorclosest ( $im,  0,0,0 ); // GET BLACK COLOR
    imagecolorset($im,$index,0,150,255); // SET COLOR TO BLUE
    $name = basename($imgname);
    imagepng($im, getcwd()."/tmp/$name" ); // save image as png
    imagedestroy($im);
}
$dir = getcwd()."/img/";
$images = glob($dir."/*.png",GLOB_BRACE);
foreach($images as $image) {
    LoadPNG($image);
}

最初,此功能是 GIF 图像(255 色调色板)的解决方案,所以我想这就是为什么会有硬边。我正在寻找一种解决方案(改进此脚本)来保留 PNG 图像的透明度和柔和边缘。

编辑 2:

我在这里找到了一种使用 html5 画布和 javascript 的有趣方法: http://users7.jabry.com/overlord/mug.html

如果可能的话,也许有人会知道如何将其翻译成 PHP。

新解决方案

在答案中

【问题讨论】:

  • 你能发布一些代码来解决导致第三张图片的问题吗?
  • @Maximus2012 是的,请给我一点时间,我必须找到它,因为在此期间我尝试了许多其他方法,但均未成功。
  • @Maximus2012 更新了我的问题。
  • 我认为由于图像尺寸非常小,像素中可能存在一些重叠,因此第三张图像是您能得到的最接近的图像。不过我在这里可能是错的,我也想为此寻找更好的解决方案。
  • 非常相关且措辞恰当的问题,为我节省了大量时间。 @SteAp 的出色回答

标签: php image colors png transparency


【解决方案1】:

此代码没有举例说明问题,而是像这样转换颜色:

使用图像的 ALPHA 通道来确定颜色。对于其他结果,只需使用imagecolorallocatealpha()

function colorizeBasedOnAplhaChannnel( $file, $targetR, $targetG, $targetB, $targetName ) {

    $im_src = imagecreatefrompng( $file );

    $width = imagesx($im_src);
    $height = imagesy($im_src);

    $im_dst = imagecreatefrompng( $file );

    // Note this:
    // Let's reduce the number of colors in the image to ONE
    imagefilledrectangle( $im_dst, 0, 0, $width, $height, 0xFFFFFF );

    for( $x=0; $x<$width; $x++ ) {
        for( $y=0; $y<$height; $y++ ) {

            $alpha = ( imagecolorat( $im_src, $x, $y ) >> 24 & 0xFF );

            $col = imagecolorallocatealpha( $im_dst,
                $targetR - (int) ( 1.0 / 255.0  * $alpha * (double) $targetR ),
                $targetG - (int) ( 1.0 / 255.0  * $alpha * (double) $targetG ),
                $targetB - (int) ( 1.0 / 255.0  * $alpha * (double) $targetB ),
                $alpha
                );

            if ( false === $col ) {
                die( 'sorry, out of colors...' );
            }

            imagesetpixel( $im_dst, $x, $y, $col );

        }

    }

    imagepng( $im_dst, $targetName);
    imagedestroy($im_dst);

}

unlink( dirname ( __FILE__ ) . '/newleaf.png' );
unlink( dirname ( __FILE__ ) . '/newleaf1.png' );
unlink( dirname ( __FILE__ ) . '/newleaf2.png' );

$img = dirname ( __FILE__ ) . '/leaf.png';
colorizeBasedOnAplhaChannnel( $img, 0, 0, 0xFF, 'newleaf1.png' );
colorizeBasedOnAplhaChannnel( $img, 0xFF, 0, 0xFF, 'newleaf2.png' );
?>

Original
<img src="leaf.png">
<br />
<img src="newleaf1.png">
<br />
<img src="newleaf2.png">

【讨论】:

  • 边缘较暗,如果我是正确的(小图像,所以我看不清楚),但这是迄今为止最好的基于 php 的解决方案。感谢您的努力,我会接受这个答案。
【解决方案2】:

answer from SteAp 上进行扩展,我还需要能够根据 RGBA 目标颜色调整每个像素的透明度。

我还修复了边缘暗的问题 - 这是每个像素的颜色被原始 alpha 级别调整的结果,而不是仅仅调整它自己的 alpha。

// R,G,B = 0-255 range
// A = 0.0 to 1.0 range
function colorizeBasedOnAplhaChannnel($file, $targetR, $targetG, $targetB, $targetA, $targetName ) {
    $im_src = imagecreatefrompng($file);

    $width = imagesx($im_src);
    $height = imagesy($im_src);

    $im_dst = imagecreatefrompng($file);

    // Turn off alpha blending and set alpha flag
    imagealphablending($im_dst, false);
    imagesavealpha($im_dst, true);

    // Fill transparent first (otherwise would result in black background)
    imagefill($im_dst, 0, 0, imagecolorallocatealpha($im_dst, 0, 0, 0, 127));

    for ($x=0; $x<$width; $x++) {
        for ($y=0; $y<$height; $y++) {
            $alpha = (imagecolorat( $im_src, $x, $y ) >> 24 & 0xFF);

            $col = imagecolorallocatealpha( $im_dst,
                $targetR - (int) ( 1.0 / 255.0 * (double) $targetR ),
                $targetG - (int) ( 1.0 / 255.0 * (double) $targetG ),
                $targetB - (int) ( 1.0 / 255.0 * (double) $targetB ),
                (($alpha - 127) * $targetA) + 127
            );

            if (false === $col) {
                die( 'sorry, out of colors...' );
            }

            imagesetpixel( $im_dst, $x, $y, $col );
        }
    }

    imagepng( $im_dst, $targetName);
    imagedestroy($im_dst);
}

【讨论】:

    【解决方案3】:

    使用SteAp 接受的代码作为起点(因为我没有设法实现透明度,只是一个白色背景),我修改了上述代码,结果是这样的:

    <?php 
    
    function colorizeKeepAplhaChannnel( $inputFilePathIn, $targetRedIn, $targetGreenIn, $targetBlueIn, $outputFilePathIn ) {
        $im_src = imagecreatefrompng( $inputFilePathIn );
        $im_dst = imagecreatefrompng( $inputFilePathIn );
        $width = imagesx($im_src);
        $height = imagesy($im_src);
    
        // Note this: FILL IMAGE WITH TRANSPARENT BG
        imagefill($im_dst, 0, 0, IMG_COLOR_TRANSPARENT);
        imagesavealpha($im_dst,true);
        imagealphablending($im_dst, true);
    
        $flagOK = 1;
        for( $x=0; $x<$width; $x++ ) {
            for( $y=0; $y<$height; $y++ ) {
                $rgb = imagecolorat( $im_src, $x, $y );
                $colorOldRGB = imagecolorsforindex($im_src, $rgb);
                $alpha = $colorOldRGB["alpha"];
                $colorNew = imagecolorallocatealpha($im_src, $targetRedIn, $targetGreenIn, $targetBlueIn, $alpha);
    
                $flagFoundColor = true;
                // uncomment next 3 lines to substitute only 1 color (in this case, BLACK/greys)
    /*
                $colorOld = imagecolorallocatealpha($im_src, $colorOldRGB["red"], $colorOldRGB["green"], $colorOldRGB["blue"], 0); // original color WITHOUT alpha channel
                $color2Change = imagecolorallocatealpha($im_src, 0, 0, 0, 0); // opaque BLACK - change to desired color
                $flagFoundColor = ($color2Change == $colorOld);
    */
    
                if ( false === $colorNew ) {
                    //echo( "FALSE COLOR:$colorNew alpha:$alpha<br/>" );
                    $flagOK = 0; 
                } else if ($flagFoundColor) {
                    imagesetpixel( $im_dst, $x, $y, $colorNew );
                    //echo "x:$x y:$y col=$colorNew alpha:$alpha<br/>";
                } 
            }
        }
        $flagOK2 = imagepng($im_dst, $outputFilePathIn);
    
        if ($flagOK && $flagOK2) {
            echo ("<strong>Congratulations, your conversion was successful </strong><br/>new file $outputFilePathIn<br/>");
        } else if ($flagOK2 && !$flagOK) {
            echo ("<strong>ERROR, your conversion was UNsuccessful</strong><br/>Please verify if your PNG is truecolor<br/>input file $inputFilePathIn<br/>");
        } else if (!$flagOK2 && $flagOK) {
            $dirNameOutput = dirname($outputFilePathIn)."/";
            echo ("<strong>ERROR, your conversion was successful, but could not save file</strong><br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
        } else {
            $dirNameOutput = dirname($outputFilePathIn)."/";
            echo ("<strong>ERROR, your conversion was UNsuccessful AND could not save file</strong><br/>Please verify if your PNG is truecolor<br/>Please verify that you have PERMISSION to save to directory $dirName <br/>input file $inputFilePathIn<br/>");
        }
    
        echo ("TargetName:$outputFilePathIn wid:$width height:$height CONVERTED:|$flagOK| SAVED:|$flagOK2|<br/>");
        imagedestroy($im_dst);
        imagedestroy($im_src);
    }
    
    
    
    
    $targetRed = 0;
    $targetGreen = 180;
    $targetBlue = 0;
    
    //$inputFileName = 'frameSquareBlack_88x110.png';
    $inputFileName = 'testMe.png';
    $dirName = "../img/profilePics/";
    $nameTemp = basename($inputFileName, ".png");
    $outputFileName = $nameTemp."_$targetRed"."_$targetGreen"."_$targetBlue.png";
    $inputFilePath = $dirName.$inputFileName;
    $outputFilePath = $dirName.$outputFileName;
    
    //echo "inputFileName:$inputFilePath<br>outputName:$outputFilePath<br>";
    colorizeKeepAplhaChannnel( $inputFilePath, $targetRed, $targetGreen, $targetBlue, $outputFilePath);
    ?>
    <br/><br/>
    Original <br/>
    <img src="<?php echo $inputFilePath; ?>">
    <br /><br />Colorized<br/>
    <img src="<?php echo $outputFilePath; ?>">
    <br />
    

    这种变化将所有颜色更改为所选颜色(不仅仅是黑色,简单的 IF 可以解决问题 - 取消注释函数中指示的 3 行,您将实现此目的)

    出于说明目的,在本例中,使用了以下图像(因为leaf.png 是单色的,具有透明度):

    【讨论】:

      【解决方案4】:

      正如我已经说过的,我花了很多时间搜索,到目前为止我发现的是使用 html5 canvas、javascript 和 ajax。

      我使用的唯一库是 javascript 库 jQuery,但它是可选的。代码可以很容易地改写为使用普通的 javascript。

      它是如何工作的:

      1) js 从 ajax.php 中提取数据,返回所有文件的数组

      2) js 然后循环遍历文件列表并为每个项目执行change(src,color)

      3) js 函数change(src,color) 从源加载图像,替换它的颜色并将一个img 元素添加到#Cell 并显示它(用于调试)。

      4) change() 也调用 save(src,filename,cname) 函数 5) js 函数save(src,filename,cname) 发送带有图像数据的ajax 请求,ajax.php 将图像保存到服务器。

      代码如下:

      ajax.php

      <?php
      $r = $_REQUEST;
      $act = $r['action'];
      if($act == "get_all") {
          $js = "";
          $dir = getcwd()."/img/";
          $images = glob($dir."/*.png",GLOB_BRACE);
          foreach($images as $image) {
              $name = basename($image);
              $js[] = $name;
          }
          echo json_encode($js);
          die();
      }
      elseif($act == "save") {
          $img = $r['file'];
          $name = $r['name'];
          $color = $r['color'];
          $dir = "results/$color";
          if(!file_exists($dir) || !is_dir($dir)) mkdir($dir,777,true);
          $file = $dir."/$name";
          file_put_contents($file,file_get_contents("data://".$img));
          if(file_exists($file)) echo "Success";
          else echo $file;
          die();
      }
      

      index.php(仅限 html)

      <!doctype html>
              <html>
      <head>
          <script src="jquery.js" type="text/javascript"></script>
          <script src="demo.js" type="text/javascript"></script>
      </head>
      <body>
      <div id="ctrl">
          <input type="text" id="color" value="#666666" placeholder="Color in HEX format (ex. #ff0000)" />
          <input type="text" id="cname" value="grey" placeholder="Color name (destionation dir name)" />
          <button type="button" id="doit">Change</button>
      </div>
      <div id="Cell">
      
      </div>
      </body>
      
      </html>
      

      demo.js

      $(document).ready(function() {
          $(document).on("click","#doit",function() {
              var c = $("#color");
              if(c.val() != "") {
                  $("#Cell").html("");
                  $.post("ajax.php",{ action: "get_all" },function(s) {
                      var images = $.parseJSON(s);
                      $.each(images, function(index, element) {
                          change(images[index], c.val());
                      });
                  });
              }
          });
      });
      function change(src,color) {
          var myImg = new Image();
          myImg.src = "img/"+src;
          myImg.onload = function() {
              var canvas = document.createElement("canvas");
              var ctx = canvas.getContext("2d");
              ctx.drawImage(myImg,0,0);
              var imgd = ctx.getImageData(0, 0, myImg.width, myImg.height);
              canvas.height = myImg.height;
              canvas.width = myImg.width;
              var new_color = HexToRGB(color);
              // console.log(imgd)
              for (i = 0; i <imgd.data.length; i += 4) {
                  imgd.data[i]   = new_color.R;
                  imgd.data[i+1] = new_color.G;
                  imgd.data[i+2] = new_color.B;
              }
              ctx.putImageData(imgd, 0, 0);
              var newImage=new Image()
              newImage.src=canvas.toDataURL("image/png");
              $(newImage).css("margin","5px");
              $(newImage).attr('data-title',src);
              $("#Cell").append(newImage);
              var c = $("#cname");
              if(c.val() == "") c.val("temp");
              save(newImage.src,src, c.val());
          };
      }
      function save(src,filename,cname) {
          $.post("ajax.php", { action: "save", file: src, name: filename, color: cname },function(s) {
              console.log(s);
          })
      }
      function HexToRGB(Hex)
      {
          var Long = parseInt(Hex.replace(/^#/, ""), 16);
          return {
              R: (Long >>> 16) & 0xff,
              G: (Long >>> 8) & 0xff,
              B: Long & 0xff
          };
      }
      

      我已经对其进行了测试,对于重新着色和保存 420 个 24x24 图像,它花费了不到 10 秒(在 localhost 上)(420 个异步 ajax 调用)。一旦原始图像被缓存,它就会更快地完成。图像质量与原始图像相同。

      同样,这个解决方案是供我个人使用的,所以代码非常不受管理,我相信它可以改进,但你去吧 - 照原样,它可以工作。

      【讨论】:

        【解决方案5】:

        第三张图片看起来不太好,因为imagetruecolortopalette($im,true, 255); 渲染了一张丑陋的图片:

        由于第二张图片不好看,第三张也不好看。

        代码:

        <?php
        unlink( dirname ( __FILE__ ) . '/newleaf.png' );
        unlink( dirname ( __FILE__ ) . '/newleaf1.png' );
        
        function LoadPNG( $imgname )
        {
            $im = imagecreatefrompng ($imgname);
            imagetruecolortopalette($im,true, 255);
        
            imagepng($im, 'newleaf1.png' ); // save image as png
        
            $index = imagecolorclosest ( $im,  0,0,0 ); // GET BLACK COLOR
            imagecolorset($im,$index,0,150,255); // SET COLOR TO BLUE
            $name = basename($imgname);
            imagepng($im, 'newleaf.png' ); // save image as png
            imagedestroy($im);
        }
        
        $img = dirname ( __FILE__ ) . '/leaf.png';
        LoadPNG( $img );
        
        ?>
        
        Original
        <img src="leaf.png">
        <br />After make truecolortopalette($im,true, 255);
        <img src="newleaf1.png">
        <br />Thus..
        <img src="newleaf.png">
        

        【讨论】:

        • 和我一样。你有更好的渲染解决方案吗?
        • @NikolaR。不幸的是,我现在无法提供解决方案。太晚了……大概明天吧。有趣的案例...
        • 谢谢,非常感谢!
        • @NikolaR。请在我的附加答案中找到一个可行的解决方案。
        【解决方案6】:

        我尝试过 SteAp 中的示例,但它不适用于某些文件。我改用 imagemagick:

        convert liquid.png -fuzz 100% -fill 'green' +opaque transparent -colorize 100 liquid_im.png
        

        【讨论】:

          猜你喜欢
          • 2014-11-24
          • 2015-02-03
          • 1970-01-01
          • 2018-11-14
          • 1970-01-01
          • 2012-09-30
          • 2013-03-17
          • 1970-01-01
          • 2010-12-28
          相关资源
          最近更新 更多