重复Photoshop的“颜色” ImageMagick中的混合模式 [英] Duplicate Photoshop's "Color" blend mode in ImageMagick

查看:131
本文介绍了重复Photoshop的“颜色” ImageMagick中的混合模式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在ImageMagick中创建一个模仿Adobe PhotoshopColor混合模式的命令,以便为图像着色。为了做到这一点,我试图组合原始图像和另一个由全色层组成的图像,不透明度为35%。这应该与原始图像混合并创建一个颜色着色结果图像。

I need to create a command that mimics Adobe Photoshop's "Color" blend mode in ImageMagick in order to tint an image. In order to do this, I'm trying to compose the original image and another image that consists of a full color layer, at 35% opacity. This should blend with the original image and create a color tinted result image.

这是预期的结果:

This is the expected result:

正在Adobe网站上定义颜色混合模式,如这样:使用基色的亮度和混合色的色调和饱和度创建结果颜色。这样可以保留图像中的灰度级,对于着色单色图像和着色彩色图像非常有用。

The "Color" blend mode is being defined, on the Adobe site, like this: "Creates a result color with the luminance of the base color and the hue and saturation of the blend color. This preserves the gray levels in the image and is useful for coloring monochrome images and for tinting color images."

在ImageMagick中定义了一个似乎做同样事情的组合方法(Luminize),但结果并不是预期的结果。

There is a compose method defined into ImageMagick that seems to do the same thing (Luminize), but the results are not by far what is expected.

似乎在Imagemagick中提供最接近的结果是默认的混合组合方法,使用如下:

What seems to provide the closest result in Imagemagick is the default blend compose method, used like this:

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

我也试过创建一个图像那将包含使用-fx运算符,第一个图像的亮度和第二个图像的色调和饱和度,但结果再也没有我所需要的。

I also tried creating an image that would contain the luminosity of the first image and the hue and saturation of the second using the -fx operator, but the result was again nowhere near what I needed.

推荐答案

基于Castles有价值的答案,我试图在PHP中找到这样做的最佳解决方案。他引用的实现有两个主要缺陷:一个是不考虑不透明度,如果有的话,第二个是非常慢和资源消耗。在PHP中处理500x500像素的图像大约需要15秒,而Apache会将处理器保持在95%以上。

Based on Castles valuable answer, I tried to find the best solution of doing this in PHP. The implementation he cited has two major flaws: one that it doesn't take account of the opacity, if any and the second that is very slow and resource consuming. Processing a 500x500 pixels image in PHP would take about 15 seconds in which Apache would hold the processor up to 95%.

我发现的最快和最少的资源消耗实际上是在做它在HTML5中使用canvas来处理图像。结果令人惊讶,图像正在现场处理。

The fastest and least resources consuming I found was actually doing it in HTML5 by using canvas to process the image. The results are amazing and the image is being processed on the spot.

我将在最后的代码块下面发布,一个用于PHP,一个用于HTML。如果您需要使用此服务器端,您可以在Node.js和NodeCanvas中复制粘贴HTML代码: https: //github.com/LearnBoost/node-canvas

I will post below the final chunks of code, one for PHP and one for HTML. If you need to use this serverside, you can copy-paste the HTML code in Node.js and NodeCanvas: 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>

这篇关于重复Photoshop的“颜色” ImageMagick中的混合模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