Подтвердить что ты не робот

Php - заменить базовый цвет прозрачного png изображения

Я много искал, и я нашел только несколько решений (в google и stackoverflow, поэтому, пожалуйста, не отмечайте это как дубликат, если не существует действительно дублированного вопроса), но проблемы - это жесткие края. Есть ли правильный способ изменения базового цвета, скажем, черного png-изображения с прозрачным фоном, но для сохранения мягких краев?

Это пример изображения:

enter image description here

Я хочу, чтобы это выглядело так:

enter image description here

но найденные решения дают мне следующее:

enter image description here

Так как я буду использовать это на своем локальном хосте, только для личного использования, любая php-библиотека, которая могла бы помочь в этом, была бы оценена.

UPDATE:

Это функция, которая дает мне третье изображение:

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 цветов), поэтому я догадываюсь, почему существуют жесткие края. Я ищу решение (улучшение для этого script), чтобы сохранить прозрачность и мягкие края изображения PNG.

ИЗМЕНИТЬ 2:

Я нашел интересный подход, используя html5 canvas и javascript здесь: http://users7.jabry.com/overlord/mug.html

Может быть, у кого-то может быть идея, как перевести это на PHP, если это возможно.

НОВОЕ РЕШЕНИЕ

В ответах

4b9b3361

Ответ 1

Этот код не иллюстрирует проблему, но преобразует цвета следующим образом:

enter image description here

Использует канал 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 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">

Ответ 2

Продолжая ответ от 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);
}

Ответ 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 />

enter image description here

этот вариант изменяет ВСЕ цвета на выбранный цвет (не только черный, простой IF может решить проблему - раскол 3 обозначенных строки в функции, и вы достигнете этого)

enter image description here

Для иллюстративных целей в этом случае использовалось следующее изображение (поскольку leaf.png является монохроматическим с прозрачностью): enter image description here

Ответ 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 секунд (на локальном хосте) (420 асинхронных вызовов aynax). Когда исходные изображения кэшируются, он заканчивается намного быстрее. Качество изображения остается прежним.

Опять же, это решение для моего личного использования, поэтому код довольно неуправляемый, и я уверен, что он может быть улучшен, но здесь вы идете - как есть, он работает.

Ответ 5

Третье изображение выглядит не очень хорошо, потому что imagetruecolortopalette($im,true, 255); отображает уродливое изображение:

enter image description here

Так как второе изображение выглядит не очень хорошо, третье тоже не может выглядеть красиво.

код:

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