简体   繁体   English

Php - 替换透明png图像的基色

[英]Php - replace base color of transparent png image

I have searched a lot and I found only few solutions (on google and stackoverflow so please don't mark this one as a duplicate unless there's really duplicate question), but problems are hard edges.我搜索了很多,我只找到了几个解决方案(在 google 和 stackoverflow 上,所以请不要将这个标记为重复,除非确实有重复的问题),但问题是硬边。 Is there any proper way of changing base color of, let's say black shape png image with transparent background but to preserve soft edges?是否有任何适当的方法可以更改基色,例如具有透明背景的黑色形状 png 图像但要保留软边缘?

This is an example image:这是一个示例图像:

在此处输入图片说明

I want it to look like this:我希望它看起来像这样:

在此处输入图片说明

but the solutions I found give me this one:但我找到的解决方案给了我这个:

在此处输入图片说明

Since I will be using this on my localhost, only for personal use, any php library that could help achieve this is appreciated.由于我将在我的本地主机上使用它,仅供个人使用,因此感谢任何可以帮助实现这一目标的 php 库。

UPDATE:更新:

This is the function that gives me 3rd image:这是给我第三张图片的功能:

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);
}

Originally, this function was a solution for GIF images (palette of 255 colors) so I guess that's why there are hard edges.最初,此功能是针对 GIF 图像(255 色调色板)的解决方案,所以我想这就是存在硬边的原因。 I am looking for a solution (improvement to this script) to preserve transparency and soft edges of PNG image.我正在寻找一种解决方案(改进此脚本)来保留 PNG 图像的透明度和软边缘。

EDIT 2:编辑2:

I have found an interesting approach using html5 canvas and javascript here: http://users7.jabry.com/overlord/mug.html我在这里找到了一种使用 html5 画布和 javascript 的有趣方法: http : //users7.jabry.com/overlord/mug.html

Maybe someone could have an idea how to translate this into PHP if even possible.如果可能的话,也许有人可以知道如何将其翻译成 PHP。

NEW SOLUTION新解决方案

In answers在回答中

This code doesn't exemplify the problem, but transforms colors like this:这段代码没有举例说明问题,而是像这样转换颜色:

在此处输入图片说明

Uses the ALPHA channel of an image to determines coloring.使用图像的 ALPHA 通道来确定颜色。 For other results, just play around with imagecolorallocatealpha() :对于其他结果,只需使用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">

Extending on the answer from SteAp , I had the need to also be able to adjust the transparency of each pixel based on an RGBA target color.扩展来自 SteAp答案,我还需要能够根据 RGBA 目标颜色调整每个像素的透明度。

I also fixed the issue of having dark edges - which was a result of each pixel's color being adjusted by the original alpha level, rather than just adjusting the alpha on it's own.我还解决了有暗边缘的问题——这是每个像素的颜色由原始 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);
}

Using SteAp 's accepted code as a starting point (since with it i didnt manage to achieve transparency, just a white background), i adapted said code and the result is this:使用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 />

在此处输入图片说明

this variation changes ALL colors to chosen color (not just black, a simple IF can solve the problem - uncomment 3 indicated lines in function and you will achieve this)这种变化将所有颜色更改为所选颜色(不仅仅是黑色,一个简单的 IF 可以解决问题 - 在函数中取消注释 3 行,您将实现这一点)

在此处输入图片说明

For illustrative purposes, in this case, the following image was used (because leaf.png is monochromatic, with transparency):出于说明目的,在这种情况下,使用了以下图像(因为leaf.png是单色的,具有透明度):在此处输入图片说明

As I already told, I spent a lot of time searching and what I found so far is using html5 canvas, javascript and ajax.正如我已经说过的,我花了很多时间进行搜索,到目前为止我发现的是使用 html5 canvas、javascript 和 ajax。

Only library I used is javascript library jQuery but it is optional.我使用的唯一库是 javascript 库jQuery但它是可选的。 Code can be easily rewritten to use plain javascript.代码可以很容易地重写为使用纯 javascript。

How it works:这个怎么运作:

1) js pulls data from ajax.php which returns an array of all the files 1) js 从 ajax.php 中提取数据,它返回所有文件的数组

2) js then loops thru file list and performs change(src,color) for each item 2)js然后循环遍历文件列表并为每个项目执行change(src,color)

3) js function change(src,color) loads image from source, replaces it's color and adds an img element to #Cell and displays it (for debug). 3) js 函数change(src,color)从源加载图像,替换它的颜色并将一个 img 元素添加到#Cell并显示它(用于调试)。

4) change() also calls save(src,filename,cname) function 5) js function save(src,filename,cname) sends an ajax request with image data and ajax.php saves image to server. 4) change()也调用save(src,filename,cname)函数 5) js 函数save(src,filename,cname)发送一个带有图像数据的 ajax 请求, ajax.php将图像保存到服务器。

So here's the code:所以这是代码:

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 only) 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演示.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
    };
}

I have tested it, for re-coloring and saving 420 24x24 images, it took less than 10 seconds (on localhost) (420 async ajax calls).我已经对其进行了测试,为了重新着色和保存 420 个 24x24 图像,花费了不到 10 秒的时间(在本地主机上)(420 个异步 ajax 调用)。 Once original images are cached, it finishes much faster.一旦原始图像被缓存,它完成得更快。 Image quality stays the same as original.图像质量与原始图像保持一致。

Again, this solution is for my personal use so code is pretty unmanaged and I am sure it can be improved but here you go - as is, it works.同样,这个解决方案是供我个人使用的,所以代码是非常不受管理的,我相信它可以改进,但是你去吧 - 就这样,它可以工作。

The third image doesn't look fine, because imagetruecolortopalette($im,true, 255);第三张图看起来不太好,因为imagetruecolortopalette($im,true, 255); renders an ugly image:渲染出丑陋的图像:

在此处输入图片说明

Since the second image doesn't look fine, the third can't look beautiful too.由于第二张图片看起来不太好,所以第三张看起来也不漂亮。

Code:代码:

<?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">

I have tried example from SteAp, but it's not working on some files.我已经尝试过 SteAp 的示例,但它不适用于某些文件。 I use imagemagick instead:我使用 imagemagick 代替:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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