On a PWA, full legacy/handmade PHP/JS (jquery), i'm trying to avoid all the bug and problems with picture.
I've solved the famous bug where picture are 90° rotate
I've dicreased the weight with the "quality" parameter in imagejpeg
And i'm trying to resize it with no less than 800px for width and no more less 600px for height. Like, 810x750 it's okay for me, 1500x620 too, but no 500x450.
1 and 2 work fine. The third point is bit complicated it "crop" my picture with false position and there is this famous black bar on right.
I've read a lot of articles on it with sometimes solutions, but they are always "isolated" scripts, intended for the black bar. I want to solve my problem by keeping this big function which makes resize/rotate/decrease weight.
I don't want a copy/thumbnail, just the original with correct size.
Here's my code
function compressImage($source, $destination, $quality) {
error_log('====================');
error_log('Source : '.$source);
error_log('Destination : '.$destination);
error_log('Qualité : '.$quality);
$imgInfo = getimagesize($source);
$mime = $imgInfo['mime'];
// EXTRACT INFO
$newPic = imagecreatefromstring(file_get_contents($source));
$exif = exif_read_data($source);
$rotation = 0;
if(!empty($exif['Orientation'])) {
error_log('Orientation : '.$exif['Orientation']);
// CHECK IF PHP DID WRONG ROTATION
switch($exif['Orientation']) {
case 8:
$newPic = imagerotate($newPic, 90, 0);
break;
case 3:
$newPic = imagerotate($newPic, 180, 0);
break;
case 6:
$newPic = imagerotate($newPic, -90 ,0);
break;
}
$rotation = 1;
time_nanosleep(0, 500000000);
}
$w = 800;
$h = 600;
list($width, $height) = $imgInfo;
error_log('Actual width : '.$width);
error_log('Actual height : '.$height);
$r = $width / $height;
if($w/$h > $r) {
$newWidth = $h*$r;
$newHeight = $h;
} else {
$newHeight = $w/$r;
$newWidth = $w;
}
// CALCULATE NEW SIZE (MAYBE HERE FOR MISTAKE ?)
error_log('New width : '.$newWidth);
error_log('New height : '.$newHeight);
$thumb = imagecreatetruecolor($newWidth, $newHeight);
if($rotation == 1) {
error_log('2nd MIME : '.$mime);
switch($mime) {
case 'jpg':
error_log('JPG');
$newPic = imagejpeg($image, $destination, $quality);
break;
case 'jpeg':
error_log('JPEG');
$newPic = imagejpeg($image, $destination, $quality);
break;
}
time_nanosleep(0, 500000000);
}
imagecopyresized($thumb, $newPic, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
$newPic = imagejpeg($thumb, $destination, $quality);
move_uploaded_file($newPic, $destination);
}
Here's a complete example :
Randomly wanted 9000x12000 to be 800x600 but for example if someone sends a picture of 1685x1235 (random) i want 819x600. (if we took the width as a reference in this specific case instead it would not be good because we would have 800x586 (and therefore 586 < 600)
Edit : after two weeks of tests i can't afford what i want, thanks to everyone who have solution to resize, prevent rotate bug and crop in one script.
Related
In my old PHP apps i used to run a function like the one bellow to create jpeg image thumbnails.
function imageThumbanail() {
$image_src = imagecreatefromjpeg('http://examplesite.com/images/sample-image.jpg');
$thumbnail_width = 180; //Desirable thumbnail width size 180px
$image_width = imagesx($image_src); //Original image width size -> 1080px
$image_height = imagesy($image_src); //Original image height size -> 1080px
$thumbnail_height = floor( $image_height * ( $thumb_width / $image_width ) ); //Calculate the right thumbnail height depends on given thumbnail width
$virtual_image = imagecreatetruecolor($thumbnail_width, $thumbnail_height);
imagecopyresampled($virtual_image, $image_src, 0, 0, 0, 0, $thumbnail_width, $thumbnail_height, $image_width, $image_height);
header('Content-Type: image/jpeg');
imagejpeg($virtual_image, null, 100);
imagedestroy($virtual_image); //Free up memory
}
The problem is that now i'd like to run a similar function in a laravel 5.6 app, so i created a controller with the exact same function but instead of get as an output the image thumbnail i get question marks and strange diamond icons, something like encoded version of php gd library jpeg image.
I tried to use return response()->file($pathToFile); as laravel documentation describes but i won't store in a location the thumbnail image.
Any ideas?
Thank you in advance!
I recommend you this package is very simple to install and use it and very kind with programming.
Its called Intervention
Intervention package to handle images
You can make a thumbnail very simple like the following :
$img = Image::make('public/foo.jpg')->resize(320, 240)->insert('public/watermark.png');
Resize and crop image by center
No need to install and include any package. Just create a helper in laravel / lumen and put below code in that helper file and use it wherever you want:
function resize_crop_image($max_width, $max_height, $source_file, $dst_dir, $quality = 80){
$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 = 7;
break;
case 'image/jpeg':
$image_create = "imagecreatefromjpeg";
$image = "imagejpeg";
$quality = 80;
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);
}
//usage example
resize_crop_image(100, 100, "test.jpg", "test.jpg");
Code is already tested many times and working well. All the best, save your time and enjoy your life :)
I'm building a wallpaper website and therefore i need to be able to resize the original image before downloading. I tried this code to resize the image:
//resize and crop image by center
function resize_crop_image($max_width, $max_height, $source_file, $dst_dir, $quality = 80){
$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 = 7;
break;
case 'image/jpeg':
$image_create = "imagecreatefromjpeg";
$image = "imagejpeg";
$quality = 80;
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);
}
And this will do the resize:
resize_crop_image($width, $height, $image_URL, $save_URL)
This code works fine for me but i want to ONLY send the output to user's browser, since saving thousands of extra images isn't possible. There are libraries i can use, but i don't want to use a third party snippet. Is there a way to alter this code in the way i desire?
Thanks.
For your $image function , do not specify a destination directory (null) and an image stream will be created.
header("Content-type:{$mime}");
That should send the image to the user
You just need to set the proper headers and echo the output. Every PHP request "downloads a file" to the browser, more or less. You just need to specify how the browser handles it. So try something like:
header("Content-Type: $mime");
header("Content-Disposition: attachment; filename=$dst_img");
echo $dst_img;
That should do it for you.
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.
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.
I use this function to resize images but i end up with ugly creepy image with a black background if it's a transparent GIF or PNG with alpha, however it works perfectly for jpg and normal png.
function cropImage($nw, $nh, $source, $stype, $dest) {
$size = getimagesize($source);
$w = $size[0];
$h = $size[1];
switch($stype) {
case 'gif':
$simg = imagecreatefromgif($source);
break;
case 'jpg':
$simg = imagecreatefromjpeg($source);
break;
case 'png':
$simg = imagecreatefrompng($source);
break;
}
$dimg = imagecreatetruecolor($nw, $nh);
switch ($stype)
{
case "png":
imagealphablending( $dimg, false );
imagesavealpha( $dimg, true );
$transparent = imagecolorallocatealpha($dimg, 255, 255, 255, 127);
imagefilledrectangle($dimg, 0, 0, $nw, $nh, $transparent);
break;
case "gif":
// integer representation of the color black (rgb: 0,0,0)
$background = imagecolorallocate($simg, 0, 0, 0);
// removing the black from the placeholder
imagecolortransparent($simg, $background);
break;
}
$wm = $w/$nw;
$hm = $h/$nh;
$h_height = $nh/2;
$w_height = $nw/2;
if($w> $h) {
$adjusted_width = $w / $hm;
$half_width = $adjusted_width / 2;
$int_width = $half_width - $w_height;
imagecopyresampled($dimg,$simg,-$int_width,0,0,0,$adjusted_width,$nh,$w,$h);
} elseif(($w <$h) || ($w == $h)) {
$adjusted_height = $h / $wm;
$half_height = $adjusted_height / 2;
$int_height = $half_height - $h_height;
imagecopyresampled($dimg,$simg,0,-$int_height,0,0,$nw,$adjusted_height,$w,$h);
} else {
imagecopyresampled($dimg,$simg,0,0,0,0,$nw,$nh,$w,$h);
}
imagejpeg($dimg,$dest,100);
}
Example : cropImage("300","200","original.png","png","new.png");
I use php 5.3.2 and the GD library bundled (2.0.34 compatible)
How to make it support transparency? i've added imagealphablending() and imagesavealpha but it didn't work. Or atlast is there any similar good classes?
Thanks
The ugly black background disappears if you output the image to png. So here are the two alternative solutions, both tested:
If you can store the thumbnail as png, just do it: change imagejpeg($dimg,$dest,100); to imagepng($dimg,$dest);
If it's important to store it as jpeg, remove the lines imagealphablending( $dimg, false ); and imagesavealpha( $dimg, true ); -- the default values of true and false, respectively, will provide the desired effect. Disabling alpha blending only makes sense if the result image also has an alpha channel.
I haven't tested it myself but this was an idea I've had for doing exactly that when working on a project of mine.
First, find a color that isn't used in the image and create a new image with that as the background (really flashy green for example, just like they do with motion capture)
Next, copy your image with transparency over it (I know this works with PHP I used to do this to put borders over images)
Then, use the function imagecolortransparent to define which color in that image is transparent and give it your flashy green color.
I think it would work but I haven't tested it.