Image resize and keep ratio bug - php

I found and modified a GD image resize and keep ratio script but it's not working as it should.
For example I want to resize a picture to MAXIMUM 200w x 200h keeping ratio. The picture I want to resize is 518w x 691h and the script should resize it to 150w x 200h to keep the aspect ratio but instead it resize it to 200w x 226h. What is the problem?
function resize_image($source_image, $name, $new_width, $new_height, $destination)
{
list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_image);
switch($source_image_type)
{
case IMAGETYPE_GIF:
$source_gd_image = imagecreatefromgif($source_image);
break;
case IMAGETYPE_JPEG:
$source_gd_image = imagecreatefromjpeg($source_image);
break;
case IMAGETYPE_PNG:
$source_gd_image = imagecreatefrompng($source_image);
break;
}
$source_aspect_ratio = $source_image_width / $source_image_height;
$thumbnail_aspect_ratio = $new_width / new_height;
if($source_image_width <= $new_width && $source_image_height <= new_height)
{
$thumbnail_image_width = $source_image_width;
$thumbnail_image_height = $source_image_height;
}
elseif ($thumbnail_aspect_ratio > $source_aspect_ratio)
{
$thumbnail_image_width = (int)(new_height * $source_aspect_ratio);
$thumbnail_image_height = new_height;
}
else
{
$thumbnail_image_width = $new_width;
$thumbnail_image_height = (int)($new_width / $source_aspect_ratio);
}
$thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
imagealphablending($thumbnail_gd_image, false);
imagesavealpha($thumbnail_gd_image, true);
imagecopyresampled($thumbnail_gd_image, $source_gd_image, 0, 0, 0, 0, $thumbnail_image_width, $thumbnail_image_height, $source_image_width, $source_image_height);
$destination = $destination.$name;
switch($source_image_type)
{
case IMAGETYPE_GIF:
imagegif($thumbnail_gd_image, $destination);
break;
case IMAGETYPE_JPEG:
imagejpeg($thumbnail_gd_image, $destination, 100);
break;
case IMAGETYPE_PNG:
imagepng($thumbnail_gd_image, $destination, 9);
break;
}
imagedestroy($source_gd_image);
imagedestroy($thumbnail_gd_image);
}

This section:
elseif ($thumbnail_aspect_ratio > $source_aspect_ratio)
should never execute, since you want to keep the aspect ratios the same. To determine the new width / height, try something like this:
if($width > $MAX_SIZE || $height > $MAX_SIZE) {
$aspect = $width / $height;
if($width > $height) {
$width = $MAX_SIZE;
$height = intval($MAX_SIZE / $aspect);
} else {
$height = $MAX_SIZE;
$width = intval($MAX_SIZE * $aspect);
}
}
Update
All this code is doing is attempting to determine a new width / height based on a restriction $MAX_SIZE, while keeping the aspect ratio the same. It's not going to be perfect because floating point arithemetic is rarely ever so (especially since in this case you can't have 'fractional' pixels, which is why the calculations above use intval). Consider if, for example, before this code is ran $width, $height and $MAX_SIZE are set as follows:
$MAX_SIZE = 100;
$width = 1920;
$height = 1080;
The original aspect ratio is 1.77777777.... After running the snippet above, the width / height will be set to 100 x 56, which is an aspect ratio of 1.7857. Moving the output width/height either up or down by a pixel will not ever get you the exact input aspect ratio, unless you allow pixel values with fractional components.
However you're uploading the file and determining the input file's height / width shouldn't matter, the snippet above is only supposed to get you the resized dimensions as close as possible to the input aspect ratio.

Related

Dart/Flutter Alternative to PHP's resize_crop_image?

