Crop bug in PHP Imagick? - php

I was trying to crop the animated gif and in the output i'm getting the same sized image, but cropped.
A lot of empty space is filled with canvas.
For example i had animated gif 600x100, but have requested 100x100 crop, on the output i'm getting 600x100 image with cropped image and empty space.
Someone knows the solution for this issue?
$gif = new Imagick($s['src']);
foreach($gif as $frame){
$frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
}
$gif->writeImages($s['dest_path'] .'/'. $fullname,true);

I've been having the same problem as you, and I found the solution was using the coalesceimages function.
Here's a working example for crop and resize an animated gif in php with Imagick:
<?php
// $width and $height are the "big image"'s proportions
if($width > $height) {
$x = ceil(($width - $height) / 2 );
$width = $height;
} elseif($height > $width) {
$y = ceil(($height - $width) / 2);
$height = $width;
}
$image = new Imagick(HERE_YOU_PUT_BIG_IMAGE_PATH);
$image = $image->coalesceImages(); // the trick!
foreach ($image as $frame) {
$frame->cropImage($width, $height, $x, $y); // You crop the big image first
$frame->setImagePage(0, 0, 0, 0); // Remove canvas
}
$image = $image->coalesceImages(); // We do coalesceimages again because now we need to resize
foreach ($image as $frame) {
$frame->resizeImage($newWidth, $newHeight,Imagick::FILTER_LANCZOS,1); // $newWidth and $newHeight are the proportions for the new image
}
$image->writeImages(CROPPED_AND_RESIZED_IMAGE_PATH_HERE, true);
?>
Code above is being used for generating thumbnails with same with and height.
You might change it the way you want.
Notice that when using $frame->cropImage($width, $height, $x, $y); you should put there the values you might need.
IE $frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
Of course that if you just want to crop instead of croping and resizing, just can do this:
$image = new Imagick(HERE_YOU_PUT_BIG_IMAGE_PATH);
$image = $image->coalesceImages(); // the trick!
foreach ($image as $frame) {
$frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
$frame->setImagePage(0, 0, 0, 0); // Remove canvas
}
Hope it helps!
Ps: sorry for my english :)

Often ImageMagick has a 'page' or working area, something like a background layer. It sounds like this is remaining after cropping the image (I had a confusing time working out some compositing and resizing behavior with the command line tool before...).
Checking out the PHP manual page for cropImage, I saw this comment:
Christian Dehning - 09-Apr-2010 10:57
When cropping gif-images (I had no problems with jpg and png images), the canvas is not removed. Please run the following command on the cropped gif, to remove the blank space:
$im->setImagePage(0, 0, 0, 0);

Related

Merge JPEG and PNG with transparent Background to one Image PHP

i have the following script for coping a JPEG and a PNG to an existing PNG called base.png. Within the function "transparent_background" i replace the white background with transparency. This function is the problem. Standlone the function is working with output directly in the browser. please see the comment out "//imagepng($img);". but if i return the $img out of the function its still a jpeg i think, thats why it isnt transparent. The second function is just for resize.
<?php
function transparent_background($img)
{
$img = imagecreatefromjpeg($img); //or whatever loading function you need
$colors= array("255","255","255");
$remove = imagecolorallocate($img, $colors[0], $colors[1], $colors[2]);
imagecolortransparent($img, $remove);
//imagepng($img);
return $img;
imagedestroy($img);
}
function resize($img, $w){
$img = imagecreatefromjpeg($img);
$ratio = imagesx($img)/imagesy($img);
if( $ratio > 1) {
$width = $w;
$height = $w/$ratio;
}
else {
$width = $w*$ratio;
$height = $w;
}
$dst = imagecreatetruecolor($width,$height);
imagecopyresampled($dst,$img,0,0,0,0,$width,$height,imagesx($img),imagesy($img));
return $dst;
imagedestroy($dst);
imagedestroy($img);
}
$h="https://images-eu.ssl-images-amazon.com/images/I/415zYwg2-TL.jpg";
$base = imagecreatefrompng("base.png");
$logo = imagecreatefrompng("fs_logo_line.png");
$pos1=resize($h,"730");
$pos1=transparent_background($h);
imagecopy($base,$pos1,0, 5, 0, 0, imagesx($pos1),imagesy($pos1));
imagecopy($base,$logo,0, 1136, 0,0,imagesx($logo),imagesy($logo));
imagepng($base);
?>
I think the problem is, that i get a jpeg back from the transparent_background function and thats why the image in $pos1 is not transparent. Any ideas how i can solve that? I have tried with ob_start & ob_get_contents but this also didn't work.
You can merge the two images together using the PHP GD2 library.
Example:
<?php
# If you don't know the type of image you are using as your originals.
$image = imagecreatefromstring(file_get_contents($your_original_image));
$frame = imagecreatefromstring(file_get_contents($your_frame_image));
# If you know your originals are of type PNG.
$image = imagecreatefrompng($your_original_image);
$frame = imagecreatefrompng($your_frame_image);
imagecopymerge($image, $frame, 0, 0, 0, 0, 50, 50, 100);
# Save the image to a file
imagepng($image, '/path/to/save/image.png');
# Output straight to the browser.
imagepng($image);
?>
Add imagealphablending($frame,true); before imagecopymerge() if you want to keep PNG frame transparancy over the image.

