Sur la base de Castles réponse précieuse, j'ai essayé de trouver la meilleure solution de le faire en PHP. L'implémentation qu'il a citée a deux défauts majeurs: l'un qui ne tient pas compte de l'opacité, le cas échéant, et l'autre qui est très lent et consomme beaucoup de ressources. Le traitement d'une image de 500x500 pixels en PHP prendrait environ 15 secondes, ce qui permettrait à Apache de maintenir le processeur à 95%.
La ressource la plus rapide et la moins gourmande que j'ai trouvée était en fait en HTML5 en utilisant canvas pour traiter l'image. Les résultats sont incroyables et l'image est en cours de traitement.
Je posterai ci-dessous les derniers morceaux de code, un pour PHP et un pour HTML. Si vous avez besoin d'utiliser ce côté serveur, vous pouvez copier-coller le code HTML dans le nœud.js et NodeCanvas: https://github.com/LearnBoost/node-canvas
PHP (avec l'opacité):
<?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>
Comment êtes-vous rendre compte de la "opacité de 35%" dans vos commandes ImageMagick? – DarenW
Afin d'obtenir cela à l'écart, j'ai créé un fichier png transparent avec la couleur et réduit l'opacité à 35%. Donc, j'essaie pratiquement de mélanger deux fichiers image existants. – viorel
Pouvez-vous fournir les images suivantes: (1) image originale; (2) image de couche de couleur à une opacité de 35%; (3) image du résultat attendu (ce que crée photoshop) –