Data Loss in JPEG/PNG resizing - php

I've adapted this code that I found. It resizes .jpg and .png images, and maintains alpha layers.
When I resize an image, however, the reduction in file size is far more than I had anticipated. This isn't a problem for me, as I cannot see with my eyes any degradation or data loss.
What is causing the huge compression of the file, and if I ever needed to, how can I avoid it?
function thumbnail($image, $width, $height, $target) {
if($image[0] != "/") { // Decide where to look for the image if a full path is not given
if(!isset($_SERVER["HTTP_REFERER"])) { // Try to find image if accessed directly from this script in a browser
$image = $_SERVER["DOCUMENT_ROOT"].implode("/", (explode('/', $_SERVER["PHP_SELF"], -1)))."/".$image;
} else {
$image = implode("/", (explode('/', $_SERVER["HTTP_REFERER"], -1)))."/".$image;
}
} else {
$image = $_SERVER["DOCUMENT_ROOT"].$image;
}
$image_properties = getimagesize($image);
$image_width = $image_properties[0];
$image_height = $image_properties[1];
$image_ratio = $image_width / $image_height;
$type = $image_properties["mime"];
if(!$width && !$height) {
$width = $image_width;
$height = $image_height;
}
if(!$width) {
$width = round($height * $image_ratio);
}
if(!$height) {
$height = round($width / $image_ratio);
}
if($type == "image/jpeg") {
header('Content-type: image/jpeg');
$thumb = imagecreatefromjpeg($image);
} elseif($type == "image/png") {
header('Content-type: image/png');
$thumb = imagecreatefrompng($image);
} else {
return false;
}
$temp_image = imagecreatetruecolor($width, $height);
imagealphablending($temp_image, false);
imagesavealpha($temp_image,true);
$transparent = imagecolorallocatealpha($temp_image, 255, 255, 255, 127);
imagefilledrectangle($temp_image, 0, 0, $nWidth, $nHeight, $transparent);
imagecopyresampled($temp_image, $thumb, 0, 0, 0, 0, $width, $height, $image_width, $image_height);
//$thumbnail = imagecreatetruecolor($width, $height);
//imagecopyresampled($thumbnail, $temp_image, 0, 0, 0, 0, $width, $height, $width, $height);
if($type == "image/jpeg") {
imagejpeg($temp_image, 'img/'.$target.'.jpg');
} else {
imagepng($temp_image,'img/'.$target.'.png');
}
imagedestroy($temp_image);
//imagedestroy($thumbnail);
}

As showdev commented, both imagejpeg() and imagepng() accept a third optional parameter for the image quality. For imagejpeg() it runs from 0 to 100 and defaults to 75), for imagepng() it is from 0 to 9 and defaults to 6.
Also, 4.4Mb is a really big size for a ~2.4Mpx image. It problably contains a lot of metadata and most of it -like Photoshop thumbsnails- is not preserved when you do an imagecopyresampled() with PHP's GD library.
You should also run one of your test images through a lossless compression program like JStrip (http://davidcrowell.com/jstrip/), to check how much it weights without the bloat.

Related

Managing long/heavy functions in PHP

I'm using this:
# $file = '/path/existing_file.jpg';
# $info = getimagesize($file);
# $target = '/path/newfile.jpg';
function imgCreator($file, $info, $target, $size) {
list($width, $height) = $info;
$w = $size;
if ($size > 300) {
if ($size > 800) {
$h = ($size/16)*9;
} else {
$h = ($size/16)*10;
}
} else {
$h = $size;
}
$r = $width/$height;
if ($w/$h > $r) {
$nWidth = $h*$r;
$nHeight = $h;
} else {
$nHeight = $w/$r;
$nWidth = $w;
}
if ($info['mime'] == 'image/png') {
$src = imagecreatefrompng($file);
$dst = imagecreatetruecolor($nWidth, $nHeight);
imagealphablending($dst, false);
imagesavealpha($dst,true);
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
imagefilledrectangle($dst, 0, 0, $nWidth, $nHeight, $transparent);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $nWidth, $nHeight, $width, $height);
imagepng($dst, $target);
} else {
$src = imagecreatefromjpeg($file);
$dst = imagecreatetruecolor($nWidth, $nHeight);
imagecopyresampled($dst, $src, 0, 0, 0, 0, $nWidth, $nHeight, $width, $height);
imagejpeg($dst, $target);
}
imagedestroy($dst);
}
to create an image of specified resolution and width to height ratio based on wanted size.
It works well for making thumbnails etc. but any bigger size given, like let's say 2000px will cause problems. Basically the whole script including this function stops working as soon as it reaches the callout, with absolutely no echo or whatever output from whole script, even if it occured before executing it, however other operations before that function are being executed. Typically in this case I'm making a couple of diffrent sized images, and it seems like the $size is the only problem here.
I assume it would be perfect to do such tasks in the background, but I'm quite a beginner and I'm not sure, if I want to learn how to do this right now, especially if it's going to be only one person using this at a time.
So my question is, what is the cause of the problem here, and how should I deal with it?

