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
Related
I am trying to convert a color pdf to black and white pdf. I am going to use the pdf to send it in fax and I am using Twilio and it is explicitly converting the color pdf to monochrome pdf however, I want to do it in my server-side to be able to preview the outcome.
As I have Imagick, and found some topics mainly on Imagick, I wanted to give it a try but I couldn't find the necessary class in Imagick. I found some for grayscale but fax is explicitly black & white (monochrome), so no identical to the fax case.
Closest I could find was:
->transformImageColorSpace(\Imagick::COLORSPACE_SRGB) and
->setImageColorSpace(Imagick::COLORSPACE_GRAY)
But these are grayscale, not monochrome.
Also, on Imagick formums, I found these commands but I don't want to execute with shell_exec (as I've read there are couple of down-sides)
// I am pretty sure this one should work, but couldn't find how to do it in php:
convert -density 288 in.pdf -resize 25% out.png
// thus this one:
convert -density 600 in.pdf -threshold 15% -type bilevel -compress fax out.pdf
// Also found this one:
convert -density 288 image.pdf -resize 25% -threshold 50% -type bilevel image.tiff
How to achieve what I am trying to achieve in php either using above commands or any other php compatible way? How does Twilio do it?
Update:
Expected output (how Twilio does it):
Using below answer:
$img = new Imagick('path/to/document.pdf');
$img->quantizeImage(2, // Number of colors
Imagick::COLORSPACE_GRAY, // Colorspace
50, // Depth tree
TRUE, // Dither
FALSE); // Error correction
$img->writeImage('path/to/output.png')
Update 2:
Using $img->quantizeImage(2, Imagick::COLORSPACE_GRAY, 1, TRUE, FALSE);
Use Imagick::quantizeImage to monochrome the PDF
$img = new Imagick('path/to/document.pdf');
$img->quantizeImage(2, // Number of colors
Imagick::COLORSPACE_GRAY, // Colorspace
1, // Depth tree
TRUE, // Dither
FALSE); // Error correction
$img->writeImage('path/to/output.png')
For example...
$img = new Imagick();
$img->newPseudoImage(300, 300, 'radial-gradient:');
$img->quantizeImage(2, Imagick::COLORSPACE_GRAY, 1, TRUE, FALSE);
$img->writeImage('output.png');
**
Or increase color count to allow gray values between black & white. See the usage docs Color Quantization and Dithering for great examples.
$img = new Imagick();
$img->newPseudoImage(300, 300, 'radial-gradient:');
$img->quantizeImage(255, Imagick::COLORSPACE_GRAY, 1, TRUE, FALSE);
$img->writeImage('output.png');
There are images I want to remove the background of (or set it to transparent rather). For that reason, I have tested a bash imagick command that looks like this:
convert test.jpg -alpha set -channel RGBA -bordercolor white -border 1x1 -fuzz 2% -fill none -floodfill +0+0 white -shave 1x1 test.png
Because I need to use this in my php script, I need to translate this over now. What I've come up with is this:
$imagick->readImage($path);
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$imagick->borderImage('white', 1, 1);
$imagick->floodFillPaintImage('transparent', 20, 'white', 0, 0, false);
$imagick->shaveImage(1, 1);
$imagick->writeImage(str_replace('.jpg', '.png', $path));
From what I can tell, it generates the image and it removes big parts of the background. But the fuzziness setting seems to be ignored.
The results of this script are always the same as when I use -fuzz 0% in the command prompt, no matter what fuzziness value I pass. Am I doing something wrong or is there a bug (which would leave me searching for another script capable of doing this)?
Am I doing something wrong or is there a bug
Let's call it a documentation error.
Imagick::floodFillPaintImage (and most of the other functions that take a fuzz parameter) need to have the fuzz scaled to the quantum range that ImageMagick was compiled with. e.g. for ImageMagick compiled with 16 bits of depth, the quantum range would be (2^16 - 1) = 65535
There is an example at http://phpimagick.com/Imagick/floodFillPaintImage
$imagick->floodFillPaintImage(
$fillColor,
$fuzz * \Imagick::getQuantum(),
$targetColor,
$x, $y,
$inverse,
$channel
);
So the reason that you're seeing the output image be the same as if you had passed in 0 fuzz, is that ImageMagick is interpreting the value of 2 that you are passing in as 2 / 65535 .... which is approximately zero.
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.
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.
I've got the following two commands for imagemagick in the command line:
convert in.png container-mask.png +matte -compose CopyOpacity -composite out.png
composite container.png out.png -compose multiply final.png
Those two commands include 3 files:
in.png: the file that should be masked
container-mask.png: the back/white mask of the areas of container.png where in.png should be visible
container.png image that includes the container for in.png, the one that has been masked in black/white with container-mask.png
Now the question is how to transpose this commands into PHP calls. I've played around quite a bit, but I can't make sense of the API at http://php.net/manual/en/book.imagick.php
Thanks and Bests,
Charly
I've found the answer. Well, that was not too complicated after all:
$original = new Imagick("in.png");
$mask = new Imagick("container-mask.png");
$container = new Imagick("container.png");
$mask->setImageMatte(0);
$original->compositeImage($mask, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
$container->compositeImage($original, Imagick::COMPOSITE_MULTIPLY, 0,0);
$container->setImageFormat( "png" );
echo $container;