我搜索了很多,我发现只有少数解决方案(在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 canvas和javascript的有趣方法: http://users7.jabry.com/overlord/mug.html
如果可能的话,也许有人可以知道如何将其翻译成PHP。
新解决方案
回答
答案 0 :(得分:14)
此代码不能解释问题,但会转换如下颜色:
使用图像的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">
答案 1 :(得分: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);
}
答案 2 :(得分:2)
使用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
是单色的,具有透明度):
答案 3 :(得分:1)
正如我已经说过的,我花了很多时间搜索,到目前为止我发现的是使用html5 canvas,javascript和ajax。
我使用的库只是javascript库 jQuery ,但它是可选的。可以轻松地重写代码以使用普通的javascript。
工作原理:
1)js从ajax.php中提取数据,该数据返回所有文件的数组
2)然后js循环到文件列表并为每个项目执行change(src,color)
3)js function change(src,color)
从源加载图像,替换它的颜色并将img元素添加到#Cell
并显示它(用于调试)。
4)change()
也调用save(src,filename,cname)
函数
5)js function 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个async ajax调用)。一旦原始图像被缓存,它就会更快地完成。图像质量与原始图像保持一致。
同样,这个解决方案仅供我个人使用,所以代码非常不受管理,我相信它可以改进但是你去了 - 因为它可以工作。
答案 4 :(得分:0)
第三张图片看起来不太好,因为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">
答案 5 :(得分:0)
我已经尝试过SteAp提供的示例,但是它不适用于某些文件。我改用imagemagick:
convert liquid.png -fuzz 100% -fill 'green' +opaque transparent -colorize 100 liquid_im.png