Resizing/cropping images in PHP results in images with black border on one side

I created a function that takes images, resizes them to fit at least one dimension perfectly onto a canvas and finally crops out excess image content.
Process:
Take a 640x400 image
Scale image fit into 450x450 canvas (image is now 450x281)
Crop out excess data if any
The problem I'm encountering currently involves images that are too small for the newly created thumbnail (such as the one provided in the example above). I expected the output to either have a white background(JPGs) or a transparent background(PNGs). However, despite my best efforts, this tends to be the resulting mess:
As can be observed, the image has a black background in place of a transparent/white background. I am completely stumped on how to correct this flaw.
A copy of my code:
function create_thumbnail($path, $saveto, $width, $height) {
ini_set('memory_limit', '128M'); //Unlocking more memory for download
$info = getimagesize($path);
$rate = $info[0]/$info[1];
// Determine image size/position
if ($info[0] < $width || $info[1] < $height) {
// Image is too small
if ($info[0] < $width && $info[1] < $height) {
// Both width and height too small
$nw = $info[0];
$nh = $info[1];
} else if ($info[0] < $width) {
// Width is too small
$nw = ($info[0]*$height)/$info[1];
$nh = $height;
} else if ($info[1] < $height) {
// Height is too small
$nw = $width;
$nh = ($info[1]*$width)/$info[0];
}
} else {
// Image fits
if (($width/$height) > $rate) {
$nw = $width;
$nh = $width/$rate;
} else {
$nw = $height*$rate;
$nh = $height;
}
}
$nw = round($nw);
$nh = round($nh);
$x_mid = round($nw/2);
$y_mid = round($nh/2);
switch($info[2]) {
case IMAGETYPE_PNG :
$src = imagecreatefrompng($path);
break;
case IMAGETYPE_JPEG :
$src = imagecreatefromjpeg($path);
break;
case IMAGETYPE_GIF :
$src = imagecreatefromgif($path);
break;
default :
return false;
}
// Create image
$proc = imagecreatetruecolor($nw, $nh);
$clr = imagecolorallocate($proc, 255, 255, 255);
imagefill($proc, 0, 0, $clr);
imagecopyresampled($proc, $src, 0, 0, 0, 0, $nw, $nh, $info[0], $info[1]);
$thmb = imagecreatetruecolor($width, $height);
$clr = imagecolorallocate($thmb, 255, 255, 255);
imagefill($thmb, 0, 0, $clr);
imagecopyresampled($thmb, $proc, 0, 0, ($x_mid-($width/2)), ($y_mid-($height/2)), $width, $height, $width, $height);
if ($info[2] == IMAGETYPE_PNG || $info[2] == IMAGETYPE_GIF) {
$trnprt_idx = imagecolortransparent($src);
if ($trnprt_idx >= 0) {
// Attempt to forcefully correct transparencies using original image's color index
$trnprt_clr = imagecolorsforindex($src, $trnprt_idx);
$trnprt_idx = imagecolorallocate($thmb, $trnprt_clr['red'], $trnprt_clr['green'], $trnprt_clr['blue']);
imagefill($thmb, 0, 0, $trnprt_idx);
imagecolortransparent($thmb, $trnprt_idx);
imagealphablending($thmb, false);
imagesavealpha($thmb, true);
} else if ($info[2] == IMAGETYPE_PNG) {
// Attempt to forcefully correct transparencies by shutting down blending
$clr = imagecolorallocatealpha($thmb, 0, 0, 0, 127);
imagefill($thmb, 0, 0, $clr);
imagealphablending($thmb, false);
imagesavealpha($thmb, true);
}
}
switch($info[2]) {
case IMAGETYPE_PNG :
imagepng($thmb, $saveto);
break;
case IMAGETYPE_JPEG :
imagejpeg($thmb, $saveto, 100);
break;
case IMAGETYPE_GIF :
imagegif($thmb, $saveto);
break;
default :
return false;
}
return true;
} //End of create_thumbnail()
I have attempted to correct the transparency/coloring (as visible in my code), but it only affects one side of the image. Everything I have tried has either resulting in one side having a transparent/white background or both sides being completely black.
After a long time spent playing around trying to figure out what exactly was going on and breaking, I have found the solution.
The problem is here:
$proc = imagecreatetruecolor($nw, $nh);
$clr = imagecolorallocate($proc, 255, 255, 255);
imagefill($proc, 0, 0, $clr);
imagecopyresampled($proc, $src, 0, 0, 0, 0, $nw, $nh, $info[0], $info[1]);
$thmb = imagecreatetruecolor($width, $height);
$clr = imagecolorallocate($thmb, 255, 255, 255);
imagefill($thmb, 0, 0, $clr);
imagecopyresampled($thmb, $proc, 0, 0, ($x_mid-($width/2)), ($y_mid-($height/2)), $width, $height, $width, $height);
The newly created image $proc was not transferring it's transparency over to $thmb when I was doing imagecopyresampled from one to the other. The solution I found was to either skip creating/using $thmb altogether, or to save $proc as a png/gif first, then use that saved image for imagecopyresample.

