2012-08-15 70 views
4

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

这是预期的结果: the expected result

“颜色”混合模式被定义,在Adobe部位,这样的:“创建具有基色的亮度和色相和结果颜色混合颜色的饱和度,这保留了图像中的灰度级,对于着色单色图像和着色彩色图像非常有用。“

在ImageMagick中定义了一种组合方法,它似乎可以做同样的事情(Luminize),但结果并不是迄今为止所期望的。

什么似乎提供最接近结果的ImageMagick是默认的混合撰写方法,像这样使用:

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

我还试图创建将包含第一图像的亮度和色调的图像和第二个使用-fx操作符饱和,但结果再次远不及我所需要的。

+0

你是如何占 “35%不透明度” 在你的ImageMagick命令? – DarenW 2012-08-15 16:27:51

+0

为了解决这个问题,我用颜色创建了一个透明png文件,并将不透明度降低到35%。所以,我几乎试图混合两个现有的图像文件。 – viorel 2012-08-15 16:33:35

+0

您可以提供以下图像:(1)原始图像; (2)35%不透明度的彩色图像; (3)预期结果的图像(什么photoshop创建) – 2012-08-15 17:01:01

回答

3

在城堡基于有价值的答案,我试图找到在PHP这样做的最佳解决方案。他列举的实施有两个主要缺陷:一个不考虑不透明度(如果有的话),另一个非常缓慢且耗费资源。使用PHP处理500x500像素的图像需要15秒左右的时间,其中Apache将处理器的容量保持在95%以上。

我发现的最快和最少的资源消耗实际上是通过使用画布处理图像在HTML5中完成的。结果令人惊叹,图像正在现场处理。

我会在最后的代码块之下发布,一个用于PHP,一个用于HTML。如果您需要使用此服务器端,则可以在Node中复制粘贴HTML代码。js和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> 

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']).')'); 
+1

Hello Castles,这实际上似乎为RGB图像(无RGBA) 。我也设法添加透明度位(我的颜色的alpha为0.35)。你可以在这里找到代码:http://pastebin.com/U1g5X16z。在我进行混合后应用透明度而不是之前,似乎我的效果更好。我的逻辑是在实际混合之前需要添加透明度。但是,这个代码存在一个巨大的问题:它运行速度非常慢,它使得Apache在运行时使用了99.5%的处理器。 – viorel 2012-08-30 11:11:35

+1

我也把它翻译成Imagemagick,但结果远不如它应该的。也许你有一些想法? convert original.jpg transparent_layer.png -channel RGBA -fx“diff =(ur * 0.3 + ug * 0.59 + ub * 0.11) - (vr * 0.3 + vg * 0.59 + vb * 0.11); v + diff”result .jpg 注意:Altoughe Imagemagick能够自行获取亮度,它不使用相同的公式,并且不能进行减法,因为它会引发错误 – viorel 2012-08-30 11:14:49