Resize image and place in center of canvas - php

I'm trying to do the following in iMagick and I can't get it to work:
Check if image is over 390 pixels high, if it is then resize it to 390 pixels high, if it isn't keep the dimensions.
Add a white canvas, 300px wide by 400px high and then place the image into the center of that.
My Code is:
$im = new Imagick("test.jpg");
$imageprops = $im->getImageGeometry();
$width = $imageprops['width'];
$height = $imageprops['height'];
if($height > '390'){
$newHeight = 390;
$newWidth = (390 / $height) * $width;
}else{
$newWidth = $imageprops['width'];
$newHeight = $imageprops['height'];
}
$im->resizeImage($newWidth,$newHeight, Imagick::FILTER_LANCZOS, 0.9, true);
$canvas = new Imagick();
$canvas->newImage(300, 400, 'white', 'jpg');
$canvas->compositeImage($im, Imagick::COMPOSITE_OVER, 100, 50);
$canvas->writeImage( "test-1.jpg" );
When the images are produced the large ones are scaled to 388 pixels high for some reason and the small ones are left to their original dimensions.
The placing on the canvas is always incorrect although does work on the large images with 100,50 added to the composite image.
Most of the images are tall and thin however there are a few that are wider than they are tall.
Any ideas where I am going wrong ?
Thanks,
Rick

Mark's comments may be the better option. Extent respects the gravity, and ensures that the finial image will always be 300x400. For placing the image in the center using Imagick::compositeImage, you'll need to calculate the offset -- which is easy as you already have the width/height of the subject & canvas images.
$canvas = new Imagick();
$finalWidth = 300;
$finalHeight = 400;
$canvas->newImage($finalWidth, $finalHeight, 'white', 'jpg' );
$offsetX = (int)($finalWidth / 2) - (int)($newWidth / 2);
$offsetY = (int)($finalHeight / 2) - (int)($newHeight / 2);
$canvas->compositeImage( $im, imagick::COMPOSITE_OVER, $offsetX, $offsetY );

Related

How do I Resize images to fixed width & height while maintaing aspect ratio in PHP?

I am trying to batch resize images to the size of 250 x 250 in PHP
All source images are way bigger than 250 x 250 so that is helpful.
I want to maintain aspect ratio but make them all 250 x 250. I know that a portion of the image will be cropped off to do this. That is not an issue for me
The problem is that my current script only works on width and makes height according to aspect but sometimes, the image will now end up being let's say, 250 x 166. I can't use that.
In that cause It would need to be resized in the opposite manner (height to width)
How would the script have to look to always make the final image 250 x 250 without stretching. Again, I don't care if there is cropping. I assume there is going to be an else in the somewhere but this is way over my head now. I am more of a front end guy.
Any help would be great.
Below is just the relevant portion of my full script:
$width = 250;
$height = true;
// download and create gd image
$image = ImageCreateFromString(file_get_contents($url));
// calculate resized ratio
// Note: if $height is set to TRUE then we automatically calculate the height based on the ratio
$height = $height === true ? (ImageSY($image) * $width / ImageSX($image)) : $height;
// create image
$output = ImageCreateTrueColor($width, $height);
ImageCopyResampled($output, $image, 0, 0, 0, 0, $width, $height, ImageSX($image), ImageSY($image));
// save image
ImageJPEG($output, $destdir, 100);
$newWidth = 250;
$newHeight = 250;
// download and create gd image
$image = ImageCreateFromString(file_get_contents($url));
$width = ImageSX($image);
$height = ImageSY($image);
$coefficient = $newHeight / $height;
if ($newHeight / $width > $coefficient) {
$coefficient = $newHeight / $width;
}
// create image
$output = ImageCreateTrueColor($newWidth, $newHeight);
ImageCopyResampled($output, $image, 0, 0, 0, 0, $width * $coefficient, $height * $coefficient, $width, $height);
// save image
ImageJPEG($output, $destdir, 100);

PHP Imagick - Center Cropped image on top of another

