Detect JPEG image quality - php

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);
}
}
}

Related

PHP imagick - Convert eps to jpg but poor quality

I'm trying to convert and resize eps files into jpg. I use php imagick for this.
After converting the quality is very bad.
my eps you can download here:
https://www.file-upload.net/download-14285439/icon.eps.html
my jpg-img
i use this code:
if ( extension_loaded('imagick') ) {
$imagePath = 'icon.eps';
$imagick = new Imagick();
$imagick->setResolution(300, 300);
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
$imagick->readImage($imagePath);
$imagick->resizeImage(0, 1000, Imagick::FILTER_LANCZOS, 1);
$imagick->setImageResolution(72, 72);
$imagick->setImageCompressionQuality(70);
$imagick->setImageCompression(\Imagick::COMPRESSION_JPEG);
$imagick->setCompressionQuality(70);
$imagick->setImageFormat('jpeg');
$imagick->writeImage('test.jpg');
} else {
echo 'not found';
}
same result with this settings without resize/only convert, but the quality is still bad:
if ( extension_loaded('imagick') ) {
$imagePath = 'icon.eps';
$imagick = new Imagick();
$imagick->setResolution(300, 300);
$imagick->setColorspace(Imagick::COLORSPACE_SRGB);
$imagick->readImage($imagePath);
$imagick->setImageFormat('jpeg');
$imagick->writeImage('test.jpg');
} else {
echo 'not found';
}
i use this version with php 7.2.33:
phpinfo
What is wrong?
As you obviously know ImageMagick uses Ghostscript to render EPS files to JPEG. I would suggest that, rather than use ImageMagick you use Ghostscript directly. This will give you more control over the process than using ImageMagick and will mean that you can post the Ghostscript command line instead of an IM one.
I'm afraid that I have no idea what ImageMagick sends to Ghostscript which makes it rather difficult to offer any suggestions.
In addition you really need to be much more explicit about your problem. What do you actually mean by 'the quality is very bad'. Is this purely subjective or is there some objective criteria you are using ?
The image you've posted doesn't look much like what I see, but since I don't know what command is being used to drive Ghostscript, it may simply be that I am not reproducing your setup exactly.
First note; the nature of your EPS is not really suitable for JPEG compression. JPEG performs best when applied to smoothly varying images, like photographs (JPEG = Joint Photographic Expert Group), it does not work well with large areas of flat colour with sharp edges (which is exactly what you have here), the high frequency component of the sharp edges gives rise to 'ringing' or 'fringing' effects.
When using Ghostscript directly it is possible to alter the JPEG quality. Setting -dJPEGQ=100 will produce the highest quality, trading off the compression (ie the output file will be larger).
In addition your EPS gives its BoundingBox as 20x20 points. So that's 20/72 inch in each dimension. Even at 300 dpi that's going to result in an image which is 84x84 pixels. Pretty small. At 72 dpi you'll get an image which is 20x20 pixels,
It looks to me like you have rendered the EPS at 72 dpi with the default JPEGQ value, the 'poor quality' appears to be nothing more than the well known artefacts produced by JPEG compression. Using that setup with Ghostscript produces something not entirely unlike your posted image (though without the sharp edged corner artefacts). Setting JPEGQ to 100 produces something more sensible. The file produced by the default settings is 3,564 bytes, while the higher quality file is 4,485 bytes.
If it were me I would render to a TIFF file at a decent resolution, say 1200 dpi to give an image 333x333 pixels. Then load that into ImageMagick and resize it to your desired dimensions. Finally export as a JPEG if you require it that way for some reason.
here's a comparison of the output from Ghostscript. On the left is a JPEG produced at 1200 dpi, in the middle is the default quality rendering at 72 dpi and on the right the 72 dpi rendering with JPEGQ set to 100.

PHP GD imagejpeg: output file size is bigger size than original

The following script is supposed to create an image with lower quality and so smaller file size (kb), instead it create an image with lower quality but bigger file size.
On my test the original is about 300kb, using 90% quality the output is almost the double and using 100% quality, the output is more than 1mb ...
<?php
$quality = 90;
$path = '/var/www/TEST/';
$inputSrc = $path . 'original.jpg';
$outputSrc = $path . 'after' . $quality . '.jpg';
$handler = imagecreatefromjpeg($inputSrc);
imagejpeg($handler, $outputSrc, $quality);
I assume the issue is related to imagejpeg bad implementation ...
is there any way to workaround this ?
isImageMagicka better solution ?
Thanks
Update
I was curious so I gave a try to ImageMagick and unfortunately I have similar result (slightly better).
Full test results:
Original size: 294.6Kb
GD (imagejpeg) 90%: 581.7Kb
GD (imagejpeg) 100%: 1.1Mb
ImageMagick 90%: 431.7Kb
ImageMagick 100%: 780.9kb
Update 2
I did some more test with GIMP and looks that in order to obtain a file with very similar size to the original one you have to check the option use quality setting from original image.
Now I'm confused more ... since when I select that setting Gimp automatically change the output quality to 74% (for the example image).
I was assuming that the JPEG quality value, if lower that 100%, decrease the image quality at every iteration of a save ... but I start to think I'm wrong here.
Update 3
With ImageMagick is not necessary to specify the quality of the sample and if you leave it emptyImageMagick will use the same quality detected in the input image.
So for the example image it is detected as a quality of 69 and the outfile is 326kb. That is the best result so far.
Here the image I'm using:
I had a little look at this. You can work backwards in ImageMagick and, rather than define the quality and see what size results, you can define the size and see what quality results. So, for a concrete example, you can say you want the output file not to exceed 100kB, like this:
convert MShRR.jpg -define jpeg:extent=100k out.jpg
and you get 99kB like this:
-rw-r--r--# 1 mark staff 294608 14 Jan 09:36 MShRR.jpg
-rw-r--r--# 1 mark staff 99989 14 Jan 09:44 out.jpg
To my eyes, the resulting image is a little posterised:
You can often add a tiny amount of blur to disguise this, as follows:
convert MShRR.jpg -blur x0.5 -define jpeg:extent=100k out.jpg
YMMV - Your Mileage May Vary !!!

Why does image compression sometimes reduce the size, and sometimes increase the size?

I'm using Imagick to compress images (JPEG mostly), but the results are quite random. For example, check out these results (aproximate numbers, and all JPEG):
1600kb -> 600kb (saved 1000kb)
1000kb -> 1200kb (wasted 200kb)
400kb -> 500kb (wasted 100kb)
Here's the code I used:
$image = new Imagick($path);
$image->stripImage(); // remove metadata, though Imagick adds its own, not sure why
$image->setImageCompressionQuality(0); // lossless compression
$image->writeImages($path, true); // writeImages instead of writeImage, in case it's a GIF
Image recompression will vary depending on the source image files. If the original image was carefully made with good software, ImageMagick might not be able to match it. Simple solution: compare the file sizes of the original and new versions, keep the smaller.
Also, note that the Compression Quality number means different things depending on the file type of the image. In particular, 0 means "worst quality, smallest size" for JPEG, but means "fast compression, not necessarily small" for PNG. See http://www.imagemagick.org/script/command-line-options.php#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);

Categories