Imagick - Composite image mask through PHP - php

I'm trying to recreate a script that uses the ImageMagick command "convert" to compose an image. But I want to do the same in PHP using Imagick:
convert ./a.png ./b.png ./c.png -virtual-pixel transparent -channel rgba -alpha on -background transparent -define compose:args=300x53.033 -compose displace -composite ./final.png
a.png
b.png
c.png
Result:
Where a.png is a base image, b.png is the overlay and c.png is the gray-scale 'mask'. For achieving this result, I've to execute all manipulations which are supported by the extension, write the image in disk and then execute this command through the exec function, which is a terrible workaround.
The following snippet does not work:
$a = new Imagick('../a.png');
$b = new Imagick('../b.png');
$c = new Imagick('../c.png');
$a->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
$a->setImageBackgroundColor(new ImagickPixel('none'));
$a->setOption('compose:args', '300x53.033');
$a->compositeImage($c, Imagick::COMPOSITE_DSTIN, 0, 0, Imagick::CHANNEL_ALPHA);
$a->compositeImage($b, Imagick::COMPOSITE_DISPLACE, 0, 0);
Result:
Expected:
I would like to achieve this same result using only Imagick PHP.

Related

Convert CMYK PDF to SRGB JPG using PHP and Imagick

I'm trying to convert the first page of PDF documents to a JPEG using Imagick and PHP. As long as the colorspace of the PDF is SRGB, conversion succeeds and the resulting images have correct colors. However, if the PDF has a CMYK colorspace, after conversion, the image colors are off (much brighter or darker).
I'm currently using the following software:
PHP 7.4.3
ImageMagick 6.9.10-23 Q16 x86_64 20190101 (deb package)
Ghostscript 9.50 (2019-10-15)
I'm working on WSL2 on Windows 10.
My test PDF can be found here.
Because I was not happy with the resulting conversions, I firstly tried to see if it is possible to do a successful conversion using Imagick cli. After lots of trial and error, I found that the following command yielded the best result:
convert -density 300 -colorspace srgb input.pdf[0] -layers flatten -strip output.jpg
Result:
I then rewrote the command to PHP:
$input = 'input.pdf';
$output = 'output.pdf';
$image = new Imagick();
$image->setResolution(300, 300);
$image->readImage("{$input}[0]");
$image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
$image = $image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('jpeg');
$image->stripImage();
$image->writeImage($output);
$image->destroy();
Result:
The result of the PHP code is not the same as the result of the CLI version and the original PDF. The result is the same as if I would run te following CLI command:
convert -density 300 input.pdf[0] -colorspace srgb -layers flatten -strip output.jpg
The command looks almost the same, however the transforming of the color space happens later.
My question is: what step do I miss in my PHP code to achieve the same result as the command
convert -density 300 -colorspace srgb input.pdf[0] -layers flatten -strip output.jpg
Additional information:
I also tried using color profiles to do the colorspace transformations. Instead of
$image->transformImageColorspace(Imagick::COLORSPACE_SRGB);
I used
$cmyk = file_get_contents('USWebCoatedSWOP.icc');
$rgb = file_get_contents('sRGB_v4_ICC_preference.icc');
$image->profileImage('icc', $cmyk);
$image->profileImage('icc', $rgb);
Besides these two profiles, I also tried combinations of other CMYK (CoatedFOGRA39, JapanColor2001Coated...) and SRGB (AdobeRGB1998, AppleRGB, sRGB_v4_ICC_preference_displayclass...) profiles.
However, I couldn't find a profile combination that came close to the result of the CLI output and the original PDF file.
Thanks to #fmw42, I was able to fix my issue. To fix it, set the color space using setColorSpace() before reading in the pdf.
$input = 'input.pdf';
$output = 'output.pdf';
$image = new Imagick();
$image->setResolution(300, 300);
$image->setColorSpace(Imagick::COLORSPACE_SRGB); // Add this line
$image->readImage("{$input}[0]");
// $image->transformImageColorspace(Imagick::COLORSPACE_SRGB); // You don't need this line
$image = $image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$image->setImageFormat('jpeg');
$image->stripImage();
$image->writeImage($output);
$image->destroy();

ImageMagick displacement does not work as expected

I'm using latest ImageMagick with PHP and this is my code:
$diplacementmap = new Imagick('displacement.png');
$android = new Imagick('android.png');
$android->compositeImage($diplacementmap, Imagick::COMPOSITE_DISPLACE, 0, 0);
echo $android;
For some reason, I'm not getting expected results. Why could it be?
Both files separately:
Android - https://i.stack.imgur.com/bYbYX.png
Displacement map - https://i.stack.imgur.com/6g4v9.png
Set your displacement values to 10,10 in ImageMagick or Imagick. When you set the arguments to 0,0, you may be getting some larger default.
For example in command line:
Input:
Displacement map:
convert android.png displacement.png -define compose:args=10,10 -compose displace -composite result10.png
or at 20
convert android.png displacement.png -define compose:args=20,20 -compose displace -composite result20.png

Alpha channel failing after Imagick update