Currently, I am resizing avatars on my server using PHP's resize_crop_image. The code below takes an image, crops and resizes it so that it's exactly 500px X 500px square.
resize_crop_image(500, 500, $filename, $filename);
But I'd like to move this process from the server to the flutter app. Most of the plugins I'm seeing on pub.dev are overkill with the user cropping the image using a crop tool. But I'd like this function to happen automatically the way I currently do it in PHP.
Here's the resize_crop_image code;
function resize_crop_image($max_width, $max_height, $source_file, $dst_dir, $quality = 100){
$imgsize = getimagesize($source_file);
$width = $imgsize[0];
$height = $imgsize[1];
$mime = $imgsize['mime'];
switch($mime){
case 'image/gif':
$image_create = "imagecreatefromgif";
$image = "imagegif";
break;
case 'image/png':
$image_create = "imagecreatefrompng";
$image = "imagepng";
$quality = 10;
break;
case 'image/jpeg':
$image_create = "imagecreatefromjpeg";
$image = "imagejpeg";
$quality = 100;
break;
default:
return false;
break;
}
$dst_img = imagecreatetruecolor($max_width, $max_height);
$src_img = $image_create($source_file);
$width_new = $height * $max_width / $max_height;
$height_new = $width * $max_height / $max_width;
//if the new width is greater than the actual width of the image, then the height is too large and the rest cut off, or vice versa
if($width_new > $width){
//cut point by height
$h_point = (($height - $height_new) / 2);
//copy image
imagecopyresampled($dst_img, $src_img, 0, 0, 0, $h_point, $max_width, $max_height, $width, $height_new);
}else{
//cut point by width
$w_point = (($width - $width_new) / 2);
imagecopyresampled($dst_img, $src_img, 0, 0, $w_point, 0, $max_width, $max_height, $width_new, $height);
}
$image($dst_img, $dst_dir, $quality);
if($dst_img)imagedestroy($dst_img);
if($src_img)imagedestroy($src_img);
}
?>
You could use this library: flutter_native_image for resizing/croping your images.
Important to take a look at the other parameters of this function, you can probably customize the way the image will be cropped.
Something like that:
ImageProperties properties = await FlutterNativeImage.getImageProperties(file.path);
File croppedFile = await FlutterNativeImage.cropImage(file.path, originX, originY, 500, 500);
originX/originY: x/y position for the cut.

Best output when resizing images in PHP

I want to make a thumbnail(64X64) of images uploaded by user to reduce the overall page size. I did it using the following PHP code, But the result is really disappointing! in one instance the original file size was 554 byte (70X70). by converting it to a 64X64 image, the file size has increase to 1.43 KB! while the quality of the image is terribly degraded.
What is wrong here? Isn't it any other way to resize the images by PHP and have a better output?
Thank you very much
function image_resize($src, $dst, $width, $height, $crop=0){
if(!list($w, $h) = getimagesize($src)) return "Unsupported picture type!";
$type = strtolower(substr(strrchr($src,"."),1));
if($type == 'jpeg') $type = 'jpg';
switch($type){
case 'bmp': $img = imagecreatefromwbmp($src); break;
case 'gif': $img = imagecreatefromgif($src); break;
case 'jpg': $img = imagecreatefromjpeg($src); break;
case 'png': $img = imagecreatefrompng($src); break;
default : return "Unsupported picture type!";
}
// resize
if($crop){
if($w < $width or $h < $height) return "Picture is too small!";
$ratio = max($width/$w, $height/$h);
$h = $height / $ratio;
$x = ($w - $width / $ratio) / 2;
$w = $width / $ratio;
}
else{
if($w < $width and $h < $height) return "Picture is too small!";
$ratio = min($width/$w, $height/$h);
$width = $w * $ratio;
$height = $h * $ratio;
$x = 0;
}
$new = imagecreatetruecolor($width, $height);
// preserve transparency
if($type == "gif" or $type == "png"){
imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
imagealphablending($new, false);
imagesavealpha($new, true);
}
imagecopyresampled($new, $img, 0, 0, $x, 0, $width, $height, $w, $h);
switch($type){
case 'bmp': imagewbmp($new, $dst); break;
case 'gif': imagegif($new, $dst); break;
case 'jpg': imagejpeg($new, $dst); break;
case 'png': imagepng($new, $dst); break;
}
return true;
}
I think that probably nothing is wrong here. 1.43KB is not very much, so it's not clear that the image produced is inefficiently stored. As for image quality, we're talking about a very small image; resizing from 70x70 to 64x64 is very likely to produce an odd-looking result. If you have a larger image to start with then it will probably work better.
There are other PHP image libraries (e.g. WideImage), but I'd be surprised if using them would make a great deal of difference here. In fact they probably use GD internally anyway.

Copy and resize image with php GD library

