A followup to this question:
How to get new width and height after an image has been rotated with imagerotate()?
The answer I got is based on the filenames actual image size, but if I want to start from another width and height.
How would I achieve that? See below code for my attempt...
$ps['product_angle'] = 77; //Could be any angle
$filename = 'test.png' //filename to the original product
list($source_width, $source_height) = getimagesize($filename);
$source_image = imagecreatefromjpeg($filename);
//Example for clarification (this is parameters that is used to save new image)
$ps['product_width'] = 40; //for example $source_width = 200
$ps['product_height'] = 80; //and $source_height = 400
$angle = $ps['product_angle'];
if (intval($angle) <> 0) {
//Actual dimensions of image from filename
$current_source_x = imagesx($source_image);
$current_source_y = imagesy($source_image);
//Get current ratio of "scaled down image"
$ratio_x = $ps['product_width'] / $current_source_x;
$ratio_y = $ps['product_height'] / $current_source_y;
$source_image = imagerotate(
$source_image,
360-$angle,
imageColorAllocateAlpha($source_image, 255, 255, 255, 127)
);
//New dimensions from actual filename
//This would be fine if I just wanted the new width and height
//based on the filenames dimension, but I want new dimensions
//for the "scaled downed version" of the image (40, 80)
$new_image_source_x = imagesx($source_image);
$new_image_source_y = imagesy($source_image);
//I tried this, but obviously I'm doing something totally wrong
$new_width = ($new_image_source_x - $current_source_x) * $ratio_x;
$new_height = ($new_image_source_y - $current_source_y) * $ratio_y;
//Set new width after rotation
$ps['product_width'] = $new_width;
$ps['product_height'] = $new_height;
}
$ps['source_image'] = $source_image;
list($source_width, $source_height) = getimagesize($filename);
$dest_width = (int)$ps['product_width'];
$dest_height = (int)$ps['product_height'];
//Resize source-image to new width and height
//
imagecopyresized($dest_image, $ps['source_image'], 0, 0, 0, 0, $dest_width, $dest_height, $source_width, $source_height);
Probably I'm missing something very essential....
UPDATE
An example of real values...
image current width63.224619257754
image current height80.210337864315
//after calculation
image new width37.583523669887
image newt height21.716336015666
where angle is 41.10419020401479
Looking at my comment
"I've actually came into a solution"
.. It wasn't the day when I used my best english skills...
I guess you know what I meant though and here is how I solved my issue:
In my approach I was trying to calculate the new values based on ratio from the "scaled down image" and then get new width and height based on the difference from the "scaled down image" and "the image after rotated".
Something like this:
Set the relation/ratio between "scaled down image" and original image.
Do the actual image-rotation
Get the difference between original dimensions from the rotated image and multiply that with ratio-factor set before image was rotated.
Get the new width and height based on the rotation angle
This really didn't work correctly (failed at step4). I've been looking around a lot to find answers how to calculate width and height after an image is rotated. But these width and heights didn't return the same dimensions that the GD-functions imagesx() and imagesy() returned after the image was rotated. I've tried out several of calculations using sin() and cos() for retrieving the width and height but still didn't get exact same values as imagesx() and imagesy().
That got me thinking... What if I change my approach to:
Set the relation/ratio between "scaled down image" and original image.
Do the actual image-rotation
Apply the new dimensions based on the ratio multiplied with imagesx() and imagesy() - values return after the rotation
New code:
//Rotate resized image (if it should be)
$angle = $ps['product_angle'];
if (intval($angle) <> 0) {
//Get current dimensions from file
$current_source_x = imagesx($source_image);
$current_source_y = imagesy($source_image);
//Get current ratio of "scaled down image"
$ratio_x = $ps['product_width'] / $current_source_x;
$ratio_y = $ps['product_height'] / $current_source_y;
//Rotate image
$source_image = imagerotate($source_image, 360-$angle, imageColorAllocateAlpha($source_image, 255, 255, 255, 127));
//Now we get a new width from the imagerotate()-function, use those to set new_width from
//ratio/propoprtions is used from origin width and height
$ps['product_width'] = imagesx($source_image) * $ratio_x;
$ps['product_height'] = imagesy($source_image) * $ratio_y;
}
This worked fine - almost... the issue now was that the if the rotated image's new width and/or height got larger then the origin dimensions of the image, then the proportions wouldn't be accurate (and would cut off the imagesometimes (depending the rotation angle)).
The code was modified so heights and widths would be in proportion to the image from the file when creating the resized image.
//Rotate resized image (if it should be)
$angle = $ps['product_angle'];
if (intval($angle) <> 0) {
//Get current dimensions from file
$current_source_x = imagesx($source_image);
$current_source_y = imagesy($source_image);
//Get current ratio of "scaled down image"
$ratio_x = $ps['product_width'] / $current_source_x;
$ratio_y = $ps['product_height'] / $current_source_y;
//Rotate image
$source_image = imagerotate($source_image, 360-$angle, imageColorAllocateAlpha($source_image, 255, 255, 255, 127));
//Now we get a new width from the imagerotate()-function, use those to set new_width from
//ratio/propoprtions is used from origin width and height
$ps['product_width'] = imagesx($source_image) * $ratio_x;
$ps['product_height'] = imagesy($source_image) * $ratio_y;
//Set these so we can modifiy the width and height given from getimagesize()-function below
$ps['source_width'] = imagesx($source_image) ;
$ps['source_height'] = imagesy($source_image);
}
//If image is rotated, then width and height are adjusted with these values
if (isset($ps['source_width']) && isset($ps['source_height']) ) {
$source_width = $ps['source_width'];
$source_height = $ps['source_height'];
}
//Set position where to place in the image to save
$dest_x = $ps['product_left'];
$dest_y = $ps['product_top'];
$dest_width = (int)$ps['product_width'];
$dest_height = (int)$ps['product_height'];
//Resize source-image to new width and height and then copy from source to destination point
imagecopyresized($dest_image, $ps['source_image'], $dest_x, $dest_y, 0, 0, $dest_width, $dest_height, $source_width, $source_height);
So my final solution would include these steps:
Set the relation/ratio between "scaled down image" and original image.
Do the actual image-rotation
Apply the new dimensions based on the ratio multiplied with imagesx() and imagesy() - values return after the rotation
Set "fake" width and height when image has been rotated, so the resizing would work in proportion to the original image.
I hope this would help anyone who's struggling with the same issue I've struggled with (a few hours to much)!
Related
I'm rotating and cropping a image with PHP, but I get the black border showing, I know you can change the background color, but I want to rotate and crop the image to fill the whole image. Basically something similar to background-size: cover; (left) in CSS versus background-size: contain; (right).
See the image below, at right is what I got now, left is what I want to achieve. The number of degrees to rotate is dynamic and the image to be produced and the source-image are both square (200x200).
EDIT: Here is my quick and dirty code:
$rotate = imagecreatefromjpeg($image);
// part of code created by www.thewebhelp.com, modified
$square_size = 200;
$original_width = imagesx($rotate);
$original_height = imagesy($rotate);
if($original_width > $original_height){
$new_height = $square_size;
$new_width = $new_height*($original_width/$original_height);
}
if($original_height > $original_width){
$new_width = $square_size;
$new_height = $new_width*($original_height/$original_width);
}
if($original_height == $original_width){
$new_width = $square_size;
$new_height = $square_size;
}
$new_width = round($new_width);
$new_height = round($new_height);
$smaller_image = imagecreatetruecolor($new_width, $new_height);
$square_image = imagecreatetruecolor($square_size, $square_size);
imagecopyresampled($smaller_image, $rotate, 0, 0, 0, 0, $new_width, $new_height, $original_width, $original_height);
if($new_width>$new_height){
$difference = $new_width-$new_height;
$half_difference = round($difference/2);
imagecopyresampled($square_image, $smaller_image, 0-$half_difference+1, 0, 0, 0, $square_size+$difference, $square_size, $new_width, $new_height);
}
if($new_height>$new_width){
$difference = $new_height-$new_width;
$half_difference = round($difference/2);
imagecopyresampled($square_image, $smaller_image, 0, 0-$half_difference+1, 0, 0, $square_size, $square_size+$difference, $new_width, $new_height);
}
if($new_height == $new_width){
imagecopyresampled($square_image, $smaller_image, 0, 0, 0, 0, $square_size, $square_size, $new_width, $new_height);
}
$degrees = rand(1,360);
$square_image = imagerotate($square_image, $degrees, 0);
imagejpeg($square_image,NULL,100);
Replace these lines at around the end of your code:
$degrees = rand(1,360);
$square_image = imagerotate($square_image, $degrees, 0);
imagejpeg($square_image,NULL,100);
With this:
$degrees = rand(1,360);
$square_image = imagerotate($square_image, $degrees, 0);
$rotated_size = imagesx($square_image);
$enlargement_coeff = ($rotated_size - $square_size) * 1.807;
$enlarged_size = round($rotated_size + $enlargement_coeff);
$enlarged_image = imagecreatetruecolor($enlarged_size, $enlarged_size);
$final_image = imagecreatetruecolor($square_size, $square_size);
imagecopyresampled($enlarged_image, $square_image, 0, 0, 0, 0, $enlarged_size, $enlarged_size, $rotated_size, $rotated_size);
imagecopyresampled($final_image, $enlarged_image, 0, 0, round($enlarged_size / 2) - ($square_size / 2), round($enlarged_size / 2) - ($square_size / 2), $square_size, $square_size, $square_size, $square_size);
imagejpeg($final_image,NULL,100);
Here's the logic behind that:
1) After performing imagerotate() our new image has changed its dimensions, since every rotation generally results in a larger image. Since the source is a square image we take either the width or the height in order to determine the dimensions of the rotated image.
2) When the original image is rotated, even a little bit, the dimensions of the largest square of usable pixel data from the original image will always be smaller than the original unrotated square image. Therefore, in order to generate a new square image of the same size as the initial square image, but without the "black border" artifact, as you call it, we need to enlarge the rotated image, so that the largest square of usable pixel data from the original image in the rotated image can become as big as the initial square image.
The key value here is 1.807. This value basically shows how many pixels you need to enlarge a rotated image for each pixel of difference between its dimensions and the dimensions of the original unrotated image. There's probably a better Math formula to retrieve this value, unfortunately I suck at Math, so here's the hard way of coming up with that value.
A rotation of 45 / 135 / 225 / 315 degrees will always produce the largest image with the smallest usable pixel data square.
Knowing this, you compare the dimensions of the original image and its 45-degrees-rotated version. In our case the original image is 200x200 and a 45-degrees-rotated version is about 283x283
In a program like Photoshop, you determine how many times you need to enlarge the 45-degrees-rotated version of the image in order to be able to extract a 200x200 square from it, without a "black border" - in our case the 283x283 image needed to be enlarged to a 433x433 image, so we could extract a 200x200 square
433 - 283 = 150 -> meaning we need to enlarge the largest possible rotated image with 150 pixels in order to be able to extract a 200x200 square from it.
283 - 200 = 83 -> 83 pixels is the difference between the largest possible rotated image and the original unrotated image.
The "smaller" the transformation - the "larger" the square area we can use and thus - the "smaller" the amount of enlargement we need to apply. And since a 45 degree rotation resulted in a difference of 83 pixels between the original image and the transformed image that required a 150 pixel enlargement, we can do:
150 / 83 = 1.807 -> meaning a difference of 1 pixel between the original image and the rotated image requires that the rotated image is enlarged with 1.807 pixels, so that we can extract a square from it that has the same dimensions as the original image
3) Knowing that for each 1 pixel difference we need to enlarge with 1.807 pixels, we check what's the difference between our rotated image size and original image size and multiply it by that value, to see what dimensions should the enlarged image have:
$enlargement_coeff = ($rotated_size - $square_size) * 1.807;
$enlarged_size = round($rotated_size + $enlargement_coeff);
4) We go ahead and generate the enlarged rotated image.
imagecopyresampled($enlarged_image, $square_image, 0, 0, 0, 0, $enlarged_size, $enlarged_size, $rotated_size, $rotated_size);
5) Finally, we extract a 200x200 square from our enlarged rotated image, using its center coordinates as reference
imagecopyresampled($final_image, $enlarged_image, 0, 0, round($enlarged_size / 2) - ($square_size / 2), round($enlarged_size / 2) - ($square_size / 2), $square_size, $square_size, $square_size, $square_size);
To break that down ($square_size / 2) returns the X and Y coordinates of the center point in the enlarged rotated image. round($enlarged_size / 2) returns the amount of pixels that you need left from the center along the X axis, and above the center along the Y axis, in order to get a 200x200 square.
I hope you understand the logic, although I'm understanding my explanation may sound a bit ambiguous, so please feel free to ask more!
I tried a lot of methods to do that, but I still have a lot of troubles with that.
Is it possible to resize all images to fixed width and height?
I want every uploaded image with width>=200px and height>=260px to be resized to width=200px and height=260px, but I want to keep a bit of proportionality and if image is bigger than 200x260px to resize it proportionally and then to capture center of image 200x260px.
I just need idea to know where to start and what to do, but if you have an example I would like to see it. Thanks.
If you want to trim the image you can do in the following way:-
//Your Image
$imgSrc = "image.jpg";
//getting the image dimensions
list($width, $height) = getimagesize($imgSrc);
//saving the image into memory (for manipulation with GD Library)
$myImage = imagecreatefromjpeg($imgSrc);
// calculating the part of the image to use for thumbnail
if ($width > $height) {
$y = 0;
$x = ($width - $height) / 2;
$smallestSide = $height;
} else {
$x = 0;
$y = ($height - $width) / 2;
$smallestSide = $width;
}
// copying the part into thumbnail
$thumbSize = 100;
$thumb = imagecreatetruecolor($thumbSize, $thumbSize);
imagecopyresampled($thumb, $myImage, 0, 0, $x, $y, $thumbSize, $thumbSize, $smallestSide, $smallestSide);
//final output
header('Content-type: image/jpeg');
imagejpeg($thumb);
To begin writing the function, we have to declare it as such… Then we have to throw in our attributes. We want to restrict our image, so we have to let the function know the dimensions to which we want to restrict it, and we have to know what the original image size is to begin with (we’ll get to that part in a second).
<?php
function imageResize($width, $height, $target) {
//takes the larger size of the width and height and applies the
formula accordingly...this is so this script will work
dynamically with any size image
if ($width > $height) {
$percentage = ($target / $width);
} else {
$percentage = ($target / $height);
}
//gets the new value and applies the percentage, then rounds the value
$width = round($width * $percentage);
$height = round($height * $percentage);
//returns the new sizes in html image tag format...this is so you
can plug this function inside an image tag and just get the
return "width=\"$width\" height=\"$height\"";
}
?>
Before we take our new function on a test drive, we need to get the width and height of the image that we want to display. There is a magical command in PHP called getimagesize(). This command, used properly, will return the image width, height, type, and even the width and height in HTML image tag format (width=”x” height=”y”).
$mysock = getimagesize("images/sock001.jpg");
Now, $mysock is an array that holds vital information about the particular image we want to display. In index 0, we have the width ($mysock[0]), and in index 1, we have the height ($mysock[1]). That’s really all we need, in order to get what we want done. Want to see the function… well, function? Here we go!
Let’s say you want to display a list of your beautiful socks, but you want room on the page to show them neatly in a row, and to do that they cannot be larger than 150 pixels tall or wide.
<?php
//get the image size of the picture and load it into an array
$mysock = getimagesize("images/sock001.jpg");
?>
<!-using a standard html image tag, where you would have the
width and height, insert your new imageResize() function with
the correct attributes -->
<img src="images/sock001.jpg" <?php imageResize($mysock[0],
$mysock[1], 150); ?>>
That’s it! Now, no matter what the original file size, it will be restricted to no more than 150 pixels in width or height (or whatever you specify).
i'm familiar with resizing and cropping images under php using imagecopyresampled but now i'm having a special problem:
the task is cropping a large image from eg. 1600x1200 to 500x120, which means resizing down to 500px and crop its height that it'S 120px. is there some easy way or do i need to calculate the cropping values all on my own? thanks
There is PHP library that could help you out called PHPThumb. You can find here https://github.com/masterexploder/PHPThumb
They have an adaptive resize method that does what you're looking for. https://github.com/masterexploder/PHPThumb/wiki/Basic-Usage
You have to do it yourself.
I don't know if you want to crop or not, so here's how to calculate the values for both:
Scale image: resize to fit within new w x h keeping aspect ratio (so 1 side may be shorter than specified)
function calc_scale_dims($width_orig, $height_orig, $max_width, $max_height) {
$new_width=$width_orig;
$new_height=$height_orig;
$ratioh = $max_height/$new_height;
$ratiow = $max_width/$new_width;
$ratio = min($ratioh, $ratiow);
// New dimensions
$dims["w"] = intval($ratio*$new_width);
$dims["h"] = intval($ratio*$new_height);
return $dims;
}
Resize and Crop: Resizes image and crops it to fit into the specified w x h if new aspect ratio is different (e.g. if aspect ratios are different, image will be resized to match specified size on the short size and the longer size if cropped in the middle)
function calc_crop_resize_dims($width_orig, $height_orig, $new_width, $new_height) {
//Calculate scaling
$ratio_orig = $width_orig/$height_orig;
$ratio_new = $new_width/$new_height;
if ($ratio_new < $ratio_orig) {
$copy_width = $height_orig*$ratio_new;
$copy_height = $height_orig;
} else {
$copy_width = $width_orig;
$copy_height = $width_orig/$ratio_new;
}
//point to start copying from (to copy centre of image if we are cropping)
$dims["src_x"] = ($width_orig - $copy_width)/2;
$dims["src_y"] = ($height_orig - $copy_height)/2;
$dims["copy_width"] = $copy_width;
$dims["copy_height"] = $copy_height;
return $dims;
}
I am trying to find a way to calculate an image width/height ratio
to resize it and keep the proportions.
For example, I want to resize a 500x750 image and reduce its width to 350. What height should I use that is proportional to 350?
Use Javascript.
See this tutorial:
http://www.ajaxblender.com/howto-resize-image-proportionally-using-javascript.html
Using the function the other guy posted:
<?PHP
$imagePath = "images/your_image.png";
list($oldWidth, $height, $type, $attr) = getimagesize($image_path);
$percentChange = $newWidth / $oldWidth;
$newHeight = round( ( $percentChange *$height ) );
echo '<img src="'.$imagePath.'" height="'.$new_height.'" width="'.$newWidth.'">';
?>
I think this is the php function you're looking for: getimagesize
From the manual:
Returns an array with 7 elements.
Index 0 and 1 contains respectively the width and the height of the
image.
Here's a short example how to work with this for your problem:
// get the current size of your image
$data = getimagesize('link/your/image.jpg');
// your defined width
$new_width = 350;
// calculate the ratio
$ratio = $data[0] / $new_width;
// apply the ratio to get the new height of your image
$new_height = round($data[1] / $ratio);
...done!
use getImagesize and get the new height by dividing by the aspect ratio.
list($width, $height, $type, $attr) = getimagesize("image.jpg");
$aspect = $width / $height;
$newWidth = 350;
$newHeight = $newWidth / $aspect;
You tagged your question with the PHP tag, so assuming you want to use PHP:
To get an image's height or width from an image resource, use imagesx() and imagesy(). http://www.php.net/manual/en/function.imagesx.php
http://www.php.net/manual/en/function.imagesy.php
To get an image's height and width from an image file, use getimagesize(). Items 0 and 1 in the array returned by that function are the width and height of the image.
http://www.php.net/manual/en/function.getimagesize.php
If you have an image that is 500 pixels wide and 750 pixels high, and you have a container that is 350 pixels wide, you can calculate the ratio by dividing the desired width by the actual width: 350/500 which is 0.7. So to calculate the height, multiply it by that ratio (750 * 0.7 or 525).
I have image upload form, user attaches aimage file, and selects image size to resize the uploaded image file(200kb, 500kb, 1mb, 5mb, Original). Then my script needs to resize image file size based on user's optional size, but im not sure how to implement this feature,
For example, user uploads image with one 1mb size, and if user selects 200KB to resize, then my script should save it with 200kb size.
Does anyone know or have an experience on similar task ?
Thanks for you reply in advance.
With the GD library, use imagecopyresampled().
<?php
// The file
$filename = 'test.jpg';
$percent = 0.5;
// Content type
header('Content-type: image/jpeg');
// Get new dimensions
list($width, $height) = getimagesize($filename);
$new_width = $width * $percent;
$new_height = $height * $percent;
// Resample
$image_p = imagecreatetruecolor($new_width, $new_height);
$image = imagecreatefromjpeg($filename);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
// Output
imagejpeg($image_p, null, 100);
?>
Edit: If you want to resize the image file to a specified size, that's a little harder. All the major image formats use compression and compression rates vary by the nature of what's being compressed. Compress clear blue sky and you'll get a better compression ratio than you will sea of people.
The best you can do is try a particular size is try a particular size and see what the file size is, adjusting if necessary.
Resize ratio = desired file size / actual file size
Resize multipler = square root (resize ratio)
New height = resize multiplier * actual height
New width = resize multiplier * actual width
This basically factors in an approximation of the expected compression ratio. I would expect that you would have some tolerance (like +/- 5%) and you can tweak the numbers as necessary.
There is no direct way to resize to a particular file size. Lastly I'll add that resizing to a particular file size is rather unusual. Resizing to a particular height and/or width (maintaining aspect ratio) is far more common and expected (by users).
Update: as correctly pointed out, this gets the file size wrong. The ratio needs to be the square root of the file size ratios as you're applying it twice (once to height, once to width).
Using the GD Library provided in PHP:
// $img is the image resource created by opening the original file
// $w and $h is the final width and height respectively.
$width = imagesx($img);$height = imagesy($img);
$ratio = $width/$height;
if($ratio > 1){
// width is greater than height
$nh = $h;
$nw = floor($width * ($nh/$height));
}else{
$nw = $w;
$nh = floor($height * ($nw/$width));
}
//centralize image
$nx = floor(($nw- $w) / 2.0);
$ny = floor(($nh-$h) / 2.0);
$tmp2 = imagecreatetruecolor($nw,$nh);
imagecopyresized($tmp2, $img,0,0,0,0,$nw,$nh,$width,$height);
$tmp = imagecreatetruecolor($w,$h);
imagecopyresized($tmp, $tmp2,0,0,$nx,$ny,$w,$h,$w,$h);
imagedestroy($tmp2);imagedestroy($img);
imagejpeg($tmp, $final_file);
This piece of code will take the original image, resize to the specified dimensions. It will first try to ratio aspect resize the image, then crop off + centralize the image, making it fall nicely into the dimensions specified.