PHP: Black strip on image while using GD library

I have a little problem with GD library in PHP - I resize image and then I want crop it to 320px (width) / 240px (height). Let me say that resized image is 320px/300px. When I crop it, a 1-px black strip appears on the bottom of the image - I don't know why.
I'm using imagecrop, imagecreatefromjpeg and imagecopyresampled
Here's the example:
Thanks for your time.
The code
$filename = '../store/projects/project-123.jpg';
$mime = mime_content_type($filename);
list($w, $h) = getimagesize($filename);
$prop = $w / $h;
$new_w = 0;
$new_h = 0;
if ($prop <= 4/3) {
$new_w = 320;
$new_h = (int)floor($h*($new_w/$w));
} else {
$new_h = 240;
$new_w = (int)floor($w*($new_h/$h));
}
$thumb = imagecreatetruecolor($new_w, $new_h);
if (strcmp($mime,'image/png') == 0) {
header('Content-Type: image/png');
$source = imagecreatefrompng($filename);
} else {
header('Content-Type: image/jpeg');
$source = imagecreatefromjpeg($filename);
}
imagecopyresampled($thumb, $source, 0, 0, 0, 0, $new_w, $new_h, $w, $h);
$filename = '../store/projects-thumbs/project-123.jpg';
$crop_data = array('x' => 0 , 'y' => 0, 'width' => 320, 'height'=> 240);
$thumb = imagecrop($thumb, $crop_data);
imagejpeg($thumb, $filename, 100);
imagedestroy($thumb);
imagedestroy($source);
imagecrop() has a known bug that causes the black bottom border to be added.
You can work around the problem using imagecopyresized(). See my answer to another SO question asking for an imagecrop() alternative.

Php Resizing image increase image size

