I wrote this function in PHP for changing a picture from one color to another, and I really like it. To me, it looks very realistic for my purposes, and it seems to take the brightness of the original image into consideration pretty well. But, it's slow. With the image sizes I'm working with, it takes about 23 seconds to recolor one image. I know the speed bump is from looping through every pixel, so I tried out some different combinations of Imagemagick functions through the Imagick class, but I couldn't find any combo that I liked the results of as much as my function. Is there a way, maybe using C, that I can write this as some sort of plugin for Imagemagick, and even make it available through the Imagick class so I don't have to run it through something like exec()? I also tried using Imagick's PixelIterator, and looked at fxImage, but those were just as slow if not worse.
public function colorize($img, $rgb) {
imagealphablending($img, true);
imagesavealpha($img, true);
// get width and height of image
$iwidth = imagesx($img);
$iheight = imagesy($img);
// loop through each pixel
for ($y=0; $y<$iheight; $y++) for ($x=0; $x<$iwidth; $x++) {
// get all original r, g, b, a values of the pixel
$orgb = imagecolorat($img, $x, $y);
$oa = ($orgb >> 24) & 0xFF;
$or = ($orgb >> 16) & 0xFF;
$og = ($orgb >> 8) & 0xFF;
$ob = $orgb & 0xFF;
// add up orginal rgb values and new rgb values
$total_original = $or + $og + $ob;
$total_new = $rgb[0] + $rgb[1] + $rgb[2];
// adjust brightness using average of rgb channels
$bright = -127 + $total_new/3;
// take average difference between new color's brightness and old color's brightness, add brightness adjustment to it, and round
$adjustment = round($bright + ($total_new - $total_original) / -3);
// set each channel to new color channels, add the adjustment, and make sure the result isn't less than 0 or greater than 255
$r = max(0,min($adjustment + $rgb[0],255));
$g = max(0,min($adjustment + $rgb[1],255));
$b = max(0,min($adjustment + $rgb[2],255));
// replace original pixel
$nrgb = imagecolorallocatealpha($img, $r, $g, $b, $oa);
imagesetpixel($img, $x, $y, $nrgb);
}
}
You can add so-called modules
to ImageMagick - though it is quite involved.
First off, you will need the source of ImageMagick and a development environment - e.g. compiler and make
etc.
Then you will need to build ImageMagick with the config flags --with-modules=yes
.
Once you have built your module, you need to actually invoke it on the commandline, or modify your PHP/Perl bindings to do so. In essence, it will look like this at the command line:
convert image.png -process YourModuleName <YourModuleParameters> output.png
There is more information here - although it is written for Windows, but you can follow it for Linux - I have built modules successfully on Mac OSX too.
This is more suggestions than an answer, but ran to long for a comment format.
From what I can tell, the color enhancement you're doing is essentially an adjustment on existing colors from a user supplied color. This reminds me of Fred's whiteblance script which leverages the -color-matrix
operator (or Imagick::colorMatrixImage()
in PHP).
This can be defined as...
new_red = (delta) * red + 0 * green + 0 * blue
new_green = 0 * red + (delta) * green + 0 * blue
new_blue = 0 * red + 0 * green + (delta) * blue
The goal would be to calculate the delta
of the user-supplied color, and directly adjust the matrix.
Example calculates the given color from white.
$delta_red = 0xFF/$rgb[0];
$delta_green = 0xFF/$rgb[1];
$delta_blue = 0xFF/$rgb[2];
$wand = new Imagick($img);
$wand->colorMatrixImage([$delta_red, 0, 0,
0, $delta_green, 0,
0, 0, $delta_blue]);
Another thing that occurred to me is the calculations in your example could just be adjustments to the intensity of the colors. Ignoring the given color for a minute, we can think of how to enhance the image in YCbCR colorspace. If we convert an image into a different colorspace, we can apply the same color enhancement by simply adjusting the luminance channel.
$wand = new Imagick('rose:');
// Change colorspace
$wand->setImageColorspace(Imagick::COLORSPACE_YCBCR);
// Get quantum to work with
$qr = $wand->getQuantumRange()['quantumRangeLong'];
// Perform enhancement calculations (hard-coded for example)
$blackPoint = 0.1 * $qr;
$whitePoint = 0.9 * $qr;
$gamma = 1;
$sigmod = 0.001;
$Y = Imagick::CHANNEL_RED; // Red is the first channel, or Y in this colorspace.
// Re-level Y
$wand->levelImage($blackPoint, $gamma, $whitePoint, $Y);
// Adjust contrast of Y
$wand->sigmoidalContrastImage($sigmod, 1, 1, $Y);
// Put it back RGB
$wand->setImageColorspace(Imagick::COLORSPACE_SRGB);
Hope that's helpful.