How to properly resize large images in PHP - 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.

Related

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();

Resize uploaded image to proportion

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;

Image resizing with PHP advice

From previous experiences, I've noticed that I'm not very good at integrating 'scripts' created by others, with my own existing code, as of now. I need some advice on understanding what this person is saying about resizing images with PHP:
In the comments, he's written:
// Parameters need to be passed in through the URL's query string:
// image absolute path of local image starting with "/" (e.g. /images/toast.jpg)
// width maximum width of final image in pixels (e.g. 700)
// height maximum height of final image in pixels (e.g. 700)
Then, he gives an actual example, also as a comment:
// Resizing and cropping a JPEG into a square:
// <img src="/image.php/image-name.jpg?width=100&height=100&cropratio=1:1&image=/path/to/image.jpg" alt="Don't forget your alt text" />
I am guessing, he wants the user of the script to have something similar to the above img src. So, my question is: How will I actually make the source of my pictures similar to the example above? Below is some of the code I have. It shows how I am saving the pictures and HOW I am echoing/displaying them. It's from uploader.php:
move_uploaded_file($_FILES["file"]["tmp_name"],
"profileportraits/" . $_FILES["file"]["name"]);
echo "Stored in: " . "profileportraits/" . $_FILES["file"]["name"];
Once the photo has been saved in the folder, I save the file path in a MySQL table and later call the file path to display the picture (Below Code). It's from profile.php
echo "<img src=\"{$row['PortraitPath']}\" />";
Therefore, how will I pass the parameters similar to the one in the 'script example' if I am using the above img src to display the 'actual' picture?
THANK YOU.
$width = 100;
$height = 100;
src="/image.php?width={$width}&height={$height}"
You can use the following code to resize the image... once you got all the params
/**
* Gets the jpeg contents of the resized version of an already uploaded image
* (Returns false if the file was not an image)
*
* #param string $input_name The name of the file on the disk
* #param int $maxwidth The desired width of the resized image
* #param int $maxheight The desired height of the resized image
* #param true|false $square If set to true, takes the smallest of maxwidth and
* maxheight and use it to set the dimensions on the new image. If no
* crop parameters are set, the largest square that fits in the image
* centered will be used for the resize. If square, the crop must be a
* square region.
* #param int $x1 x coordinate for top, left corner
* #param int $y1 y coordinate for top, left corner
* #param int $x2 x coordinate for bottom, right corner
* #param int $y2 y coordinate for bottom, right corner
* #param bool $upscale Resize images smaller than $maxwidth x $maxheight?
* #return false|mixed The contents of the resized image, or false on failure
*/
function get_resized_image_from_existing_file($input_name, $maxwidth, $maxheight, $square = FALSE, $x1 = 0, $y1 = 0, $x2 = 0, $y2 = 0, $upscale = FALSE) {
// Get the size information from the image
$imgsizearray = getimagesize($input_name);
if ($imgsizearray == FALSE) {
return FALSE;
}
// Get width and height
$width = $imgsizearray[0];
$height = $imgsizearray[1];
// make sure we can read the image
$accepted_formats = array(
'image/jpeg' => 'jpeg',
'image/pjpeg' => 'jpeg',
'image/png' => 'png',
'image/x-png' => 'png',
'image/gif' => 'gif'
);
// make sure the function is available
$load_function = "imagecreatefrom" . $accepted_formats[$imgsizearray['mime']];
if (!is_callable($load_function)) {
return FALSE;
}
// crop image first?
$crop = TRUE;
if ($x1 == 0 && $y1 == 0 && $x2 == 0 && $y2 == 0) {
$crop = FALSE;
}
// how large a section of the image has been selected
if ($crop) {
$region_width = $x2 - $x1;
$region_height = $y2 - $y1;
} else {
// everything selected if no crop parameters
$region_width = $width;
$region_height = $height;
}
// determine cropping offsets
if ($square) {
// asking for a square image back
// detect case where someone is passing crop parameters that are not for a square
if ($crop == TRUE && $region_width != $region_height) {
return FALSE;
}
// size of the new square image
$new_width = $new_height = min($maxwidth, $maxheight);
// find largest square that fits within the selected region
$region_width = $region_height = min($region_width, $region_height);
// set offsets for crop
if ($crop) {
$widthoffset = $x1;
$heightoffset = $y1;
$width = $x2 - $x1;
$height = $width;
} else {
// place square region in the center
$widthoffset = floor(($width - $region_width) / 2);
$heightoffset = floor(($height - $region_height) / 2);
}
} else {
// non-square new image
$new_width = $maxwidth;
$new_height = $maxwidth;
// maintain aspect ratio of original image/crop
if (($region_height / (float)$new_height) > ($region_width / (float)$new_width)) {
$new_width = floor($new_height * $region_width / (float)$region_height);
} else {
$new_height = floor($new_width * $region_height / (float)$region_width);
}
// by default, use entire image
$widthoffset = 0;
$heightoffset = 0;
if ($crop) {
$widthoffset = $x1;
$heightoffset = $y1;
}
}
// check for upscaling
// #todo This ignores squares, coordinates, and cropping. It's probably not the best idea.
// Size checking should be done in action code, but for backward compatibility
// this duplicates the previous behavior.
if (!$upscale && ($height < $new_height || $width < $new_width)) {
// zero out offsets
$widthoffset = $heightoffset = 0;
// determine if we can scale it down at all
// (ie, if only one dimension is too small)
// if not, just use original size.
if ($height < $new_height && $width < $new_width) {
$ratio = 1;
} elseif ($height < $new_height) {
$ratio = $new_width / $width;
} elseif ($width < $new_width) {
$ratio = $new_height / $height;
}
$region_height = $height;
$region_width = $width;
$new_height = floor($height * $ratio);
$new_width = floor($width * $ratio);
}
// load original image
$orig_image = $load_function($input_name);
if (!$orig_image) {
return FALSE;
}
// allocate the new image
$newimage = imagecreatetruecolor($new_width, $new_height);
if (!$newimage) {
return FALSE;
}
// create the new image
$rtn_code = imagecopyresampled( $newimage,
$orig_image,
0,
0,
$widthoffset,
$heightoffset,
$new_width,
$new_height,
$region_width,
$region_height );
if (!$rtn_code) {
return FALSE;
}
// grab contents for return
ob_start();
imagejpeg($newimage, null, 90);
$jpeg = ob_get_clean();
imagedestroy($newimage);
imagedestroy($orig_image);
return $jpeg;
}
Hope this helps..
Thanks
Chetan sharma

