How to reduce file size using PHP and JPEGs - php

I am using a the standard PHP functions imagecopytruecolor and imagejpeg
to rescale and produce uploaded images from a standard HTML form. The images appear at the correct size however the image filesize is quite high (e.g. 540px * 350px = 250kb)
When compared to Photoshop's Save for Web using JPEG high quality settings the same files are come out at about 60kb, so about 4 times as small.
Is there anything I can do to reduce the filesize?

You can set $qualityin imageJPEG. From the manual:
bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )
quality is optional, and ranges from 0 (worst quality, smaller file) to 100 (best quality, biggest file). The default is the default IJG quality value (about 75).
In my experience, it is not advisable to go much under 70%, though.
You may want to try whether you can get better results with smaller file sizes from other image processing engines like ImageMagick, if you can use that. I often have the feeling that GD's JPG encoder is not top of the line, but that's nothing more than a subjective impression at the moment.

No image library I've ever seen comes anywhere close to how efficiently Photoshop compresses a file. You could try using another library like ImageMagick (aka Imagick) which might saves smaller files at the same quality size than the default stuff your using, but keep in mind you'll never get a 60K image from these libraries at this res.

You can change the quality setting (3rd argument of imagejpeg, see the docs). The default is only 75% though, which should already be rather small.

From the docs
bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )
experiment with $quality

Related

How efficient is a PHP image resource?

I have a very large PNG image, and I am writing a method to get the value for a color at a specific (but changing) pixel of that image. When I create the image using:
$image = imagecreatefrompng('map.png');
Is the whole image loaded into memory (not ideal), or does it just read the meta data and prepare for other calls so that when I call:
int imagecolorat ( resource $image , int $x , int $y )
Will it file seek to the right pixel or pull from memory? If I'm trying to optimize this routine to be called repeatedly, would I be better off converting the image data I need into some raw binary format and using file seek? I'd like to avoid repeatedly loading the whole file into memory if possible.
You need a big php memory to play with php image resources.
Use graphicsMagick instead. http://www.graphicsmagick.org/

Detect JPEG image quality

