我需要在 ImageMagick 中创建一个模仿 Adobe Photoshop 的“颜色”混合模式的命令,以便为图像着色。为了做到这一点,我试图合成原始图像和另一个由全彩色图层组成的图像,不透明度为 35%。这应该与原始图像混合并创建彩色结果图像。
这是预期的结果:
Adobe 网站上正在定义“颜色”混合模式,如下所示:“使用基色的亮度和混合色的色调和饱和度创建结果颜色。这会保留图像中的灰度级,并且用于为单色图像着色和为彩色图像着色。”
ImageMagick 中定义了一个 compose 方法,它似乎可以做同样的事情(Luminize),但结果远非预期的那样。
似乎在 Imagemagick 中提供最接近结果的是默认的混合组合方法,使用如下:
convert image.jpg color_layer.png -compose blend -composite result.jpg
我还尝试使用 -fx 运算符创建一个包含第一个图像的亮度和第二个图像的色相和饱和度的图像,但结果再次与我需要的相差甚远。
最佳答案
基于 CaSTLes 的宝贵答案,我试图找到在 PHP 中执行此操作的最佳解决方案。他引用的实现有两个主要缺陷:一个是它没有考虑不透明度(如果有的话),第二个是非常缓慢且消耗资源。在 PHP 中处理 500x500 像素的图像大约需要 15 秒,其中 Apache 将保持处理器高达 95%。
我发现最快和最少的资源消耗实际上是在 HTML5 中通过使用 Canvas 来处理图像。结果令人惊叹,图像正在现场处理。
我将在最后的代码块下方发布,其中一个用于 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>
关于imagemagick - 在 ImageMagick 中复制 Photoshop 的 "Color"混合模式,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11973086/