21

I have a png image with a transparent background and I want to convert it to a jpg image with a white background.

The code is basically this:

$image = new Imagick('transparent.png');
$image->writeImage('opaque.jpg');

But that creates a black background jpg. I've been struggling with the worst documentation ever trying to find a way to convert the transparent to white to no avail.

Edit: Well, I tried Marc B's idea and kind of got it to work.

$image = new Imagick('transparent.png');
$white = new Imagick();

$white->newImage($image->getImageWidth(), $image->getImageHeight(), "white");
$white->compositeimage($image, Imagick::COMPOSITE_OVER, 0, 0);
$white->writeImage('opaque.jpg');

$image->destroy();
$white->destroy();

The problem now is, it always causes the script to segfault.

fonso
  • 325
  • 1
  • 3
  • 10
  • Try doing the `destroy()` calls in the opposite order; first `$white`, then `$image`. `$white` might be hanging onto a pointer to `$image` because of the composition, then trying to refer to it during `destroy()` and segfaulting because it's gone away. – chaos Feb 11 '11 at 20:38
  • @chaos Unfortunately, it's not that. I'm having such a bad experience with this library. – fonso Feb 11 '11 at 20:40
  • Yeah, APIs to ImageMagick are kind of notoriously like that. I've been known to give up on native code and just write `system()` calls to `convert` on the command line. – chaos Feb 11 '11 at 20:42
  • It's almost funny how ridiculous the documentation on the PHP Imagick API is. – flu Feb 10 '14 at 11:17

10 Answers10

19

flattenImages() actually works.

But keep in mind that it doesn't modify the given \Imagick() object but returns a new one:

$image = new \Imagick('transparent.png');

// Need to use the result of $image->flattenImages() here!
$image = $image->flattenImages();
$image->writeImage('opaque.jpg');

flattenImages() defaults to the background color white. If you want to use another background color you have to set it before loading the image:

$image = new \Imagick();

// Needs to be called before the image is loaded!
$image->setbackgroundcolor('green');
$image->readimage('transparent.png');

$image = $image->flattenImages();
$image->writeImage('opaque.jpg');

In general the Imagick API is very sensible when it comes to the order of function calls (just like convert and its parameters on the command line) so always check if your order is correct.

Good luck!

Edit April 2016:

$image->flattenImages() was deprecated and should be replaced by:

$image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN)

It's hard to find exact informations about this, but it seems that this applies to PHP >= 5.6.

Thanks to vee for the tip!

flu
  • 14,307
  • 8
  • 74
  • 71
  • This explains what is actually going on. – likeitlikeit May 20 '14 at 10:23
  • the result of the flattenImages() worked for me. thanks – Stanislas Nichini Feb 26 '15 at 09:27
  • 2
    Now, flattenImages() is deprecated. use $image->mergeImageLayers(\Imagick::LAYERMETHOD_FLATTEN) instead. – vee Apr 18 '16 at 03:03
  • This worked for me but I first forgot to reassign the `$image` variable with the result of `flattenImages()`. – cjohansson Feb 27 '17 at 14:50
  • If you want to reassign the `$image`, you can use `setImage()` method: `$image->setImage($image->mergeImageLayers(Image::LAYERMETHOD_FLATTEN));` – Marek Skiba Apr 17 '18 at 17:11
  • Does PHP (or imagemagick?) do so some sort of auto detection when it comes to setting transparent background color when re-saving a PNG to JPG if setImageBackgroundColor is not explicitly called? I came across some PNG images where doing a simple conversion to JPG leads to some part of the background being black with color streaks in them, while the rest of the background turns white. – georaldc Dec 04 '20 at 19:03
9

Try:

