4

画像に色を付けるには、ImageMagickでAdobePhotoshopの「カラー」ブレンドモードを模倣するコマンドを作成する必要があります。これを行うために、元の画像と、不透明度35%のフルカラーレイヤーで構成される別の画像を作成しようとしています。これは元の画像とブレンドし、色付きの結果画像を作成する必要があります。

これは期待される結果です: 期待される結果

「カラー」ブレンドモードは、Adobeサイトで次のように定義されています。「ベースカラーの輝度とブレンドカラーの色相と彩度で結果カラーを作成します。これにより、画像のグレーレベルが保持されます。モノクロ画像のカラーリングやカラー画像の着色に便利です。」

同じことをするように見えるImageMagickに定義されたcomposeメソッド(Luminize)がありますが、結果は期待されたものとはほど遠いものではありません。

Imagemagickで最も近い結果を提供するように見えるのは、次のように使用されるデフォルトのブレンド作成方法です。

convert image.jpg color_layer.png -compose blend -composite result.jpg

また、-fx演算子を使用して、最初の画像の明度と2番目の画像の色相と彩度を含む画像を作成しようとしましたが、結果は私が必要とするものにはほど遠いものでした。

4

2 に答える 2

3

Castles の貴重な回答に基づいて、PHP でこれを行うための最良の解決策を見つけようとしました。彼が引用した実装には 2 つの大きな欠陥があります。1 つは不透明度が考慮されていないこと、もう 1 つは非常に遅く、リソースを消費することです。PHP で 500x500 ピクセルの画像を処理するには約 15 秒かかり、Apache はプロセッサを最大 95% 使用します。

私が見つけた最速かつ最小のリソース消費は、キャンバスを使用して画像を処理することにより、実際には HTML5 で実行していました。結果は驚くべきもので、画像はその場で処理されています。

PHP 用と HTML 用のコードの最後のチャンクを以下に掲載します。このサーバーサイドを使用する必要がある場合は、Node.js と NodeCanvas の HTML コードをコピーして貼り付けることができます: https://github.com/LearnBoost/node-canvas

PHP (不透明度あり):

<?php

function Lum($colour) {
    return ($colour['r'] * 0.3) + ($colour['g'] * 0.59) + ($colour['b'] * 0.11);
}