I want to copy, resize and (if it is not too hard) crop an image.
For example: I have an image with the dimensions 1200 height X 2300 width. I want to make an 100x100 pixels thumbnail of it, and in this case, because the height is less than the width the height should be 100% visible on the new image, and the width should keep the aspect ratio of the image. Here is an example image.
Additional details:
I am using wamp, Ihave enabled Gd 2
I just get broken images when I try rewriting the code from the example on this site.
try this one its from the Resize Images Using PHP and GD Library article
define('THUMBNAIL_IMAGE_MAX_WIDTH', 150);
define('THUMBNAIL_IMAGE_MAX_HEIGHT', 150);
function generate_image_thumbnail($source_image_path, $thumbnail_image_path)
{
list($source_image_width, $source_image_height, $source_image_type) = getimagesize($source_image_path);
switch ($source_image_type) {
case IMAGETYPE_GIF:
$source_gd_image = imagecreatefromgif($source_image_path);
break;
case IMAGETYPE_JPEG:
$source_gd_image = imagecreatefromjpeg($source_image_path);
break;
case IMAGETYPE_PNG:
$source_gd_image = imagecreatefrompng($source_image_path);
break;
}
if ($source_gd_image === false) {
return false;
}
$source_aspect_ratio = $source_image_width / $source_image_height;
$thumbnail_aspect_ratio = THUMBNAIL_IMAGE_MAX_WIDTH / THUMBNAIL_IMAGE_MAX_HEIGHT;
if ($source_image_width <= THUMBNAIL_IMAGE_MAX_WIDTH && $source_image_height <= THUMBNAIL_IMAGE_MAX_HEIGHT) {
$thumbnail_image_width = $source_image_width;
$thumbnail_image_height = $source_image_height;
} elseif ($thumbnail_aspect_ratio > $source_aspect_ratio) {
$thumbnail_image_width = (int) (THUMBNAIL_IMAGE_MAX_HEIGHT * $source_aspect_ratio);
$thumbnail_image_height = THUMBNAIL_IMAGE_MAX_HEIGHT;
} else {
$thumbnail_image_width = THUMBNAIL_IMAGE_MAX_WIDTH;
$thumbnail_image_height = (int) (THUMBNAIL_IMAGE_MAX_WIDTH / $source_aspect_ratio);
}
$thumbnail_gd_image = imagecreatetruecolor($thumbnail_image_width, $thumbnail_image_height);
imagecopyresampled($thumbnail_gd_image, $source_gd_image, 0, 0, 0, 0, $thumbnail_image_width, $thumbnail_image_height, $source_image_width, $source_image_height);
imagejpeg($thumbnail_gd_image, $thumbnail_image_path, 90);
imagedestroy($source_gd_image);
imagedestroy($thumbnail_gd_image);
return true;
}
?>
Have you tried to use Timthumb? It's a nice little script that matches your requirements.
If you get corrupted image files something with your GD installation might be wrong, but without seeing any code it's hard to guess. Do you have any other machine (maybe a webhost with LAMP stack) to test your code there?

Why is the rotated image the same aspect ratio?

I'm using some PHP code from jquery file upload and I'm trying to rotate an image prior to saving it. Below is my function call:
public function CreateThumb($file_name, $options){
$file_path = $options['src_dir'].$file_name;
$new_file_path = $options['dst_dir'].$file_name;
list($img_width, $img_height) = #getimagesize($file_path);
if (!$img_width || !$img_height) {
return false;
}
$scale = min(
$options['max_width'] / $img_width,
$options['max_height'] / $img_height
);
$new_width = $img_width * $scale;
$new_height = $img_height * $scale;
$new_img = #imagecreatetruecolor($new_width, $new_height);
switch (strtolower(substr(strrchr($file_name, '.'), 1))) {
case 'jpg':
case 'jpeg':
$src_img = #imagecreatefromjpeg($file_path);
$write_image = 'imagejpeg';
$image_quality = isset($options['jpeg_quality']) ?
$options['jpeg_quality'] : 95;
break;
case 'gif':
#imagecolortransparent($new_img, #imagecolorallocate($new_img, 0, 0, 0));
$src_img = #imagecreatefromgif($file_path);
$write_image = 'imagegif';
$image_quality = null;
break;
case 'png':
#imagecolortransparent($new_img, #imagecolorallocate($new_img, 0, 0, 0));
#imagealphablending($new_img, false);
#imagesavealpha($new_img, true);
$src_img = #imagecreatefrompng($file_path);
$write_image = 'imagepng';
$image_quality = isset($options['png_quality']) ?
$options['png_quality'] : 9;
break;
default:
$src_img = null;
}
$src_img = imagerotate($src_img, 90, 0) ;
$success = $src_img && #imagecopyresampled(
$new_img,
$src_img,
0, 0, 0, 0,
$new_width,
$new_height,
$img_width,
$img_height
) && $write_image($new_img, $new_file_path, $image_quality);
// Free up memory (imagedestroy does not delete files):
#imagedestroy($src_img);
#imagedestroy($new_img);
return $success;
}
The image gets rotated but it still maintains it's original aspect ratio and is cropping the photo. Any idea what I'm doing wrong?
Your problem is in how you set the new values:
$new_width = $img_width * $scale;
$new_height = $img_height * $scale;
Should be, in case of a 90 degrees rotation:
$new_width = $img_height * $scale; // reverse height and width
$new_height = $img_width * $scale; // reverse height and width
Edit: And as the original image is rotated, the old width and height have to be reversed:
$success = $src_img && #imagecopyresampled(
$new_img,
$src_img,
0, 0, 0, 0,
$new_width,
$new_height,
$img_height, // reverse width and height
$img_width // reverse width and height
) && $write_image($new_img, $new_file_path, $image_quality);
Just a note: if you are going to use that many #'s to ignore errors, you might as well just put error_reporting(0); at the start of the code.
Secondly, the reason that I saw for the lack of change in pixel ratio is because your $scale is backwards. You have it saying scale = Whole/Part which will almost always give you a value greater than 1 (increasing pixel ratio) unless the max size is smaller than the image size. You'll want it to be scale = Part/Whole So its a decimal ratio (ie 100px/400px, scale = .25).
Good Luck!

