Resize uploaded image to proportion - php

I have been creating an image uploading site for a while now but have been stuck on a certain part of it. On the second page, once the upload has been completed, i have displayed the various links you would expect. However. I would like to add a thumbnail to the page, or even a bigger picture, but it needs to be a certain width. I have tried many times to write a little script to take the dimensions and apply some maths to it, but to no avail.
I would really like if someone had a script or an idea how to do the above. Remember all the image sizes uploaded are different and i would want a 50x50 image being blown up to 250x250.
<?php
function imgResize($width, $height, $target) {
if ($width > $height) {
$percentage = ($target / $width);
} else {
$percentage = ($target / $height);
}
$width = round($width * $percentage);
$height = round($height * $percentage);
return "width=\"$width\" height=\"$height\"";
}
?>
Thanks,
Harry

Simple math:
$original_width = 50;
$original_height = 50;
$width = 250;
$ratio = $width/$original_width;
$height = $original_height * $ratio;

Related

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.

how do i use imagick in php? (resize & crop)

I use imagick for thumbnail crop, but sometimes cropped thumbnails are missing top part of the images (hair, eyes).
I was thinking to resize the image then crop it. Also, I need to keep the image size ratio.
Below is the php script I use for crop:
$im = new imagick( "img/20130815233205-8.jpg" );
$im->cropThumbnailImage( 80, 80 );
$im->writeImage( "thumb/th_80x80_test.jpg" );
echo '<img src="thumb/th_80x80_test.jpg">';
Thanks..
This task is not easy as the "important" part may not always be at the same place. Still, using something like this
$im = new imagick("c:\\temp\\523764_169105429888246_1540489537_n.jpg");
$imageprops = $im->getImageGeometry();
$width = $imageprops['width'];
$height = $imageprops['height'];
if($width > $height){
$newHeight = 80;
$newWidth = (80 / $height) * $width;
}else{
$newWidth = 80;
$newHeight = (80 / $width) * $height;
}
$im->resizeImage($newWidth,$newHeight, imagick::FILTER_LANCZOS, 0.9, true);
$im->cropImage (80,80,0,0);
$im->writeImage( "D:\\xampp\\htdocs\\th_80x80_test.jpg" );
echo '<img src="th_80x80_test.jpg">';
(tested)
should work. The cropImage parameters (0 and 0) determine the upper left corner of the cropping area. So playing with these gives you differnt results of what stays in the image.
Based on Martin's answer I made a more general function that resizes and crops Imagick image to fit given width and height (i.e. behaves exactly as CSS background-size: cover declaration):
/**
* Resizes and crops $image to fit provided $width and $height.
*
* #param \Imagick $image
* Image to change.
* #param int $width
* New desired width.
* #param int $height
* New desired height.
*/
function image_cover(Imagick $image, $width, $height) {
$ratio = $width / $height;
// Original image dimensions.
$old_width = $image->getImageWidth();
$old_height = $image->getImageHeight();
$old_ratio = $old_width / $old_height;
// Determine new image dimensions to scale to.
// Also determine cropping coordinates.
if ($ratio > $old_ratio) {
$new_width = $width;
$new_height = $width / $old_width * $old_height;
$crop_x = 0;
$crop_y = intval(($new_height - $height) / 2);
}
else {
$new_width = $height / $old_height * $old_width;
$new_height = $height;
$crop_x = intval(($new_width - $width) / 2);
$crop_y = 0;
}
// Scale image to fit minimal of provided dimensions.
$image->resizeImage($new_width, $new_height, imagick::FILTER_LANCZOS, 0.9, true);
// Now crop image to exactly fit provided dimensions.
$image->cropImage($new_width, $new_height, $crop_x, $crop_y);
}
Hope this may help somebody.
My code. Please vote
// Imagick
$image = new Imagick($img);
// method 1 - resize to max width
if($type==1){
$image->resizeImage($newWidth,0,Imagick::FILTER_LANCZOS,1);
// method 2 - resize to max height
}else if($type==2){
$image->resizeImage(0,$newHeight,Imagick::FILTER_LANCZOS,1);
// method 1 - resize to max width or height
}else if($type==3){
if($image->getImageHeight() <= $image->getImageWidth()){
$image->resizeImage($newWidth,0,Imagick::FILTER_LANCZOS,1);
}else{
$image->resizeImage(0,$newHeight,Imagick::FILTER_LANCZOS,1);
}
// method 4 - resize and crop to center
}else if($type==4){
if($image->getImageHeight() <= $image->getImageWidth()){
$image->resizeImage(0,$newheight,Imagick::FILTER_LANCZOS,1);
}else{
$image->resizeImage($newwidth,0,Imagick::FILTER_LANCZOS,1);
}
$cropWidth = $image->getImageWidth();
$cropHeight = $image->getImageHeight();
$image->cropimage(
$newwidth,
$newheight,
($cropWidth - $newwidth) / 2,
($cropHeight - $newheight) / 2
);
}
$image->setImageFormat("jpeg");
$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->writeImages($newImg, true);
$image->clear();
$image->destroy();