I'm creating thumnails of uploaded file.
if image width and height are greater than 200 than then i re size them to 200px.
Here is code i used to do that:
if (file_exists($old_file)) {
$path_parts = pathinfo($old_file);
$extension = $path_parts['extension'];
$filename_path = $filepath . $filename;
$destination_path = $filename_path;
if (strtolower($extension) == "jpg" || strtolower($extension) == "jpeg") {
$uploadedfile = $old_file;
$src = imagecreatefromjpeg($uploadedfile);
} else if (strtolower($extension) == "png") {
$uploadedfile = $old_file;
$src = imagecreatefrompng($uploadedfile);
} else {
$uploadedfile = $old_file;
$src = imagecreatefromgif($uploadedfile);
}
list($width, $height) = getimagesize($uploadedfile);
$newwidth = $Size['width'];
$newheight = $Size['height'];
if ($width <= $newwidth && $height <= $newheight) {
$newwidth = $width;
$newheight = $height;
$tmp = imagecreatetruecolor($width, $height);
} else {
if ($width > $height) {
$newheight = ($height / $width) * $newwidth;
$tmp = imagecreatetruecolor($newwidth, $newheight);
} else {
$newwidth = ($width / $height) * $newheight;
$tmp = imagecreatetruecolor($newwidth, $newheight);
}
}
if ((strtolower($extension) == "png") OR (strtolower($extension) == "gif")) {
imagealphablending($tmp, false);
imagesavealpha($tmp, true);
$transparent = imagecolorallocatealpha($tmp, 255, 255, 255, 127);
imagefilledrectangle($tmp, 0, 0, $newwidth, $newheight, $transparent);
}
imagecopyresampled($tmp, $src, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
if (strtolower($extension) == "jpg" || strtolower($extension) == "jpeg") {
imagejpeg($tmp, $destination_path, 100);
} elseif (strtolower($extension) == "png") {
imagepng($tmp, $destination_path, 5);
} else {
imagegif($tmp, $destination_path);
}
chmod($destination_path, 0777);
imagedestroy($src);
imagedestroy($tmp);
ob_flush();
flush();
ob_end_flush();
return true;
} else {
return false;
}
it re size the large images to 200px by 200px but image size is increased in (byte and kb etc increased).
I tried uploading 8kb png file, and new thumbnail file size was 28kb?
Tried googling but didn't find anything helpful
Thanks.
Your source image is compressed, after parsing it you get a true color image, which is uncompressed. Then you save it with a compression level of 5 (in the case of PNG), which is pretty low compression, thus a higher filesize.
Try a higher compression, like 9, for example. Also try adding a combination of filters to decrease filesize (http://us3.php.net/manual/en/image.constants.php look for PNG_FILTER_*).
See: http://us3.php.net/manual/en/function.imagepng.php
http://en.wikipedia.org/wiki/Portable_Network_Graphics#Compression
http://en.wikipedia.org/wiki/Portable_Network_Graphics#File_size_factors
The GD library doesn't appear to provide any interfaces to let you in on low-level PNG data, but you can theoretically find out the source compression level and filters, by using other bindings or trying to read it manually.
http://www.libpng.org/pub/png/spec/1.2/PNG-Compression.html
http://www.libpng.org/pub/png/spec/1.2/PNG-Filters.html
The same may happen with JPG and GIF.

JPEG Images Turn all black when converting from a PNG with PHP

The problem: When converting any PNG image into a JPEG, the image turns all black.
To start off, I've searched the internet and stackoverflow to find out how to do this. I've tried every method I could find in the PHP Manual and on Stack Overflow. The problem still exists. I'm using GD (don't have ImageMagick installed).
My code is below. This is the call to the function:
$tempImage = $dirPath.$filename.$tempMini.".jpg";
createTempImage($sourcefile, $tempImage, $tempMini_width, $tempMini_height, 100);
I've commented out the different methods that I've tried.
function createTempImage($sourcefile, $setNewName, $maxwidth, $maxheight, $quality){
$fileInfoArray = getimagesize($sourcefile);
$imagetype = $fileInfoArray['mime'];
if($imagetype == 'image/jpeg'){
$img = imagecreatefromjpeg($sourcefile);
}elseif($imagetype == 'image/gif'){
$img = imagecreatefromgif($sourcefile);
}elseif(($imagetype == 'image/png')||($imagetype == 'image/x-png')){
$img = imagecreatefrompng($sourcefile);
}
$width = imagesx( $img );
$height = imagesy( $img );
if ($width > $maxwidth || $height > $maxheight){
$factor = min(($maxwidth/$width),($maxheight/$height));
$newwidth = round($width*$factor);
$newheight = round($height*$factor);
} else {
$newwidth = $width;
$newheight = $height;
}
$tmpimg = imagecreatetruecolor( $newwidth, $newheight );
imagecopyresampled($tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );
imagejpeg($tmpimg, $setNewName, 100);
imagedestroy($tmpimg);
imagedestroy($img);
}
The following have also been attempted:
$white = imagecolorallocate($tmpimg, 255, 255, 255);
ImageFill($tmpimg, 0, 0, $white);
ImageSaveAlpha($tmpimg, false);
ImageAlphaBlending($tmpimg, false);
$white = imagecolorallocate($tmpimg, 255, 255, 255);
imagefilledrectangle($tmpimg, 0, 0, $newwidth, $newheight, $white);
Update: The top black box is the image result: http://twitpic.com/30ywf5
Just a couple of ideas:
$newHeight = $maxheight; seems to be a typo, "newheight" is spelled without the capital "H" throughout the code.
The code to determine the new size can be shortened sigificantly:
if ($width > $maxwidth || $height > $maxheight){
$factor = min(($maxwidth/$width),($maxheight/$height));
$newwidth = round($width*$factor);
$newheight = round($height*$factor);
}
You use imagecopyresampled to create the new image - this only works in specific GD-versions ("version 2"), try to use imagecopyresized otherwise.
I seemed to have fixed the problem by recreating the whole function from scratch. Thank you guys for your input.
The problem was that PNG's weren't being uploaded. When executing the script with already uploaded URLs, it worked fine.
Thanks again.

Categories