How to resize image keeping constraints php

I have made two GIFs to explain what I am trying to do. Where the grey border is the dimensions I am after (700*525). They are at the bottom of this question.
I want for all images that are larger than the given width and height to scale down to the border (from the centre) and then crop off the edges. Here is some code I have put together to attempt this:
if ($heightofimage => 700 && $widthofimage => 525){
if ($heightofimage > $widthofimage){
$widthofimage = 525;
$heightofimage = //scaled height.
//crop height to 700.
}
if ($heightofimage < $widthofimage){
$widthofimage = //scaled width.
$heightofimage = 700;
//crop width to 525.
}
}else{
echo "image too small";
}
Here are some GIFs that visually explain what I am trying to achieve:
GIF 1: Here the image proportions are too much in the x direction
GIF 2: Here the image proportions are too much in the y direction
image quality comparison for #timclutton
so I have used your method with PHP (click here to do your own test with the php) and then compared it to the original photo as you can see there is a big difference!:
Your PHP method:
(source: tragicclothing.co.uk)
The actual file:
(source: mujjo.com)
The below code should do what you want. I've not tested it extensively but it seems to work on the few test images I made. There's a niggling doubt at the back of mind that somewhere my math is wrong, but it's late and I can't see anything obvious.
Edit: It niggled enough I went through again and found the bug, which was that the crop wasn't in the middle of the image. Code replaced with working version.
In short: treat this as a starting point, not production-ready code!
<?php
// set image size constraints.
$target_w = 525;
$target_h = 700;
// get image.
$in = imagecreatefrompng('<path to your>.png');
// get image dimensions.
$w = imagesx($in);
$h = imagesy($in);
if ($w >= $target_w && $h >= $target_h) {
// get scales.
$x_scale = ($w / $target_w);
$y_scale = ($h / $target_h);
// create new image.
$out = imagecreatetruecolor($target_w, $target_h);
$new_w = $target_w;
$new_h = $target_h;
$src_x = 0;
$src_y = 0;
// compare scales to ensure we crop whichever is smaller: top/bottom or
// left/right.
if ($x_scale > $y_scale) {
$new_w = $w / $y_scale;
// see description of $src_y, below.
$src_x = (($new_w - $target_w) / 2) * $y_scale;
} else {
$new_h = $h / $x_scale;
// a bit tricky. crop is done by specifying coordinates to copy from in
// source image. so calculate how much to remove from new image and
// then scale that up to original. result is out by ~1px but good enough.
$src_y = (($new_h - $target_h) / 2) * $x_scale;
}
// given the right inputs, this takes care of crop and resize and gives
// back the new image. note that imagecopyresized() is possibly quicker, but
// imagecopyresampled() gives better quality.
imagecopyresampled($out, $in, 0, 0, $src_x, $src_y, $new_w, $new_h, $w, $h);
// output to browser.
header('Content-Type: image/png');
imagepng($out);
exit;
} else {
echo 'image too small';
}
?>
Using Imagick :
define('PHOTO_WIDTH_THUMB', 700);
define('PHOTO_HEIGHT_THUMB', 525);
$image = new Imagick();
$image->readImage($file_source);
$width = $image->getImageWidth();
$height = $image->getImageHeight();
if($width > $height){
$image->thumbnailImage(0, PHOTO_HEIGHT_THUMB);
}else{
$image->thumbnailImage(PHOTO_WIDTH_THUMB, 0);
}
$thumb_width = $image->getImageWidth();
$thumb_height = $image->getImageHeight();
$x = ($thumb_width - PHOTO_WIDTH_THUMB)/2;
$y = ($thumb_height - PHOTO_HEIGHT_THUMB)/2;
$image->cropImage(PHOTO_THUMB_WIDTH, PHOTO_THUMB_HEIGHT, $x, $y);
$image->writeImage($thumb_destination);
$image->clear();
$image->destroy();
unlink($file_source);
I have used GD library to accomplish the resize. Basically what I did is, I calculated the image dimension and then resized the image to dimension 700x525 from the center.
<?php
/*
* PHP GD
* resize an image using GD library
*/
//the image has 700X525 px ie 4:3 ratio
$src = 'demo_files/bobo.jpg';
// Get new sizes
list($width, $height) = getimagesize($src);
$x = 0;
$y = 0;
if($width < $height){
$newwidth = $width;
$newheight = 3/4 * $width;
$x = 0;
$y = $height/2 - $newheight/2;
}else{
$newheight = $height;
$newwidth = 4/3 * $height;
$x=$width/2 - $newwidth/2;
$y=0;
}
$targ_w = 700; //width of the image to be resized to
$targ_h = 525; ////height of the image to be resized to
$jpeg_quality = 90;
$img_r = imagecreatefromjpeg($src);
$dst_r = ImageCreateTrueColor( $targ_w, $targ_h );
imagecopyresampled($dst_r,$img_r,0,0,$x,$y,$targ_w,$targ_h,$newwidth,$newheight);
header('Content-type: image/jpeg');
imagejpeg($dst_r,null,$jpeg_quality);
exit;
?>
i used http://phpthumb.sourceforge.net to have a beutiful solution also with transparent curved edges.
this is an alternative route to solution, might suit someone's need with little configuration.