function ClipColour($colour) {
    $result     = $colour;
    $luminance  = Lum($colour);

    $cMin = min($colour['r'], $colour['g'], $colour['b']);
    $cMax = max($colour['r'], $colour['g'], $colour['b']);

    if ($cMin < 0.0) {
        $result['r'] = $luminance + ((($colour['r'] - $luminance) * $luminance) / ($luminance - $cMin));
        $result['g'] = $luminance + ((($colour['g'] - $luminance) * $luminance) / ($luminance - $cMin));
        $result['b'] = $luminance + ((($colour['b'] - $luminance) * $luminance) / ($luminance - $cMin));
    } 

    if ($cMax > 255) {
        $result['r'] = $luminance + ((($colour['r'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));
        $result['g'] = $luminance + ((($colour['g'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));
        $result['b'] = $luminance + ((($colour['b'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));
    }

    return $result;
}

function SetLum($colour, $luminance) {

    $result = array();

    $diff =   $luminance - Lum($colour);

    $result['r'] = $colour['r'] + $diff;
    $result['g'] = $colour['g'] + $diff;
    $result['b'] = $colour['b'] + $diff;

    return ClipColour($result);

} 

function normalizeColor( $color ) {
    $color['r'] = $color['r'] / 255;
    $color['g'] = $color['g'] / 255;
    $color['b'] = $color['b'] / 255;

    return $color;
}

function denormalizeColor( $color ) {
    $color['r'] = round($color['r'] * 255);
    $color['g'] = round($color['g'] * 255);
    $color['b'] = round($color['b'] * 255);

    return $color;
}

$overlay_color = array('r'=>180,'g'=>22,'b'=>1, 'a' => 0.35);

$img = new Imagick();

if( !isset($_GET['case']) ) {
    $_GET['case'] = '';
}

//unmodified version
$original   = new Imagick('girl.jpg');

//photoshop image to compare
$ps = new Imagick('original.jpg');

$img->addImage($original);
$it = $original->getPixelIterator();

foreach( $it as $row => $pixels ) {
    foreach ( $pixels as $column => $pixel ) {
        $rgbIni = $pixel->getColor();

        $rgb = SetLum($overlay_color, Lum($rgbIni));
         $overlay_color     = normalizeColor($overlay_color);
            $rgb        = normalizeColor($rgb);

            $rgbIni         = normalizeColor($rgbIni);

        $rgb['r'] = ((1 - $overlay_color['a']) * $rgbIni['r']) + ($overlay_color['a'] * $rgb['r']);
        $rgb['g'] = ((1 - $overlay_color['a']) * $rgbIni['g']) + ($overlay_color['a'] * $rgb['g']);
        $rgb['b'] = ((1 - $overlay_color['a']) * $rgbIni['b']) + ($overlay_color['a'] * $rgb['b']);

        $test           = denormalizeColor($test);
        $rgb            = denormalizeColor($rgb);
        $overlay_color  = denormalizeColor($overlay_color);

        $pixel->setColor('rgb('.round($rgb['r']).','. round($rgb['g']).','.round($rgb['b']).')');

    }

    $it->syncIterator();
}

//add modified version
$img->addImage($original);
$img->addImage($ps);

$img->resetIterator();
$combined = $img->appendImages(true); //stack images

header('content-type: image/jpeg');

$combined->setImageFormat("jpeg");

echo $combined;

?>

HTML:

<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <script>
        var RGBA = function(r, g, b, a) {
            this.R = r || 0;
            this.G = g || 0;
            this.B = b || 0;
            this.A = a || 0.5;
        }

        function SetLum(initialColor, pixelColor) {

            var initalColorLuminance = initialColor.R * 0.3 + initialColor.G * 0.59 + initialColor.B * 0.11;
            var pixelColorLuminance = pixelColor.R * 0.3 + pixelColor.G * 0.59 + pixelColor.B * 0.11;

            var diff = pixelColorLuminance - initalColorLuminance;

            var response = new Array;
               response[0] = initialColor.R + diff;
               response[1] = initialColor.G + diff;
               response[2] = initialColor.B + diff;

            //console.log(response[0]);

            return ClipColour(response);

        }

        function alphaComposite(mv, ov, a) {
            return (mv * a) + (ov * (1 - a));
        }

        function ClipColour(color) { //function to prevent underexposure or overexposure on some pixels

            var result     = color;
            var luminance  = color[0] * 0.3 + color[1] * 0.59 + color[1] * 0.11;

            var cMin = Math.min(color[0], color[1], color[2]);
            var cMax = Math.max(color[0], color[1], color[2]);

            if (cMin < 0.0) {
                color[0] = luminance + (((color[0] - luminance) * luminance) / (luminance - cMin));
                color[1] = luminance + (((color[1] - luminance) * luminance) / (luminance - cMin));
                color[2] = luminance + (((color[2] - luminance) * luminance) / (luminance - cMin));
            } 

            if (cMax > 255) {
                color[0] = luminance + (((color[0] - luminance) * (255 - luminance)) / (cMax - luminance));
                color[1] = luminance + (((color[1] - luminance) * (255 - luminance)) / (cMax - luminance));
                color[2] = luminance + (((color[2] - luminance) * (255 - luminance)) / (cMax - luminance));
            }

            return color;
        }

        function processImage(image, targetColour) {
            var canvas = document.createElement('canvas');
                c = canvas.getContext('2d');

            canvas.width = image.width;
            canvas.height = image.height;

            // Draw the building on the original canvas
            c.drawImage(image, 0, 0, canvas.width, canvas.height);

            // There's a (much) faster way to cycle through all the pixels using typed arrays, 
            // but I'm playing it safe so that the example works in all browsers.
            var imageData = c.getImageData(0, 0, canvas.width, canvas.height),
                imageDataPixels = imageData.data;

            for (var i = 0, len = imageDataPixels.length; i < len; i += 4) {
                var pixelColor = new RGBA(imageDataPixels[i], imageDataPixels[i+1], imageDataPixels[i+2], 1);
                var test = SetLum(targetColour, pixelColor);

                var r    = Math.round(test[0]);
                var g    = Math.round(test[1]);
                var b    = Math.round(test[2]);

                imageDataPixels[i] = alphaComposite(r, imageDataPixels[i], targetColour.A);
                imageDataPixels[i + 1] = alphaComposite(g, imageDataPixels[i + 1], targetColour.A);
                imageDataPixels[i + 2] = alphaComposite(b, imageDataPixels[i + 2], targetColour.A);
            }

            c.putImageData(imageData, 0, 0);

            return canvas;
        }

        document.addEventListener('DOMContentLoaded', function() {
            var image = new Image(),
                processImageFile = null;

            image.src = "girl.jpg";

            image.addEventListener('load', function() {
                var canvas = document.getElementById('canvas'),
                    c = canvas.getContext('2d'),
                    imageRGBA = new RGBA(180, 22, 1, 0.35);

                canvas.width = image.width;
                canvas.height = image.height;

                c.drawImage(image, 0, 0);

                processImageFile = processImage(image, imageRGBA);
                c.drawImage(processImageFile, 0, 0);
            });
        });
    </script>
</head>
<body>

    <img src="girl.jpg" />
    <br />

    <canvas id="canvas"></canvas>

    <br />
    <img src="original.jpg" />
</body>

于 2012-10-09T14:31:04.070 に答える
2

私もこれをやろうとしてきましたが、私が思いついた最善の方法は、代わりにオーバーレイフィルターを使用することです..これがphpの私のコードです:

$overlay_color = array('r'=>180,'g'=>22,'b'=>1);

function overlay ($top, $bottom) {
    return $bottom < 128 ? ( 2 * $bottom * $top ) / 255 : 255 - ( 2 * ( 255 - $bottom ) * ( 255 - $top ) / 255 );
}

$original = new Imagick('img/girl.jpg');

$img = new Imagick();

//unmodified version
$img->addImage($original);


$it = $original->getPixelIterator();

foreach( $it as $row => $pixels )
{
    foreach ( $pixels as $column => $pixel )
    {
            $rgba = $pixel->getColor();
            $pixel->setColor('rgb('.overlay($overlay_color['r'], $rgba['r']).','.overlay($overlay_color['g'], $rgba['g']).','.overlay($overlay_color['b'], $rgba['b']).')');
    }

    $it->syncIterator();
}

//add modified version
$img->addImage($original);


$img->resetIterator();
$combined = $img->appendImages(true); //stack images


header('content-type: image/jpeg');

$combined->setImageFormat("jpeg");

echo $combined;

Photoshop の「カラー」モードの式が見つからないようです。私が見つけることができれば、それはかなり簡単です。

更新: 実際の Photoshop の式をかなりよく説明しているこのサイトを見つけました: http://www.beneaththewaves.net/Photography/Secrets_of_Photoshops_Colour_Blend_Mode_Revealed_Sort_Of.htmlで、PHP で動作させることができました。関数は次のとおりです。

function Lum($colour) {

return ($colour['r'] * 0.3) + ($colour['g'] * 0.59) + ($colour['b'] * 0.11);

}



function ClipColour($colour) {

    $result = $colour;

    $luminance = Lum($colour);

    $cMin = min($colour['r'], $colour['g'], $colour['b']);

    $cMax = max($colour['r'], $colour['g'], $colour['b']);

    if ($cMin < 0.0) {

        $result['r'] = $luminance + ((($colour['r'] - $luminance) * $luminance) / ($luminance - $cMin));

        $result['g'] = $luminance + ((($colour['g'] - $luminance) * $luminance) / ($luminance - $cMin));

        $result['b'] = $luminance + ((($colour['b'] - $luminance) * $luminance) / ($luminance - $cMin));


    } 

    if ($cMax > 255) {

        $result['r'] = $luminance + ((($colour['r'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));

        $result['g'] = $luminance + ((($colour['g'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));

        $result['b'] = $luminance + ((($colour['b'] - $luminance) * (255 - $luminance)) / ($cMax - $luminance));

    }

    return $result;
}



function SetLum($colour, $luminance) {

    $result = array();

    $diff =   $luminance - Lum($colour);


    $result['r'] = $colour['r'] + $diff;

    $result['g'] = $colour['g'] + $diff;

    $result['b'] = $colour['b'] + $diff;


    return ClipColour($result);

} 

更新されたピクセル変換コードは次のとおりです。

$rgb = $pixel->getColor();
$rgb = SetLum($overlay_color,Lum($rgb));
$pixel->setColor('rgb('.round($rgb['r']).','. round($rgb['g']).','.round($rgb['b']).')');
于 2012-08-24T05:01:19.640 に答える