Search code examples
phpimageimagickimage-rotationautorotate

Does Imagick read iPhone Exif:Orientation wrong?


I try to autorotate some images from a zip with php. For the autorotate I'am using the Exif:Orientation informations of the image.

The code does work, but I have problems with some images which seems to have wrong exif informations. I have this problem only with images taken by an iPhone..

What my Code do:

  1. get the picture out of the zip as a string
  2. save the pic in a tempfile for .png handling
  3. init an imagick object with the picture string from 1.
  4. check if the pic is a png
  5. is png: read the exif orientation via helper class PNGMetadata
  6. is not png: use imagick imageproperty to get the exif orientation
  7. call autoRotateImage to turn the pic into the right orientation
  8. save the original orientation for later (I'am saving the org. orientations in a db for debugging)

The function "autoRotateImage" only handle the exif orientation and do the correct steps for each.

Parts of the php code:

try{
$img_content = $zip->getFromIndex($zip->locateName($img->getAttribute('xlink:href')));
$tmpPic = tempnam(sys_get_temp_dir(), $img->getAttribute('xlink:href'));
file_put_contents($tmpPic,$img_content);
$imagick_img = new \Imagick();
$imagick_img->readImageBlob($img_content);
if (PNGMetadata::isPNG($tmpPic)){
  $png_metadata = new PNGMetadata($tmpPic);
  $orientation_arr[$img->getAttribute('xlink:href')] = $png_metadata->get('exif:IFD0:Orientation');
  $org_orientation = $png_metadata->get('exif:IFD0:Orientation');
}else{
  $orientation_arr[$img->getAttribute('xlink:href')] = intval($imagick_img->getImageProperty('Exif:Orientation'));
  $org_orientation = intval($imagick_img->getImageProperty('Exif:Orientation'));
}
autoRotateImage($imagick_img,new \Imagick(),(isset($manualOrientation[$img->getAttribute('xlink:href')]) ? $manualOrientation[$img->getAttribute('xlink:href')] : $org_orientation));
$imagick_img->clear();
unlink($tmpPic);
}catch(Exception $e){
  if(isset($imagick_img)){$imagick_img->clear();}
  if(isset($tmpPic)){unlink($tmpPic);}
}

autoRotateImage function:

function autoRotateImage(&$image, $imagick, $orientation) {
        switch($orientation) {
            case $imagick::ORIENTATION_BOTTOMRIGHT: //3
                $image->rotateimage("#000", 180); // rotate 180 degrees
                break;

            case $imagick::ORIENTATION_BOTTOMLEFT: //4
                $image->flipImage(); // mirror vertical
                break;
    
            case $imagick::ORIENTATION_RIGHTTOP: //6
                $image->rotateimage("#000", 90); // rotate 90 degrees CW
                break;

             case $imagick::ORIENTATION_LEFTTOP: //5
                $image->transverseImage(); // horizontal mirror image by reflecting the pixels around the central y-axis while rotating them 270-degrees.
                break;
    
            case $imagick::ORIENTATION_LEFTBOTTOM: //8
                $image->rotateimage("#000", 270); // rotate 270 degrees CW
                break;

            case $imagick::ORIENTATION_RIGHTBOTTOM: //7
                $image->transverseImage();      //horizontal mirror image by reflecting the pixels around the central y-axis while rotating them 270-degrees.
                $image->rotateimage("#000", -180); // rotate 180 degrees CCW
                break;

            case $imagick::ORIENTATION_TOPLEFT: //1
                //Nothing to do
                break;

            case $imagick::ORIENTATION_TOPRIGHT: //2
                $image->transverseImage();      //horizontal mirror image by reflecting the pixels around the central y-axis while rotating them 270-degrees.
                $image->rotateimage("#000", 90); // rotate 90 degrees CW
                break;
        }

        // Now that it's auto-rotated, make sure the EXIF data is correct in case the EXIF gets saved with the image!
        $image->setImageOrientation($imagick::ORIENTATION_TOPLEFT);
    }

The class "PNGMetadata" is from someone else: https://github.com/joserick/PNGMetadata

So.. this code works fine for like 90% of handled images, except some iPhone images. Current case: Exif Orientation says 5 (mirror at y and turn 270 CW), but it should be 7 (mirror at y and turn 90 CW)

Maybe someone have an idea how to handle those cases?

I already tried to get the exif information in other forms, but the result is everytime the same. Also an commandline exif reader read the wrong exif orientation out of the faulty images.

I need a way to fix the wrong orientations..


Solution

  • It seems that the imagick function "transverseImage()" doesn't work as aspected.. I don't know exactly what it do, but I think the behaviour of it doesn't fit the description..

    The desc says "Creates a horizontal mirror image by reflecting the pixels around the central y-axis while rotating them 270-degrees.". For me that means, that the Image get mirrored and after that rotated by 270°CW. But Images will be false rotated after that func.

    I tried "Imagick::flopImage" in combination with rotateImage(270) and get different results.

    Short: I replaced transverseImage with flopImage + rotateImage and now it seems, that the Images getting rotated correctly.