PHP Resize image down and crop using imagemagick

I'm trying to downsize image uploaded by users at the time of upload.
This is no problem but I want to get specific with sizes now.
I'm looking for some advice on an algorithm im struggling to produce that will take any shape image - square or rectangle of any widths/heights and downsize it.
This image needs to be downsized to a target box size (this changes but is stored in a variable)..
So it needs to downsize the image so that both the width and height are larger than the width and height of the target maintaining aspect ratio. but only just..
The target size will be used to crop the image so there is no white space around the edges etc.
I'm looking for just the algorithm behind creating the correct resize based on different dimension images - I can handle the cropping, even resizing in most cases but it fails in a few so i'm reaching out.
I'm not really asking for PHP code more pseudo. Either is fine obviously.
Thanks Kindly.
Current code.. but I've gone through so many iterations this might not work at all anymore.. :P
$image = $image_orig->clone();
$wRatio = $imageprops['width'] / $dimensions[0]; // imageprops = original image dimens.
$hRatio = $imageprops['height'] / $dimensions[1]; // $dimensions[0] = new width
$maxRatio = max($wRatio,$hRatio); // $dimensions[1] = new height
var_dump('thumb');
$ratio = ($imageprops['width'] - $dimensions[0]) > ($imageprops['height'] - $dimensions[1]);
$shape = ($imageprops['width'] > $imageprops['height']);
$error = ($imageprops['width'] / $imageprops['height']);
if( $error < 0.95 || $error > 1.05 ) { // its NOT a square
if($shape){ // longer width
$scale = $imageprops['height'] / $dimensions[0];
var_dump('1');
$height = $imageprops['height'] / $scale;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[1];
var_dump('2');
$width = $imageprops['width'] / $scale;
$image->scaleImage($width, 0);
}
} elseif($ratio) { // is square
$scale = $imageprops['height'] / $dimensions[1];
$newWidth = $imageprops['width'] / $scale;
$extra = 0;
$height = $dimensions[1]+$extra;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[0];
$newHeight = $imageprops['height'] / $scale;
$extra = 0;
$width = $dimensions[0]+$extra;
$image->scaleImage($width, 0);
}
$image_size = $image->getImageGeometry();
$image->cropImage($dimensions[0], $dimensions[1],($image_size['width']-$dimensions[0])/2,($image_size['height']-$dimensions[1])/2);
Notice: I wrote this answer before the original poster edited his question to include things which clarified points which has since changed what I believed the original question was asking.
So, there are a few concepts and ideas that you can throw around to try and solve what it is you are intending to achieve. (Gravity Cropping, Content Aware Cropping, Content Aware Rescaling etc)
Because you are always decreasing the size of the original image, you are essentially just looking to "chop" out a section of the image. Very much like this:
The issue however, is that you frequently want to make sure that you select the best region of the image so that you don't crop on an irrelevant segment of the image. This is known as content-aware image cropping, and by just searching using "Content Aware Image Crop" you can find a wealth of information.
Anyway, moving on, depending on your exact use case, you might discover that actually you don't want to chop out anything form the image, and instead you want to "liquid scale and crop" the image, so that you do "Content Aware Resizing". The difference with content aware resizing is that the resizing ignores all aspect ratios of the image and instead looks at neighbouring edges and colors in order to resize the image in a fluidic way.
So, luckily enough, Imagick comes with it's very own [liquidRescaleImage][3] function.
You can use it like
$im->liquidRescaleImage(480, 260, 3, 18);
Using Liquid Rescale can rescale out quite nicely. Here is an example below which is anything but perfect, but I purposefully have created an example that isn't perfect so you can actually see that liquidRescale changes the composition of the image, rather than just adjusts the aspect ratio.
Original
Content Aware Liquid Rescale (450x350)
If however, you just want to scale an image, and keep the aspect ratio, you might want to do what sites such as Flickr do, where they make the images longestLength to be equal to a value. (Flickr for example has 6 or so different dimension sizes)
We resize your photos to more web-friendly dimensions. Each image has
a 75x75 and 150x150 square thumbnail and 100-, 240-, 320-, 500-, 640-
800*-, 1024-, 1600*-, and 2048*-pixel versions (that's the length of
the longest side), as well as your original file.
Basic Class that Replicates the Flickr Resize Policies...
<?php
class Scale {
public function getImageScale($x, $y, $longestLength, $allowDistort = false) {
//Set the default NEW values to be the old, in case it doesn't even need scaling
list($nx, $ny) = array($x, $y);
if ($x > $y) {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $x / $longestLength;
return array($longestLength, $y / $r);
} else {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $y / $longestLength;
return array($x / $r, $longestLength);
}
return array($nx, $ny);
}
}
And then if you were to pass in the Image Dimensions like this:
$originalImageX = 480;
$originalImageY = 260;
$scale = new Scale();
var_dump($scale->getImageScale($originalImageX,$originalImageY,120 ) );
/* Outputs
array(2) {
[0]=>
int(120)
[1]=>
int(65)
} */
Also, there is this Content Aware Cropping class on Git, that I have previously adapted the base class/Structure previously to use in my own project so I know that it works nicely. I'm not sure on the licencing of this class, but you can obviously take a look at it and slice out what works for you as I have done previously.
https://gist.github.com/2468935#file_content_aware_image.php
(And PS. Provide all the information in your questions straight up next time...)
Dunno if this is what you want, from my personal script library xD :
You only need to specify the desired $maxW $maxH and same for thumbnails, and it will downscale the image proportionally.
The script has two parts that are almost the same, one is for the thumbnails and one for the actual image.
list($w, $h) = getimagesize($file);
$maxW = 1024;
$maxH = 786;
$thumbMaxW = 138;
$thumbMaxH = 92;
$newW = $w;
$newH = $h;
if($w > $thumbMaxW OR $h > $thumbMaxH){
if($w>=$h){
$thumbW = $thumbMaxW;
$thumbRatio = $w/$thumbMaxW;
$thumbH = (int)($h/$thumbRatio);
} else {
$thumbH = $thumbMaxH;
$thumbRatio = $h/$thumbMaxH;
$thumbW = (int)($w/$thumbRatio);
}
} else {
$thumbW = $w;
$thumbH = $h;
}
--------------------------------------------
if($w > $maxW OR $h > $maxH){
if($w >= $h){
$newW = $maxW;
$reduction = $w/$newW;
$newH = (int) ($h/$reduction);
}
if($h > $w){
$newH = $maxH;
$reduction = $h/$newH;
$newW = (int)($w/$reduction);
}
} else {
$newW = $w;
$newH = $h;
}