I allow users to upload images. However, I want to keep JPEG quality not more than 90%. What I plan to do is to detect the current quality:
- If less than 90% do nothing
- If more than 90%, than use Image Magick to recompress the image to 90%
Is it possible to do that? I prefer PHP but any language will help.
paddy is correct that this setting is not always stored in the JPEG file. If it is, then you can use identify from Imagemagick to read the quality. For example:
$ identify -format '%Q' tornado_ok.jpg
93%
Update: Based on the answer to this question
https://superuser.com/questions/62730/how-to-find-the-jpg-quality I
find out that apparently the identify command can still determine
the quality by reverse engineering the quantization tables even if all
the image EXIF / other meta data is lost. By the way, the title of
your question as it stands now is a possible duplicate of that
question I linked to.
But to me your question has merit on its own because in your
question's text you explain what you are trying to do, which is more
than simply detecting jpeg quality. Nevertheless, you should perhaps
update the title if you want to reflect that you are trying to solve a
more specific problem than just reading JPEG image quality.
Unless you are archiving original images, for web use even 90% is excessive. 75% used to be the default in the old days (degradation was visible only under close inspection between side-by-side images), and now in the days of high bandwidth 85% is a very high quality option. The 5% quality difference between 90% and 85% is virtually invisible, but will save you over 30% in file size typically. The JPEG algorithm is designed to begin by eliminating information that is invisible to human perception at its first compression stages (above 80% or so).
Update/note: The compression quality settings I am talking about are
from tests with libjpeg, a very widely used JPEG library. Photoshop's
compression percentages and other software's quality settings are all
independent and do not necessarily mean the same thing as the settings
of libjpeg.
paddy's idea of using image height and image width to calculate an acceptable file size is reasonable:
You can get the image height/width like this:
list($originalWidth, $originalHeight) = getimagesize($imageFile);
My own high-quality photos posted online, like this one: http://ksathletics.com/2013/wsumbb/nw.jpg
are typically saved at a ratio of about 200 KB per megapixel.
So, for example, you can multiply width times height and divide by 1000000 to calculate the megapixels in the image. Divide the file size by 1024 to calculate the KB. Then divide the resulting KB by the megapixels. If the result is under 200 or whatever value you decide upon, then you don't need to re-compress it. Otherwise, you can re-compress it with a quality of 85% or whatever quality you decide on.
Since the OP stated he prefers php, I offer the following:
$img = new Imagick($filename);
$quality = $img->getImageCompressionQuality();
whats up? I was facing the same problem with an app I'm developing... My problem is that, I extract several images from a random site and each item have several images, i would like to show a single image for each item, and bring to the user the best quality image.
I came out with this idea, its pretty simple, and will work for any language and any type of compression:
//Parameters you will need to retrieve from image
$width = 0;
$height = 0;
$filesize = 0;
//Quality answer for your image
$quality = (101-(($width*$height)*3)/$filesize);
I ran this algorithm against the http://fotoforensics.com/tutorial-estq.php mentioned above and here are the results:
Filename Width Height Pixels BitmapBytes FileBytes Quality
estq-baseline.png 400 300 120000 360000 163250 98,79
estq-90.jpg 400 300 120000 360000 34839 90,67
estq-80.jpg 400 300 120000 360000 24460 86,28
estq-70.jpg 400 300 120000 360000 19882 82,89
estq-25.jpg 400 300 120000 360000 10300 66,05
The basic idea behind this algorithm is compare the size that an image could reach if written in a bitmap way (without any compression, 3 bytes per pixel, 3 bytes for RGB) to the size that this image is currently using. The smaller is the image size, the higher is the compression, independently of the compression method used, rather its a JPG, a PNG or whatever, and that will lead us into having a bigger gap or smaller gap between uncompressed and compressed image.
It is also important to mention, that this is a mathematical solution for comparison purposes, this method will not return the actually quality of the image, it will answer the distance percentage between uncompressed and compressed sizes!
if you need more details, you can send me an email: rafaelkarst#gmail.com
You cannot guarantee that the quality setting is stored in the JPEG's metadata. This is an encoder setting, not an image attribute.
Read more here about estimating JPEG quality
It might make more sense to simply define a maximum file size. At the end of the day, restricting image quality is all about saving bandwidth. So setting a ratio between image dimensions and file size is more appropriate.
If your jpeg was created using a straight scaling of the standard image quantization tables and the 0-100 quality was used based on the Independent JPEG Group's formula, then, assuming you have the luminance quantization tables in an array called quantization (such as Python's PIL module provides in image.quantization[0]), then the original value can be obtained via:
if quantization[58] <= 100:
originalQuality = int(100 - quantization[58] / 2)
else:
originalQuality = int(5000.0 / 2.5 / quantization[15])
Basically, the default luminance quantization value #15 is 40 and #58 is 100, so these make handy values to extract the results from. IJG scales values about 50 via 5000 / Q and below 50 via 200 - 2 * Q. If the quality setting is less than 8, this won't give decent results (if quantization[5] == 255) -- in that case, perhaps use quantization table position #5
See https://www.rfc-editor.org/rfc/rfc2435#section-4.2.
For those who are using GraphicsMagick instead of ImageMagick, you can get the JPEG quality with the following command:
gm identify -format '%[JPEG-Quality]' path_to/image_file.jpg
and according to the documentation
http://www.graphicsmagick.org/GraphicsMagick.html#details-format
Please note that JPEG has no notion of "quality" and that the quality metric used by, and estimated by the software is based on the quality metric established by IJG JPEG 6b. Other encoders (e.g. that used by Adobe Photoshop) use different encoding metrics.
Here is a PHP function that tries all available methods of getting quality (that I know of):
/* Try to detect quality of jpeg.
If not possible, nothing is returned (null). Otherwise quality is returned (int)
*/
function detectQualityOfJpg($filename)
{
// Try Imagick extension
if (extension_loaded('imagick') && class_exists('Imagick')) {
$img = new Imagick($filename);
// The required function is available as from PECL imagick v2.2.2
if (method_exists($img, 'getImageCompressionQuality')) {
return $img->getImageCompressionQuality();
}
}
if (function_exists('shell_exec')) {
// Try Imagick
$quality = shell_exec("identify -format '%Q' " . $filename);
if ($quality) {
return intval($quality);
}
// Try GraphicsMagick
$quality = shell_exec("gm identify -format '%Q' " . $filename);
if ($quality) {
return intval($quality);
}
}
}

Using imagejpeg to make image files unnoticeable smaller