When Resizing Grayscale PNG White lines appear

When resizing some png images they appear stretched and have what looks like vertical interlacing. I'm unsure of where the issue could be, however I'm starting to think its because the image starts in grayscale and needs to have a different color profile.
Any help or suggestions would be greatly appreciated.
Original image
Image showing the problem
function createImageSize($sourcefile, $setNewName, $maxwidth, $maxheight, $quality){
$fileInfoArray = #getimagesize($sourcefile);
$imagetype = $fileInfoArray['mime'];
list($width, $height, $attr) = getimagesize($sourcefile);
switch($imagetype){
case 'image/jpeg':
$img = imagecreatefromjpeg($sourcefile);
break;
case 'image/gif':
$img = imagecreatefromgif($sourcefile);
break;
case 'image/png':
$img = imagecreatefrompng($sourcefile);
break;
case 'image/x-png':
$img = imagecreatefrompng($sourcefile);
break;
}
if ($width > $maxwidth || $height > $maxheight){
if ( $width > $height ){
$newwidth = $maxwidth;
$ratio = $maxwidth / $width;
$newheight = floor($height * $ratio);
if ($newheight > $maxheight){
$newheight = $maxheight;
$ratio = $maxheight / $height;
$newwidth = floor($width * $ratio);
}
}else{
$newheight = $maxheight;
$ratio = $maxheight / $height;
$newwidth = floor($width * $ratio);
if ($newwidth > $maxwidth){
$newwidth = $maxwidth;
$ratio = $maxwidth / $width;
$newheight = floor($height * $ratio);
}
}
}else{
$newwidth = $width;
$newheight = $height;
}
$tmpimg = imagecreatetruecolor( $newwidth, $newheight );
if($imagetype == 'image/png'||$imagetype == 'image/x-png'){
imagealphablending($tmpimg, false);
imagesavealpha($tmpimg, true);
if($imagetype == 'image/gif'){
$transparent = imagecolorallocatealpha($tmpimg, 0, 0, 0, 127);
imagecolortransparent($tmpimg, $transparent);
}
imagefilledrectangle($tmpimg, 0, 0, $newwidth, $newheight, $transparent);
}
imagecopyresampled( $tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );
switch($imagetype){
case 'image/jpeg':
imagejpeg($tmpimg, $setNewName, $quality);
break;
case 'image/gif':
imagegif($tmpimg, $setNewName);
break;
case 'image/png':
imagepng($tmpimg, $setNewName, 3);
break;
case 'image/x-png':
imagepng($tmpimg, $setNewName, 3);
break;
}
imagedestroy($tmpimg);
imagedestroy($img);
}
I had the same problem. While generating EPS files into transparent PNG files (to use as mask images later), iMagick created the PNG files as grayscale. When GD reads grayscale PNGs, it doubles them horizontally with vertical lines in them.
My solution was handled on the iMagic side, by forcing it to write the PNGs as RGBA:
before:
$image->setImageFileName("image.png");
After:
$image->setImageFileName("png32:image.png");
I don't know where your grayscale PNGs are coming from, but if you are generating them, make sure they are created RGBA. Otherwise perhaps there is a way to read them with GD properly, specifying grayscale source.
I have the same issue and still haven't found any solutions. It would seem that GD doesn't like grayscale PNG8 images and is interpreting as colored.
I may have to convert the image back to 24 bits using "convert" with exec() and then delete the image, but it's far from optimal.

Categories