How to properly resize large images in PHP

For a website I run, users can upload pictures they've drawn to a gallery. We create a thumbnail and a small view of that image to display to other users (clicking the small view image shows the full sized image).
With this in mind, I created a very simple resize script. In most cases this script works perfectly. However, I've come across a single odd case in which the script messes up entirely.
When running the file http://img191.imageshack.us/img191/2268/935full.png (1641x3121) through the script (which creates one thumbnail with a max width or height of 150 and another of 400) we get a perfect thumbnail http://img267.imageshack.us/img267/5803/935thumb.png (78x150) and a small view image sized properly, but which is cut off and stretched http://img28.imageshack.us/img28/4002/935show.png (211 x 400).
With that in mind, my question is: Is this a problem in PHP or a logic error? And how can I fix it?
Thank you for your time. The code I use to create these thumbnails is below.
<?php
/**
* Creates a thumbnail for any type of pre-existing image. Always saves as PNG image
*
* #param string - The location of the pre-existing image.
* #param string - The location to save the thumbnail, including filename and extension.
* #param int - The Maximum Width, Default of 150
* #param int - The Maximum Height, Default of 150
* #return bool - Success of saving the thumbnail.
*/
function imagecreatethumbnail($file,$output,$max_width = 150,$max_height = 150)
{
$img = imagecreatefromstring(file_get_contents($file));
list($width, $height, $type, $attr) = getimagesize($file);
if($height > $max_height || $width > $max_width)
{
if($width > $height)
{
$thumb_width = $max_width;
$thumb_height = ceil(($height * $thumb_width)/$width);
}
else
{
$thumb_height = $max_height;
$thumb_width = ceil(($width * $thumb_height)/$height);
}
} else {
$thumb_width = $width;
$thumb_height = $height;
}
imagesavealpha($img,true);
$thumb = imagecreatetruecolor($thumb_width,$thumb_height);
imagesavealpha($thumb,true);
imagealphablending($thumb,false);
imagecopyresampled($thumb,$img,0,0,0,0,$thumb_width,$thumb_height,$width,$height);
$return = imagepng($thumb,$output);
imagedestroy($img);
imagedestroy($thumb);
return $return;
}
Try this library and tell me if the same occurs:
http://phpthumb.gxdlabs.com/
Have you tried removing the ceil() functions? The image functions will automatically convert floats to integers anyway.
EDIT:
See if this works:
if($width > $max_width && $height * $thumb_width / $width < $max_width)
{
$thumb_width = $max_width;
$thumb_height = $height * $thumb_width / $width;
}
elseif($height > $max_height && $width * $thumb_height / $height < $max_height)
{
$thumb_height = $max_height;
$thumb_width = $width * $thumb_height / $height;
}
else
{
$thumb_width = $width;
$thumb_height = $height;
}
This seems to be a bug in PHP.