I am just starting with PHP's Imageick library.
I start by cropping a users image like so:
$img_path = 'image.jpg';
$img = new Imagick($img_path);
$img_d = $img->getImageGeometry();
$img_w = $img_d['width'];
$img_h = $img_d['height'];
$crop_w = 225;
$crop_h = 430;
$crop_x = ($img_w - $crop_w) / 2;
$crop_y = ($img_h - $crop_h) / 2;
$img->cropImage($img_w, $img_h, $crop_x, $crop_y);
I now need to place the cropped image of 225 x 430 onto a new image at 500px x 500px in the center. The new image must have a transparent background. Like so (the grey border is visual only):
How can I do so? I have tried 2 options:
compositeImage()
$trans = '500x500_empty_transparent.png';
$holder = new Imagick($trans);
$holder->compositeImage($img, imagick::COMPOSITE_DEFAULT, 0, 0);
By making a transparent png with nothing on it at 500x500px i was hoping i could use compositeImage to put the image on top of that. It does this but doesn't keep the original size of the $holder but uses the 225x430 size
frameImage()
$frame_w = (500 - $w) / 2;
$frame_h = (500 - $h) / 2;
$img->frameimage('', $frame_w, $frame_h, 0, 0);
I create a border that makes up the remaining pixels of the image to make 500 x500px. I was hoping by leaving the first colour parameter blank it would be transparent but it creates a light grey background so isn't transparent.
How can I achieve this?
If you only want a transparent background you don't need a separate image file. Just crop the image and resize it.
<?php
header('Content-type: image/png');
$path = 'image.jpg';
$image = new Imagick($path);
$geometry = $image->getImageGeometry();
$width = $geometry['width'];
$height = $geometry['height'];
$crop_width = 225;
$crop_height = 430;
$crop_x = ($width - $crop_width) / 2;
$crop_y = ($height - $crop_height) / 2;
$size = 500;
$image->cropImage($crop_width, $crop_height, $crop_x, $crop_y);
$image->setImageFormat('png');
$image->setImageBackgroundColor(new ImagickPixel('transparent'));
$image->extentImage($size, $size, -($size - $crop_width) / 2, -($size - $crop_height) / 2);
echo $image;
Use setImageFormat to convert the image to PNG (to allow transparency), then set a transparent background with setImageBackgroundColor. Finally, use extentImage to resize it.

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.

PHP crop tall images

This is my code, but I dont know why but when I upload a vertical image it is resized and I get two black stripes on the sides. With wide images it works well most of the time but with some images I get black stripes as well. How can I fix it?
My purpose is to crop every image, horizontal just a hint to fit my box, and vertical I want to crop so the width is the same and they are cut no top and bottom.
$thumb_height = 200;
$thumb_width = 300;
$thumb = imagecreatetruecolor($thumb_width, $thumb_height);
if($width >= $height) {
// If image is wider than thumbnail (in aspect ratio sense)
$new_height = $thumb_height;
$new_width = $width / ($height / $thumb_height);
} else {
// If the thumbnail is wider than the image
$new_width = $thumb_width;
$new_height = $height / ($width / $thumb_width);
}
$output_filename_mid = 'uploads/'.IMG_L.$fileName;
imagecopyresampled($thumb,
$image,
0 - ($new_width - $thumb_width) / 2, // Center the image horizontally
0 - ($new_height - $thumb_height) / 2, // Center the image vertically
0, 0,
$new_width, $new_height,
$width, $height);
imagejpeg($thumb, $output_filename_mid, 85);
You're almost there. You've figured out that you need to determine the ratio between the old height and the destination height to resize the side that'll be cropped. However, you need to determine this in relation to the destination ratio.
if(($width / $height) > ($thumb_width / $thumb_height)) {
// If image is wider than thumbnail (in aspect ratio sense)
$new_height = $thumb_height;
$new_width = $width / ($height / $thumb_height);
} else {
// If the thumbnail is wider than the image
$new_width = $thumb_width;
$new_height = $height / ($width / $thumb_width);
}
I'm actually working with this a lot. Here is my code which is VERY similar to yours. Can you find the difference/problem yourself?
My code works 100% and does exactly what you need and ONLY that. Feel free to use it!
Variables explained: $imagewidth and $imageheight are naturally the original pixel sizes of the input image. $crop_ratio_w is crop ratio width and $crop_ratio_h is crop ratio height.
So for my code vs. your code: $crop_ratio_w = $thumb_width and $crop_ratio_h = $thumb_height
//do this if we can start cropping by width (there's enough space on height)
if (($imagewidth * $crop_ratio_h / $crop_ratio_w) < $imageheight){
//count new res
$new_width = $imagewidth;
$new_height = ($imagewidth * $crop_ratio_h / $crop_ratio_w);
//count the height difference, so that new image is cropped in HEIGHT center
$difference = ($imageheight - $new_height);
$y_offset = ($difference / 2);
//create new empty image
$croppedImage = imagecreatetruecolor($new_width, $new_height);
//copy wanted area to the new empty image
imagecopy($croppedImage, $originalImage, 0, 0, 0, $y_offset, $new_width, $new_height);
}
//else on previous condition -- do this if we have to start cropping by height (there's not enough space on height)
else{
//count new res
$new_width = ($imageheight * $crop_ratio_w / $crop_ratio_h);
$new_height = $imageheight;
//count the height difference, so that new image is cropped in WIDTH center
$difference = ($imagewidth - $new_width);
$x_offset = ($difference / 2);
//create new empty image
$croppedImage = imagecreatetruecolor($new_width, $new_height);
//copy wanted area to the new empty image
imagecopy($croppedImage, $originalImage, 0, 0, $x_offset, 0, $new_width, $new_height);
}

