How to remove exif from a JPG without losing image quality?

Fer picture Fer · Nov 30, 2012 · Viewed 14.5k times · Source

I have a PHP photo sharing application in which user-uploaded images are resized into various thumb formats using ImageMagick.

As a seemingly "smart" way to save on file size, I am stripping exif info from these thumbs as follow:

$imagick = new Imagick($image);
$imagick->stripImage();
$imagick->writeImage($image);

This works. It does remove the EXIF info, where a thumbs of 30KB saves 12KB and becomes 18KB. A significant saving when showing many of such thumbs on a single page.

The problem however is that it works a little too well. The resulting images seem to lose a lot of color information and look "flat" compared to their non-stripped versions.

Based on my research so far, my theory is that one or both of the following is true:

  • Imagick throws away essential color profile information as part of the stripping process
  • Imagick recompresses the image upon saving it, losing quality

Regardless of the cause of the problem, I'm looking for a way to remove EXIF information in such a way that it does not affect the image quality or color itself.

Is this even possible?

Update:

Based on Gerald Schneider's answer, I tried enforcing the quality setting to 100% prior to "stripping" the image:

$imagick = new Imagick($image);
$imagick->setCompression(imagick::COMPRESSION_JPEG);
$imagick->setCompressionQuality(100);
$imagick->stripImage();
$imagick->writeImage($image);

Unfortunately, the problem remains. Below is example output where despite setting the quality to 100%, images are still flattened.

enter image description here

Answer

Robbert picture Robbert · Jul 8, 2013

Consider keeping the ICC profile (which causes richer colors) while removing all other EXIF data:

  1. Extract the ICC profile
  2. Strip EXIF data and image profile
  3. Add the ICC profile back

In PHP + imagick:

$profiles = $img->getImageProfiles("icc", true);

$img->stripImage();

if(!empty($profiles))
    $img->profileImage("icc", $profiles['icc']);

(Important note: using the ImageMagick 3.1.0 beta, the result I got from getImageProfiles() was slightly different from the documentation. I'd advise playing around with the parameters until you get an associative array with the actual profile(s).)

For command line ImageMagick:

convert image.jpg profile.icm
convert image.jpg -strip -profile profile.icm output.jpg

Images will get recompressed of course if you use ImageMagick, but at least colors stay intact.

Hope this helps.