$image = new Imagick('transparent.png');
$image->setImageMatte(true);
$image->setImageMatteColor('white');
$image->setImageAlphaChannel(Imagick::ALPHACHANNEL_OPAQUE);
$image->writeImage('opaque.jpg');
chaos
  • 122,029
  • 33
  • 303
  • 309
  • 1
    copy/paste the image on top of a white image of the same size? bit more work/overhead, but should work. – Marc B Feb 11 '11 at 19:48
  • @Marc B's idea seems like it should work if my last attempt doesn't. (Should write that as an answer, Marc B. :) – chaos Feb 11 '11 at 20:01
  • Just a random thought. chaos has done a lot more work on this than I have. he deserves the credit. – Marc B Feb 11 '11 at 20:11
  • @Marc B: I don't really agree. Actually solving the problem counts for more than effort. :) – chaos Feb 11 '11 at 20:12
  • @Marc B: I tried that and it does convert the image properly, but it causes the script to segfault. Description updated with the new code. – fonso Feb 11 '11 at 20:35
  • Since you're not doing anything fancy, just pasting one image onto another, there are the GD functions which could do the trick. I haven't used Imagick at all in PHP as GD usually met my needs. – Marc B Feb 11 '11 at 21:47
8

I ran into the same problem when converting PDFs to PNGs, and I used flattenImages().

        //get the first page of the PDF
        $im = new imagick( $file.'[0]' );

        //set the background to white
        $im->setImageBackgroundColor('white');

        //flatten the image
        $im = $im->flattenImages(); 

        //do the rest of the image operations
        $im->setResolution( 181, 181 );
        $im->setCompressionQuality(100);
        $im->resizeImage ( 181, 181,  imagick::FILTER_LANCZOS, 1, TRUE);
        $im->setImageFormat('png');
        $imageName = $title.'_thumb.png';
gmjordan
  • 314
  • 3
  • 7
6
$image = new Imagick('transparent.pdf');
$image->setImageType (imagick::IMGTYPE_TRUECOLOR);
$image->writeImage('opaque.tif');

did for me!

(instead of the formerly imagick::IMGTYPE_TRUECOLORMATTE)

Klaus Wendel
  • 69
  • 1
  • 1
1

You can try it by changing Imagick constant as shown below

//$image will conatains image which needs background to be transparent
$white = new Imagick();

$white->newImage($image->getImageWidth(), $image->getImageHeight(), new ImagickPixel( "white" ));
$white->compositeimage($image, Imagick::COMPOSITE_DEFAULT, $x1OfTransparentImage, $y1OfTransparentImage,);
$white->flattenImages();
$white->writeImage('opaque.jpg');    

$white->destroy();
Poonam
  • 4,591
  • 1
  • 15
  • 20
1

Try the following, it works for me:

$im = new Imagick('trans.png');
$im->setimagebackgroundcolor('white');
$im = $im->flattenimages();

$im->writeimage('transToWhite.jpg');

Hope that helps!

Frank Sebastià
  • 107
  • 2
  • 9
1

Regarding the issue with segfault I ran into the same issue.
Apparently $image->writeImage('somename') destroys $image or the reference to it.

I was running into the same issue. The way I got around it was by removing the call to destroy on the object that I had finished writing. Seems sloppy but that solved the issue with the segfault

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
1

Try this one:

$white->newImage($image->getImageWidth(), $image->getImageHeight(), "transparent");
mtk
  • 13,221
  • 16
  • 72
  • 112
0

Segfault issue: I had a similar problem (script kept giving me segfault, even when the image was properly processed and written), the solution I found came after checking bug reports, see: https://bugs.php.net/bug.php?id=61122

Knowing that, try adding
$white->setResourceLimit(6, 1); // 6 means "limit threads to"
before the problematic line (in my case I had to put it before $im->resizeImage(...);)

aesede
  • 5,541
  • 2
  • 35
  • 33
0

I had a situation where I was trying to replace transparent background with white (but keep as png). Tried several different methods (including setImageAlphaChannel with setImageBackgroundColor). Combining OP's use of compositeImage I came up with this (hopefully helpful to someone else):

$pic = new Imagick($filelocation); //specify file name
$pic->setResourceLimit(6, 1);
if ($pic->getImageAlphaChannel()) {
    $white = new Imagick();
    $white->newImage($pic->getImageWidth(), $pic->getImageHeight(), "white");
    $white->compositeImage($pic, Imagick::COMPOSITE_OVER, 0, 0);
    $pic = clone $white;
    $white->destroy();
    $pic->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
}
//do more things with $pic
Phil W.
  • 81
  • 5