php gd: when i crop images through php some images come out smushed

here is the website im talking about
http://makeupbyarpi.com/portfolio.php
you'll notice some of the images are smushed width-wise.
the code i used is this:
$width="500";
$height="636";
$img_src = $_FILES['galleryimg']['tmp_name'];
$thumb = "../gallery/".rand(0,100000).".jpg";
//Create image stream
$image = imagecreatefromjpeg($img_src);
//Gather and store the width and height
list($image_width, $image_height) = getimagesize($img_src);
//Resample/resize the image
$tmp_img = imagecreatetruecolor($width, $height);
imagecopyresampled($tmp_img, $image, 0, 0, 0, 0, $width, $height, $image_width, $image_height);
//Attempt to save the new thumbnail
if(is_writeable(dirname($thumb))){
imagejpeg($tmp_img, $thumb, 100);
}
//Free memory
imagedestroy($tmp_img);
imagedestroy($image);
the images that get uploaded are huge sometimes 3000px by 2000px and i have php crop it down to 500 x 536 and some landscape based images get smushed. is there a formula i can use to crop it carefully so that the image comes out good?
thanks
You could resize and add a letterbox if required. You simply need to resize the width and then calculate the new height (assuming width to height ratio is same as original) then if the height is not equal to the preferred height you need to draw a black rectangle (cover background) and then centre the image.
You could also do a pillarbox, but then you do the exact same as above except that width becomes height and height becomes width.
Edit: Actually, you resize the one that is the biggest, if width is bigger, you resize that and if height is bigger then you resize that. And depending on which one you resize, your script should either letterbox or pillarbox.
EDIT 2:
<?php
// Define image to resize
$img_src = $_FILES['galleryimg']['tmp_name'];
$thumb = "../gallery/" . rand(0,100000) . ".jpg";
// Define resize width and height
$width = 500;
$height = 636;
// Open image
$img = imagecreatefromjpeg($img_src);
// Store image width and height
list($img_width, $img_height) = getimagesize($img_src);
// Create the new image
$new_img = imagecreatetruecolor($width, $height);
// Calculate stuff and resize image accordingly
if (($width/$img_width) < ($height/$img_height)) {
$new_width = $width;
$new_height = ($width/$img_width) * $img_height;
$new_x = 0;
$new_y = ($height - $new_height) / 2;
} else {
$new_width = ($height/$img_height) * $img_width;
$new_height = $height;
$new_x = ($width - $new_width) / 2;
$new_y = 0;
}
imagecopyresampled($new_img, $img, $new_x, $new_y, 0, 0, $new_width, $new_height, $img_width, $img_height);
// Save thumbnail
if (is_writeable(dirname($thumb))) {
imagejpeg($new_img, $thumb, 100);
}
// Free up resources
imagedestroy($new_img);
imagedestroy($img);
?>
Sorry it took a while, I ran across a small bug in the calculation part which I was unable to fix for like 10 minutes =/ This should work.

Categories