PHP Image Optimization

I need some ways to dynamically optimize images using PHP. Sample codes or a module would be very helpful. Thanks.
you can use thimthumb.php
go to your .htaccess
than add
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^(.*\.(png|jpg))$ /timthumb.php?q=100&src=$1
</IfModule>
"q=100" is for the quality you can set it betwen 0-100
than creat a new file "img.php" in the home directory eg: www.your-website.com/timthumb.php
http://timthumb.googlecode.com/svn/trunk/timthumb.php
than find this
// Get original width and height
$width = imagesx ($image);
$height = imagesy ($image);
$origin_x = 0;
$origin_y = 0;
// generate new w/h if not provided
if ($new_width && !$new_height) {
$new_height = floor ($height * ($new_width / $width));
} else if ($new_height && !$new_width) {
$new_width = floor ($width * ($new_height / $height));
}
and replace it with this
// Get original width and height
$width = imagesx ($image);
$height = imagesy ($image);
$origin_x = 0;
$origin_y = 0;
// don't allow new width or height to be greater than the original
if( $new_width > $width ) {
$new_width = $width;
}
if( $new_height > $height ) {
$new_height = $height;
}
// generate new w/h if not provided
if ($new_width && !$new_height) {
$new_height = floor ($height * ($new_width / $width));
} else if ($new_height && !$new_width) {
$new_width = floor ($width * ($new_height / $height));
}
so when you go to www.your-website.com/images/pict.png it's like your loading
www.your-website.com/img.php?q=100&src=/images/pict.png
hope you like this! & sorry if i couldn't explaing well because my english is not good. :D
What do you mean by optimize? If you wish to keep the dimensions but reduce the size, take a look at imageJpeg for an example, and specifically the third argument (quality). Consider 100 to be perfect quality, and start decreasing it to find an optimal balance between quality and size.
very simple one is here too:
just change quality value (60) from 1 to 100, as you wish.
after execution of script, look at the filesize, and thats way, define your optimal quality value.
source

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