I have an application that frequently uses Imagick's PHP module to recolor and composite images. I recently upgraded the server's software from PHP 5.4 to PHP 7.0 and subsequently upgraded Imagick as well. I am now running ImageMagic 7.0.3 with the module 3.4.3. I have verified this in my site's phpinfo() and with the server command convert -version. The update for both was done in cPanel's WHM.
I use the following function to color an image using a given hex while preserving it's alpha shape and it's worked just fine up until updating.
protected function recolor($obj, $hex)
{
$obj->setImageAlphaChannel(Imagick::ALPHACHANNEL_EXTRACT);
$obj->setImageBackgroundColor('#' . str_replace('#', '', $hex));
$obj->setImageAlphaChannel(Imagick::ALPHACHANNEL_SHAPE);
return $obj;
}
Example of image successfully recolored (pre-update):
Example of the same input with current behavior (post-update):
I'm not really sure what's causing this new behavior. There is definitely color being applied, but it's not being applied in the shape of the supplied image. In addition, everything that got recolored after the base layer (the first layer that gets recolored in this process) doesn't seem to be showing at all, with the only layers showing above being ones that are not recolored.
Edit: Here is one of the original input images that gets recolored:
https://i.stack.imgur.com/iyaoo.png - Base Color
https://i.stack.imgur.com/5W1nr.png - Background (Composites under base)
https://i.stack.imgur.com/LUmWd.png - Lineart (Composites over base)
The composite process has been added below. First the function recolors all the applicable layer objects and then composites them and masks them to the "color" image (the "base" layer posted above in the shape of the wolf). Then that image is composited onto the background and the lines and logo added on top to create the final image.
$this->recolor($this->color, $baseHex);
$this->recolor($this->eyes, $eyesHex);
$this->recolor($this->eyebrows, $eyebrowsHex);
$this->recolor($this->pads, $padsHex);
$this->recolor($this->nose, $noseHex);
$this->recolor($this->claws, $clawsHex);
$this->recolor($this->tongue, $tongueHex);
$this->image->newImage(800, 598, new \ImagickPixel('transparent'));
$this->image->compositeImage($this->color, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->compositeMarkings();
$this->image->compositeImage($this->eyes, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->image->compositeImage($this->eyebrows, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->image->compositeImage($this->pads, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->image->compositeImage($this->nose, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->image->compositeImage($this->claws, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->image->compositeImage($this->tongue, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->mask($this->image, $this->color);
$this->bg->compositeImage($this->image, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->bg->compositeImage($this->lines, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->bg->compositeImage($this->logo, Imagick::COMPOSITE_DEFAULT, 0, 0);
$this->bg->setImageColorSpace(Imagick::COLORSPACE_SRGB);`
Imagemagick 7.0.3.0 has a bug and I can reproduce your issue in the command line.
magick 5W1nr.png \( iyaoo.png -alpha extract -background "#988777" -alpha shape \) -compose over -composite LUmWd.png -compose over -composite result7030.png
But it works fine in the current Imagemagick 7.0.6.9 in the command line.
magick 5W1nr.png \( iyaoo.png -alpha extract -background "#988777" -alpha shape \) -compose over -composite LUmWd.png -compose over -composite result7069.png
So I suggest you upgrade your version of Imagemagick

Convert convert syntax to PHP (ImageMagick)

I'm very new to the whole ImageMagick PHP library. I need to port this function to PHP using ImageMagick:
convert staticmap.png -gaussian-blur 10
\( -size 300x600 gradient:'rgba(255,255,255,0.9)'-'rgba(255,255,255,0.1)' -rotate 270 \)
-gravity north -compose over -composite output.png
or something that will give this output:
I can't use shell_exec like I always have because I'm running on Google App Engine and I don't think that function is enabled.
Is there an easier way to get the desired result? I want to blur it, too, but I think I can figure that part out.
EDIT: found a better way to do this on the command line. Hopefully that'll help conversion into PHP?
This is easy as all CLI options map directly to ImagickMagick.
<?php
/* convert */
// staticmap.png
$staticMap = new Imagick('staticmap.png');
// -gaussian-blur 10x0
$staticMap->gaussianBlurImage(10, 0);
// -size 300x600 gradient:'rgba(255,255,255,0.9)'-'rgba(255,255,255,0.1)'
$mask = new Imagick();
$mask->newPseudoImage(300, 600, 'gradient:rgba(255,255,255,0.9)-rgba(255,255,255,0.1)');
// -rotate 270
$mask->rotateImage('black', 270);
// -gravity north
$staticMap->setGravity(Imagick::GRAVITY_NORTH);
// -compose over -composite
$staticMap->compositeImage($mask, Imagick::COMPOSITE_OVER, 0, 0);
// output.png
$staticMap->writeImage('output.png');

What is Imagick equivalent of following Imagemagick command?

What is the Imagick equivalent of following Imagemagick command?
convert i.jpg -set colorspace RGB ( -clone 0 -fill black -colorize 100% ) ( -clone 0 colorspace gray -negate ) -compose blend -define compose:args=70,30 -composite o.jpg
I did the following Imagick commands, but it doesnt seem to be the same
$img = new Imagick("i.jpg");
$img->setImageColorspace(Imagick::COLORSPACE_RGB);
$clone1 = $img->clone();
$clone1->colorizeImage('black', 1.0);
$clone2 = $img->clone();
$clone2->setImageColorspace(Imagick::COLORSPACE_GRAY);
$clone2->negateImage(0);
$img->setOption('compose:args', '70x30');
$img->compositeImage($clone1, Imagick::COMPOSITE_BLEND, 0, 0);
$img->compositeImage($clone2, Imagick::COMPOSITE_BLEND, 0, 0);
$img->writeImage("o.jpg");
Where have I made mistakes?
For the most part, what you have is correct. Two minor issues will need to be addressed to match the CLI result.
First
Move the setOption line before reading any images in the Imagick object.
$img = new Imagick();
$img->setOption('compose:args', '70x30');
$img->readImage("i.jpg");
// ...
Secound
Colorizing the black color to 100% usually results in a solid black image. For whatever reason, there's no effect with MagickColorizeImage method. There's some work-arounds using ImagickDraw & background-color assignment listed within the comments on PHP.net's documentation. I'd recommend revisiting your first $clone1 image.

Categories