Arbitrary image resizing in PHP

What would be the best way to resize images, which could be of any dimension, to a fixed size, or at least to fit within a fixed size?
The images come from random urls that are not in my control, and I must ensure the images do not go out of an area roughly 250px x 300px, or 20% by 50% of a layer.
I would think that I would first determine the size, and if it fell outside the range, resize by a factor, but I am unsure how to work out the logic to resize if the image size could be anything.
edit:I do not have local access to the image, and the image url is in a variable with is output with img src=..., I need a way to specify the values of width and height tags.
It's easy to do with ImageMagick. Either you use convert command line tool via exec or you use http://www.francodacosta.com/phmagick/resizing-images .
If you use 'convert' you can even tell ImageMagick to just resize larger images and to not convert smaller ones: http://www.imagemagick.org/Usage/resize/
If you don't have local access, you won't be able to use ImageMagick:
<?php
$maxWidth = 250;
$maxHeight = 500;
$size = getimagesize($url);
if ($size) {
$imageWidth = $size[0];
$imageHeight = $size[1];
$wRatio = $imageWidth / $maxWidth;
$hRatio = $imageHeight / $maxHeight;
$maxRatio = max($wRatio, $hRatio);
if ($maxRatio > 1) {
$outputWidth = $imageWidth / $maxRatio;
$outputHeight = $imageHeight / $maxRatio;
} else {
$outputWidth = $imageWidth;
$outputHeight = $imageHeight;
}
}
?>
Have you looked at the GD library documentation, particularly the imagecopyresized method?
If you have to keep the aspect ratio of the image intact then you should scale it by a factor so that the longer side of the image (height or width) fits inside the limitations for that side.
You could use the following:
$maxWidth = 250;
$maxHeight = 500;
$validSize = true;
$imageinfo = getimagesize($filename);
if ($imageinfo) {
$validSize &= $imageinfo[0] <= $maxWidth;
$validSize &= $imageinfo[1] <= $maxHeight;
}
The &= is a combined operator of the bitwise-and operator & and the assignment operator =. Thus $foo &= *expr* and $foo = $foo & *expr* are equivalent.
I'm not sure of the exact solution to your answer, but I know a project written in PHP which has the solution. Go and take a look at the ImageCache built for the Drupal CMS, which is written in PHP.
It basically let's you define some actions to take on an arbitrary image, and produce almost whatever scaling/cropping you want to the image.
You'd have to learn a few of the Drupal API's to be able to understand this example, but it is extensive and if you understand their algorithms, you'd be able to solve other more complicated problems.
I created the following function to do intelligent resizing of images with respect to ratio and even has a parameter to upscale smaller images (which is critical if your HTML layout screws up when thumbnails are wierd sizes).
function ImageIntelligentResize( $imagePath, $maxWidth, $maxHeight, $alwaysUpscale )
{
// garbage in, garbage out
if ( IsNullOrEmpty($imagePath) || !is_file($imagePath) || IsNullOrEmpty($maxWidth) || IsNullOrEmpty($maxHeight) )
{
return array("width"=>"", "height"=>"");
}
// if our thumbnail size is too big, adjust it via HTML
$size = getimagesize($imagePath);
$origWidth = $size[0];
$origHeight = $size[1];
// Check if the image we're grabbing is larger than the max width or height or if we always want it resized
if ( $alwaysUpscale || $origWidth > $maxWidth || $origHeight > $maxHeight )
{
// it is so let's resize the image intelligently
// check if our image is landscape or portrait
if ( $origWidth > $origHeight )
{
// target image is landscape/wide (ex: 4x3)
$newWidth = $maxWidth;
$ratio = $maxWidth / $origWidth;
$newHeight = floor($origHeight * $ratio);
// make sure the image wasn't heigher than expected
if ($newHeight > $maxHeight)
{
// it is so limit by the height
$newHeight = $maxHeight;
$ratio = $maxHeight / $origHeight;
$newWidth = floor($origWidth * $ratio);
}
}
else
{
// target image is portrait/tall (ex: 3x4)
$newHeight = $maxHeight;
$ratio = $maxHeight / $origHeight;
$newWidth = floor($origWidth * $ratio);
// make sure the image wasn't wider than expected
if ($newWidth > $maxWidth)
{
// it is so limit by the width
$newWidth = $maxWidth;
$ratio = $maxWidth / $origWidth;
$newHeight = floor($origHeight * $ratio);
}
}
}
// it's not, so just use the current height and width
else
{
$newWidth = $origWidth;
$newHeight = $origHeight;
}
return array("width"=>$newWidth, "height"=>$newHeight);
}

Categories