Я много искал, и я нашел только несколько решений (в 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.
EDIT 2:
Я нашел интересный подход, используя html5 canvas и javascript здесь: http://users7.jabry.com/overlord/mug.html
Может быть, кто-то может подумать о том, как перевести это на PHP, если это возможно.
НОВОЕ РЕШЕНИЕ
В ответах
Этот код не иллюстрирует проблему, но преобразует цвета следующим образом:
Использует канал ALPHA изображения для определения цвета. Для других результатов просто 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">
Расширяя ответ от SteAp , мне также необходимо было настроить прозрачность каждого пикселя на основе целевого цвета RGBA.
Я также исправил проблему с темными краями – это было результатом того, что цвет каждого пикселя корректировался исходным альфа-уровнем, а не просто регулировал альфу на ее собственной.
// 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); }
Используя принятый код 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:$xy:$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
является монохроматическим, с прозрачностью):
Как я уже сказал, я потратил много времени на поиск и то, что я нашел до сих пор, это использование html5 canvas, javascript и ajax.
Только библиотека, которую я использовал, – это javascript-библиотека jQuery, но она не является обязательной. Код можно легко переписать, чтобы использовать простой javascript.
Как это работает:
1) js извлекает данные из ajax.php, который возвращает массив всех файлов
2) js затем перебирает список файлов и выполняет change(src,color)
для каждого элемента
3) change(src,color)
функции 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 изображений 24×24, потребовалось менее 10 секунд (на localhost) (420 асинхронных вызовов ajax). Когда исходные изображения кэшируются, он заканчивается намного быстрее. Качество изображения остается прежним.
Опять же, это решение для моего личного использования, поэтому код довольно неуправляемый, и я уверен, что его можно улучшить, но здесь вы идете – как есть, он работает.
Третье изображение выглядит не очень хорошо, потому что 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">