CSS - Transparent bg of image not rendering on website

I'm trying to redesign my site so that my original square, tile-based rendering of images can be more of a cutout of the image... to get rid of that grid pattern.
Here's how it looked originally...
Here's a rough mock-up of what I'm going for:
So I resaved an image thumbnail with a transparent background... I just want the dog to show, and the square is transparent which will show the site's background underneath.
Yet when I render it on the page, it has this black background.
I've checked my CSS to see if there is some sort of img class, or class for the rendered comics... or even the bootstrap to see where there may be a background-color being assigned to black (and also searched for hex code 000000), but didn't find one...
Do you know why this may be happening?
Thanks!
EDIT: I've just noticed something...
My logo at the top renders with a transparent background... and the element is a png file... therefore, its MIME type is image/png.
I'm using a thumbnailing script to make the thumbnails smaller, but now the element is of thumber.php, which puts it as MIME type image/jpeg.
So I guess it's my thumbnailing script that changing the MIME type.
So I checked it, and it's creating the file as a jpeg
//imagejpeg outputs the image
imagejpeg($img);
Is there a way to change it so that the resampled image is output as a png?
Thumbnailing script:
<?php
#Appreciation goes to digifuzz (http://www.digifuzz.net) for help on this
$image_file = $_GET['img']; //takes in full path of image
$MAX_WIDTH = $_GET['mw'];
$MAX_HEIGHT = $_GET['mh'];
global $img;
//Check for image
if(!$image_file || $image_file == "") {
die("NO FILE.");
}
//If no max width, set one
if(!$MAX_WIDTH || $MAX_WIDTH == "") {
$MAX_WIDTH="100";
}
//if no max height, set one
if(!$MAX_HEIGHT || $MAX_HEIGHT == "") {
$MAX_HEIGHT = "100";
}
$img = null;
//create image file from 'img' parameter string
$img = imagecreatefrompng($image_file);
//if image successfully loaded...
if($img) {
//get image size and scale ratio
$width = imagesx($img);
$height = imagesy($img);
//takes min value of these two
$scale = min($MAX_WIDTH/$width, $MAX_HEIGHT/$height);
//if desired new image size is less than original, output new image
if($scale < 1) {
$new_width = floor($scale * $width);
$new_height = floor($scale * $height);
$tmp_img = imagecreatetruecolor($new_width, $new_height);
//copy and resize old image to new image
imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
imagedestroy($img);
//replace actual image with new image
$img = $tmp_img;
}
}
//set the content type header
header("Content-type: image/png");
//imagejpeg outputs the image
imagealphablending($img, false);
imagesavealpha($img, true);
imagepng($img);
imagedestroy($img);
?>
You will need to make some changes in the image generator and see if that works out for you.
The crucial changes are within the setting of the header and the method of image generation. You will be looking for these following two
header('Content-Type: image/jpeg');
change to:
header('Content-Type: image/png');
imagejpeg($im);
change to:
imagepng($im)
When dealing with png images with an alpha channel you should take a few extra steps.
Before spitting it out with imagepng(), these lines will need to be added.
imagealphablending($img, false);
imagesavealpha($img, true);
This information can be found on php.net
Edit:
Try with these alterations to this code:
if($scale < 1) {
$new_width = floor($scale * $width);
$new_height = floor($scale * $height);
$tmp_img = imagecreatetruecolor($new_width, $new_height);
imagealphablending($tmp_img,true); // add this line
//copy and resize old image to new image
imagecopyresampled($tmp_img, $img, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
$img = $tmp_img;
// remove line here
}
}
header("Content-type: image/png");
imagesavealpha($img, true);
imagepng($img);
imagedestroy($img);
imagedestroy($tmp_img); // add this line here
Basically you create new layers and put these together. For each layer you will need to set the alpha blending. I was successful in creating alpha images. Let me know what your findings are .. :-) ..

