I have a site where users can upload images. I process these images directly and resize them into 5 additional formats using the CodeIgniter Image Manipulation class. I do this quite efficiently as follow:
I always resize from the previous format, instead of from the original
I resize using an image quality of 90% which about halves the file size of jpegs
The above way of doing things I implemented after advise I got from another question I asked. My test case is a 1.6MB JPEG in RGB mode with a high resolution of 3872 x 2592. For that image, which is kind of borderline case, the resize process in total takes about 2 secs, which is acceptable to me.
Now, only one challenge remains. I want the original file to be compressed using that 90% quality but without resizing it. The idea being that that file too will take half the file size. I figured I could simply resize it to its' current dimensions, but that doesn't seem to do anything to the file or its size. Here's my code, somewhat simplified:
$sourceimage = "test.jpg";
$resize_settings['image_library'] = 'gd2';
$resize_settings['source_image'] = $sourceimage;
$resize_settings['maintain_ratio'] = false;
$resize_settings['quality'] = '90%';
$this->load->library('image_lib', $resize_settings);
$resize_settings['width'] = $imagefile['width'];
$resize_settings['height'] = $imagefile['height'];
$resize_settings['new_image'] = $filename;
$this->image_lib->initialize($resize_settings);
$this->image_lib->resize();
The above code works fine for all formats except the original. I tried debugging into the CI class to see why nothing happens and I noticed that the script detects that the dimensions did not change. Next, it simply makes a copy of that file without processing it at all. I commented that piece of code to force it to resize but now still nothing happens.
Does anybody know how to compress an image (any image, not just jpegs) to 90% using the CI class without changing the dimensions?
I guess you could do something like this:
$original_size = getimagesize('/path/to/original.jpg');
And then set the following options like this:
$resize_settings['width'] = $original_size[0];
$resize_settings['height'] = $original_size[1];
Ok, so that doesn't work due to CI trying to be smart, the way I see it you've three possible options:
Rotate the Image by 360ยบ
Watermark the Image (with a 1x1 Transparent Image)
Do It Yourself
The DIY approach is really simple, I know you don't want to use "custom" functions but take a look:
ImageJPEG(ImageCreateFromString(file_get_contents('/path/to/original.jpg')), '/where/to/save/optimized.jpg', 90);
As you can see, it's even more simpler than using CI.
PS: The snippet above can open any type of image (GIF, PNG and JPEG) and it always saves the image as JPEG with 90% of quality, I believe this is what you're trying to archive.
Related
I have a straightforward PHP script (php version 7+) that I run to resize images to a smaller dimension around 1200px wide ...
I use imagecreatefromjpeg and imagecopyresampled method and the save new file with imagejpeg # 88% compression.
I and ran over 100 images through it and most images worked out great, except for a few stubborn images that results to BLACK square image.
I tried imagecopyresized and played with different new dimensions but it just didnt work. I even tried outputting the ob stream from these tests at the point of resizing instead of saving to file (no compressions applied) and i can confirm that the moment the image was resized, it is already black square.
On the images that produces black square the result is false.
I did verify mime type IMAGETYPE_JPEG of file, regardless of file extension, before running the code.
Now, I tried running the same files thru imagescale instead
$new_img = imagescale($img, $new_width, $new_height, IMG_SINC );
and that worked.
Now I prefer to use the previous method for better more crispier output so I've been trying to get it to work :(
Why is this happening? I searched stack for similar issues but they are describing a issues that completely didnt work on any images, not my case, where only a select few images had issues.
I am dealing with JPG only, but is it possible that the jpg file is just named with extension ".jpg" but actually a png, bmp, or something else? Will that cause issues?
I'm stuck, and i hope you guys can help.
Thank you
Most images resized nicely
I'm trying to downsample every image that I consider large for my site on uploading, so when a user tries to upload an image I firstly check if it has an acceptable resolution, otherwise I want to drop this resolution. The code I use for this is:
if ($image->isValid()){
$imagick = new \Imagick();
$imagick->readImage($image);
$resolution = $imagick->getImageResolution();
$resolution_x = $resolution['x'];
$resolution_y = $resolution['y'];
if ($resolution_x > 30 && $resolution_y > 30){
$imagick->setImageResolution($resolution_x,$resolution_x);
$imagick->resampleImage($resolution_x/2,$resolution_x/2,\imagick::FILTER_CATROM,1);
}
$imagick->writeImage($uploadDir.$path);
}
This code was supposed to set the resolution of an image with resolution 300 dpi for example to 150dpi. Instead of this, the resolution remains 300 dpi and the image dimensions drop to the half of their previous values (e.g an image (1200x800) turns into (600x400)). Am I missing something about the functionality of Imagick::resampleImage or is there any error in my code? I've done a lot of search before post this question and tried lot of different ways to succeed my goal using Imagick but I cannot get it done!
The 'resolution' in the setImageResolution and getImageResolution functions refer to the dots per inch setting of the image, which is a hint to printers of what size to print the image i.e. how many dots per inch it should be printed at.
It does not affect the pixel dimensions of the image, and so does not have a noticeable effect on the image on a computer, which does not use the DPI setting to render an image.
You want to use either just $imagick->getImageWidth() and $imagick->getImageHeight() or $imagick->getImageGeometry() to get the pixel size of the image, and then resample it based on those pixel dimension, rather than the printer hint setting.
It sounds like the resolution values should be the same in both setImageResolution and resampleImage. Have you tried this yet?
$imagick->setImageResolution($resolution_x/2,$resolution_x/2);
I am making an avatar script from scratch and am having some problems. I got transparency working, and multi-image support for heads, bodies, shirts, etc.
Anyhow, I want to be able to generate specific sizes of the avatar within the PHP script. At this time, I have the variable $baseImage, which is an image generated using the GD script below:
$baseImage = imagecreatefrompng($startAsset);
imagealphablending($baseImage, true);
imagesavealpha($baseImage, true);
... combine all images into $base here
header("Content-type: image/png");
imagepng($baseImage);
The size of the image this generates is 350x550 (pixels) and I want to be able to get a smaller size.
I've done research but cannot find a working solution. What built-in PHP GD functions can resize this, retain transparency, and keep the great quality/colors?
There is no way to change the size of an image resource directly. Instead, you need to create a new image of the desired size and use imagecopyresampled to copy from the fullsize image to the resized one.
I have a simple image upload script that uses SimpleImage.php
(http://www.white-hat-web-design.co.uk/blog/resizing-images-with-php/)
to resize and save 2 copies of an uploaded image.
There isn't a massive amount of validation, just checking that it exists and that the file extension is fine, and also an exif_imagetype(); call.
This has worked with no problems so far until I tried to upload a seemingly normal jpeg which turned out to be invisibly (and untestably?) corrupt. There was something not right about it, but I know very little about image corruption - it looked fine and opened no problem on anything, but when I tried to save a scaled copy in my script I got a white page.
The problem is definitely that specific image, I've tested exhastively with other images both from my local stock and from stock image sites, and only that one image breaks it.
I resized a copy using Photoshop (the predicted file size thingy gave me some wierd numbers - 45meg for top quality jpeg) and that uploaded with no issues.
So my question is, how do I test for this?
The image in question is here: http://chinawin.co.uk/broken.jpg //beware, 700k
notes: I've tested with similar resolutions, image sizes and names, everything else worked apart from this image.
UPDATE:
Through trial and error I've narrowed down where the script breaks to the line where I load the image into a var for SimpleImage. Strangely this is the second line that does so (the first being to create the large copy, this one to create a thumbnail).
Commenting it out means the rest works ok... perhaps some refactoring will avoid this problem.
2nd Update:
Here's a snippet of code and some context from the line that fails:
//check if our image is OK
if ($image && $imageThumb)
{
//check if image is a jpeg
if (exif_imagetype($_FILES[$k]['tmp_name']) == IMAGETYPE_JPEG)
{
list($width, $height, $type, $attr) = getimagesize($_FILES[$k]['tmp_name']);
//echo 1;
$image = new SimpleImage();
//echo 2;
$image->load($_FILES[$k]['tmp_name']);
//echo 3;
$imageThumb = new SimpleImage();
//echo 4;
//this next line topples my script, but only for that one image - why?:
$imageThumb->load($_FILES[$k]['tmp_name']);
//echo '5<br/><br/>-------<br/>';
//do stuff, save & update db, etc
}
}
Final edit:
Turns out my script was running out of memory, and with good reason - 4900x3900 image with 240 ppi turns out to be around 48 meg when loaded into memory, twice - so I was using probably > 90meg of ram, per image.
Hats off to #Pekka for spotting this.
Refactoring the script to only have the image loaded once, and then this variable used instead of it's sibling, fixed my script. Still having (different) issues with upoading larger (2.5meg) images but this is for another question.
This is most likely a memory issue: Your JPG is very large (more than 4000 x 4000 pixels) and, uncompressed, will indeed eat up around 48 Megabytes of RAM.
Activate error reporting to make sure. If it's the reason, see e.g. here on what to do: Uploading images with PHP and hitting the script memory limit
I was using the firebug page speed utility and one of the suggestions given was to compress the images - So I wrote the following code to compress the image
$filename="http://localhost.com/snapshots/picture.png";
$img = imagecreatefrompng($filename);
$this->_response->setHeader('Content-Type', 'image/png');
imagepng($img,null,9);
imagedestroy($img);
Now the actual image size is 154K
So I experimented by giving different quality levels and here is what I found
imagepng($img,null,0); --> Size = 225K
imagepng($img,null,1); --> Size = 85.9K
imagepng($img,null,2); --> Size = 83.7K
imagepng($img,null,3); --> Size = 80.9K
imagepng($img,null,4); --> Size = 74.6K
imagepng($img,null,5); --> Size = 73.8K
imagepng($img,null,6); --> Size = 73K
imagepng($img,null,7); --> Size = 72.4K
imagepng($img,null,8); --> Size = 71K
imagepng($img,null,9); --> Size = 70.6K
Do these results look accurate - I'm not sure why with quality 0 - the image size is larger than the actual size.
Secondly is this the best way to go about in PHP to compress images before rendering them in the browser to improve performance.
Based on the suggestions, that its better to compress the image once at the time of saving - I digged up the code that is called by the flash program to generate the snap shot -
$video = $this->_getParam('video');
$imgContent = base64_decode($this->_getParam('snapshot'));
file_put_contents("snapshots/" . $video . ".png", $imgContent);
EDITED
Based on Alvaro's suggestion, I have made the following modification to the code which generates a much small jpg file
$video = $this->_getParam('video');
$imgContent = base64_decode($this->_getParam('snapshot'));
file_put_contents("snapshots/" . $video . ".png", $imgContent);
$filename="snapshots/".$video.".png";
$img = imagecreatefrompng($filename);
imagejpeg($img,'test.jpg',75);
So now this is a 3 step process
create the initial image using file_put_contents
Use imagecreatefrompng and imagejpeg to compress the file and generate a smaller image
Delete the orig image
Is this the best optimal way to go about it.
Since PNG uses lossless data compression, the only way to achieve a decent compression in a PNG image (edge cases apart) is to save it as palette (rather than true colour) and reduce the number of colours. You appear to be processing some sort of screenshots. You may obtain smaller file sizes is you use a lossy compression, i.e., save as JPEG. In either cases, you reduce both file size and picture quality. You could also try the GIF format, which tends to be smaller for small graphs.
Last but not least, you should compress images once (typically when they get uploaded), not every time they're served. I suppose your code is just a quick test but I mention just in case.
Answer to updated question:
I'm not familiar with PHP image functions but you should probably use a combination of imagecreatefrompng() and imagejpeg(). Also, consider whether you need to keep the original PNG for future reference or you can discard it.
You have not understood the last parameter, that's not quality that's the compression level so increasing it will decrease the image size. Anyway i've used that method before to compress png images and it works well so i think you should continue to use it.
1- The result seems accurate since 0 means no compression
quality
Compression level: from 0 (no
compression) to 9.
It's normal for the 0ed file to be larger than the original (that can be slightly compressed to begin with). You need to understand file compression and PHP GD image constructor.
2- IMHO, the wisest choice would be to compress your png file before uploading them on your server (of course, it states only if you have the choice : static, few files).
Help for that :
http://www.webreference.com/dev/graphics/compress.html
http://www.punypng.com/
http://omaralzabir.com/reduce_website_download_time_by_heavily_compressing_png_and_jpeg/
If it means to be dynamic, the php is the choice.