I'm making an image sharing website with php. I was reading about google's pagespeed, and they said I needed to optimize my images, so I've been trying to do that with imagejpeg, then hopefully I'll be able to use basically the same code with png and gif.
This is the code I have at the moment:
$img = imagecreatefromjpeg($original);
if ($extension === "jpg" || $extension === "jpeg"){
$location = $_SERVER['DOCUMENT_ROOT'] . "/uploads/" . $id . "/fullsize." . $extension;
$temp = fopen($location, 'w');
imagejpeg($img, $location, 80);
fclose($temp);
}
And it does the job of taking the image at $original, and saving a copy that's been through imagejpeg to $location.
But sometimes the resulting file is larger than the original was! This is where I get stumped.
Why is this happening?
How can I make it stop happening?
I'm trying to reduce image file sizes without hurting their appearance, is there a better way to do it than this?
Thanks,
Liam
imagejpeg will only shrink the file if you save it at a lower quality setting than the original was produced with
e.g.
imagejpeg($gd, 'temp.jpg', 50);
$temp = imagecreatefromjpeg('temp.jpg');
imagejpeg($gd, 'temp2.jpg', 70);
will probably produce a larger temp2 file, because it's at a higher quality setting, which roughly translates to "don't compress as much".
There's no way to predict what the final output size will be for a given image and quality setting without actually going through the whole jpeg compress process. You may have to do a few trial recompresses of an image and lower the quality setting each time until you start getting some shrinkage.
of course, if you lower the quality TOO low, you'll basically trash the image and definitely introduce visible artifacts.
It definitely depends on the quality of the original image. If your image is quality 60 originally, saving at quality 80 will generally result in a larger file size.
However, it also depends on what software created the original image. For example, I've observed many times where Adobe Photoshop can save an image at quality 60 that looks as good as 80 from GD (the graphics library you are using). Also, Photoshop can often save images at the same quality as another graphics package but with a smaller size. This is subjective, of course. I'm not necessarily concluding Photoshop is "better," but it does appear to have it's options well tuned.
I recommend you try ImageMagick, as I've seen it generally do a better job (file sizes, image quality) than GD. You should also determine the original quality setting per image so you don't accidentally grow images.

imagecreatetruecolor() makes my image lose quality

I'm using the script below to create an JPG based on people uploading an image. What happens is that even if an image is the exact same width/height it still loses quality so much that it's unacceptable.
$im = imagecreatefromjpeg($_FILES['cpicture']['tmp_name']);
$thumb=imagecreatetruecolor(507,307);
ImageCopyResampled($thumb,$im,0,0,0,0,$newwidth,$newheight,ImageSX($im),ImageSY($im));
ImagejpeG($thumb,"uploads/".$randomvalue.".jpg");
$imgurl="uploads/".$randomvalue.".jpg";
Are these functions reducing the quality?
imagejpeg's third parameter is the quality of the image to be output:
quality is optional, and ranges from 0 (worst quality, smaller file) to 100 (best quality, biggest file). The default is the default IJG quality value (about 75).
Try changing the relevant line of your code to:
ImagejpeG($thumb,"uploads/".$randomvalue.".jpg", 100);
JPEG always loses quality because it is a lossy compressor.
If you don't want that, use e.g. png.
Yeah or set the quality of the output to 100:
ImagejpeG($thumb,"uploads/".$randomvalue.".jpg", 100);

GD imagejpeg() compression?

I'm working on a project where I edit the pixels of a jpg in PHP using the GD library.
Its very crucial and key to my project that the output from PHP retains the pixel values I set (cough-steganography-cough).
This is the first time I've attempted image manipulation in PHP. I've had no problems before in my Java implementations, so I was foolish not to investigate GD's jpeg compression quality before pursuing further.
It turns out that after all my effort, my code does not function the way its supposed to. I am pretty sure it is not my code (tested the encode and decode functions on edited image resources and they worked).
My questions are:
The only problem I can see is with imagejpeg()'s compression. Could I be right?
Are there any libraries that provide the compression that I need?(retain pixel values that I changed)
By default the quality of the image output from imagejpeg is 75, Try setting it at 100 to get the image at full quality.
bool imagejpeg ( resource $image [, string $filename [, int $quality ]] )
Check the manual for further details.
Also try using imagecopyresampled. (I think you would have been using imagecopyresized somewhere in your code. Use imagecopyresampled instead of it. )
EDIT
Then I think you should try ImageMagick(or GD2). It gives a better quality than GD . Check this
The JPEG file format is not very suitable for steganography if you do the steganography pixel by pixel.
JPEG uses an image compression - that even with maximum quality - that will destroy the information on the bit level on each pixel. The jpeg type of compression (lossy compression) is made for the human eye/brain to retain the image but not the bits of the image in the file .
You need to use an image format that is capable to retain the pixels - as you wrote it already as well. Such a format more likely is BMP with RLE compression or the TIFF image format with ZIP or RLE compression. That's called lossless data compression.
Use the imagepng() instead of the imagejpeg() function and set the compression to 0 :
bool imagepng ( resource $image [, string $filename [, int $quality [, int $filters ]]] )
See : http://php.net/manual/en/function.imagepng.php

Categories