Photo color loss after watermarking

Having a few teething problems watermarking a photo. It all works fine apart from the watermarked photo's colors become duller than they should be - very noticeable in-fact.
I'm using imagecopyresized to do my watermarking, as this specifically allows me to use PNG-24 watermarks, the others do not. I know the colors are usually OK, as I have just used readfile($url) as a test, and the photos are perfect.
Here is my script:
<?php
// get parent and watermark images & sizes
$image = imagecreatefromjpeg($url);
$imageSize = getimagesize($url);
$watermark = imagecreatefrompng('watermark.png');
$watermark_o_width = imagesx($watermark);
$watermark_o_height = imagesy($watermark);
// calculate new watermark width and position
if ($imageSize[0] > $imageSize[1] || $imageSize[0] == $imageSize[1]) {
$leftPercent = 23;
} else {
$leftPercent = 7;
}
$leftPixels = ($imageSize[0]/100)*$leftPercent;
$newWatermarkWidth = $imageSize[0]-$leftPixels;
$newWatermarkHeight = $watermark_o_height * ($newWatermarkWidth / $watermark_o_width);
// place watermark on parent image, centered and scaled
imagecopyresized(
$image,
$watermark,
$imageSize[0]/2 - $newWatermarkWidth/2,
$imageSize[1]/2 - $newWatermarkHeight/2,
0,
0,
$newWatermarkWidth,
$newWatermarkHeight,
imagesx($watermark),
imagesy($watermark)
);
// print
imagejpeg($image);
// destroy
imagedestroy($image);
imagedestroy($watermark);
?>
How can I stop this from happening? I'm reading about imagecreatetruecolor, does that solve the issue? I'm Googling "imagecreatetruecolor color loss photos" and variations but nobody really talks about this issue. If I do need this function, where would I add that to this script?
This has totally thrown a spanner in the works for me and would love for somebody to tell me where to stick it (not literally).
Here is an example of the color loss. The preview image should be exactly the same colors as the thumbnail. The thumbnails are created using readfile() whereas the previews are created using imagecreatefromjpeg and imagecopresized.
This example code works fine, by using the same characteristics as your images:
Original JPG: dark background; beautiful girl; red dress.
Watermark PNG: transparent background; text; gray color.
<?php
// Path the the requested file (clean up the value if needed)
$path = $url;
// Load image
$image = imagecreatefromjpeg($path);
$w = imagesx($image);
$h = imagesy($image);
// Load watermark
$watermark = imagecreatefrompng('watermark.png');
$ww = imagesx($watermark);
$wh = imagesy($watermark);
// Merge watermark upon the original image (center center)
imagecopy($image, $watermark, (($w/2)-($ww/2)), (($h/2)-($wh/2)), 0, 0, $ww, $wh);
// Output the image to the browser
header('Content-type: image/jpeg');
imagejpeg($image);
// destroy both images
imagedestroy($image);
imagedestroy($watermark);
// kill script
exit();
?>
Left: Output Image | Right: Original Image
Note:
The output image was compressed several times until: Original -> PHP Output -> GIMP -> Here.
After much testing, I came to the conclusion that PHP's GD Image does not support color profiles on the images that are being watermarked. I am now using Imagick and the colors are perfect.

How can I overlay a watermark on an already resampled image in PHP (using GD)?

Here's my current code:
define('IMG_WIDTH', (isset ($_GET['width'])) ? (int) $_GET['width'] : 99);
define('IMG_HEIGHT', (isset ($_GET['height'])) ? (int) $_GET['height'] : 75);
$image = imagecreatefromjpeg($_GET['image']);
$origWidth = imagesx($image);
$origHeight = imagesy($image);
$croppedThumb = imagecreatetruecolor(IMG_WIDTH, IMG_HEIGHT);
if ($origWidth > $origHeight)
{
$leftOffset = ($origWidth - $origHeight) / 2;
imagecopyresampled($croppedThumb, $image, 0, 0, $leftOffset, 0, IMG_WIDTH, IMG_HEIGHT, $origHeight, $origHeight);
}
else
{
$topOffset = ($origHeight - $origWidth) / 2;
imagecopyresampled($croppedThumb, $image, 0, 0, 0, $topOffset, IMG_WIDTH, IMG_HEIGHT, $origWidth, $origWidth);
}
It basically takes an image and re-sizes it to create a thumbnail. It works quite nicely. What I would like to do now is add a watermark to the bottom right corner. I've seen the imagecopymerge function used for this... However, that doesn't seem to allow me to supply a resampled image as the source.
How can I take my already modified image and add a watermark? :/
I've thought of saving the image to /tmp and then unlink()'ing it once I've added the watermark but that seems like a bit of a mess...
You can use $croppedThumb as the first argument to imagecopymerge. You don't have to save the image first.

Categories