86

I am using the following code to rotate an uploaded jpeg image if the orientation is off. I am only having problems with images uploaded from iPhones and Android.

if(move_uploaded_file($_FILES['photo']['tmp_name'], $upload_path . $newfilename)){
            chmod($upload_path . $newfilename, 0755);
            $exif = exif_read_data($upload_path . $newfilename);
            $ort = $exif['IFD0']['Orientation'];
            switch($ort)
            {

                case 3: // 180 rotate left
                    $image->imagerotate($upload_path . $newfilename, 180, -1);
                    break;


                case 6: // 90 rotate right
                    $image->imagerotate($upload_path . $newfilename, -90, -1);
                    break;

                case 8:    // 90 rotate left
                    $image->imagerotate($upload_path . $newfilename, 90, -1);
                    break;
            }
            imagejpeg($image, $upload_path . $newfilename, 100);
            $success_message = 'Photo Successfully Uploaded';
        }else{
            $error_count++;
            $error_message = 'Error: Upload Unsuccessful<br />Please Try Again';
        }

Am I doing something wrong with the way I am reading the EXIF data from the jpeg? It is not rotating the images as it is supposed to.

This is what happens when I run a var_dump($exif);

array(41) {
    ["FileName"]=> string(36) "126e7c0efcac2b76b3320e6187d03cfd.JPG"
    ["FileDateTime"]=> int(1316545667)
    ["FileSize"]=> int(1312472)
    ["FileType"]=> int(2)
    ["MimeType"]=> string(10) "image/jpeg"
    ["SectionsFound"]=> string(30) "ANY_TAG, IFD0, THUMBNAIL, EXIF"
    ["COMPUTED"]=> array(8) {
        ["html"]=> string(26) "width="2048" height="1536""
        ["Height"]=> int(1536)
        ["Width"]=> int(2048)
        ["IsColor"]=> int(1)
        ["ByteOrderMotorola"]=> int(1)
        ["ApertureFNumber"]=> string(5) "f/2.8"
        ["Thumbnail.FileType"]=> int(2)
        ["Thumbnail.MimeType"]=> string(10) "image/jpeg" }
        ["Make"]=> string(5) "Apple"
        ["Model"]=> string(10) "iPhone 3GS"
        ["Orientation"]=> int(6)
        ["XResolution"]=> string(4) "72/1"
            ["YResolution"]=> string(4) "72/1" ["ResolutionUnit"]=> int(2) ["Software"]=> string(5) "4.3.5" ["DateTime"]=> string(19) "2011:09:16 21:18:46" ["YCbCrPositioning"]=> int(1) ["Exif_IFD_Pointer"]=> int(194) ["THUMBNAIL"]=> array(6) { ["Compression"]=> int(6) ["XResolution"]=> string(4) "72/1" ["YResolution"]=> string(4) "72/1" ["ResolutionUnit"]=> int(2) ["JPEGInterchangeFormat"]=> int(658) ["JPEGInterchangeFormatLength"]=> int(8231) } ["ExposureTime"]=> string(4) "1/15" ["FNumber"]=> string(4) "14/5" ["ExposureProgram"]=> int(2) ["ISOSpeedRatings"]=> int(200) ["ExifVersion"]=> string(4) "0221" ["DateTimeOriginal"]=> string(19) "2011:09:16 21:18:46" ["DateTimeDigitized"]=> string(19) "2011:09:16 21:18:46" ["ComponentsConfiguration"]=> string(4) "" ["ShutterSpeedValue"]=> string(8) "3711/949" ["ApertureValue"]=> string(9) "4281/1441" ["MeteringMode"]=> int(1) ["Flash"]=> int(32) ["FocalLength"]=> string(5) "77/20" ["SubjectLocation"]=> array(4) { [0]=> int(1023) [1]=> int(767) [2]=> int(614) [3]=> int(614) } ["FlashPixVersion"]=> string(4) "0100" ["ColorSpace"]=> int(1) ["ExifImageWidth"]=> int(2048) ["ExifImageLength"]=> int(1536) ["SensingMethod"]=> int(2) ["ExposureMode"]=> int(0) ["WhiteBalance"]=> int(0) ["SceneCaptureType"]=> int(0) ["Sharpness"]=> int(1) }
Marc B
  • 356,200
  • 43
  • 426
  • 500
Jeff Thomas
  • 4,728
  • 11
  • 38
  • 57
  • Note that this code will recompress the source image, even if no rotation was needed. – Marc B Sep 20 '11 at 18:13
  • My problem right now is that the images that need to be rotated are not being rotated. – Jeff Thomas Sep 20 '11 at 18:24
  • Do a `var_dump($exif)` to see what the android phones are producing in the way of rotation data. – Marc B Sep 20 '11 at 18:28
  • I have updated the post to include the var_dump of $exif – Jeff Thomas Sep 20 '11 at 19:09
  • 1
    Ok, i cleaned up the dump there. Obviously. the orientation field is not in an 'IFD0' section, it's `$exif['COMPUTED']['Orientation']` and has value 6. – Marc B Sep 20 '11 at 19:14
  • Thank you. For some reason even with `$ort = $exif['COMPUTED']['Orientation'];` it is still not rotating the image as case 6 is satisfied. Could the problem lie in `imagejpeg` as I have it set to overwrite the original file? – Jeff Thomas Sep 20 '11 at 19:31
  • Well, definitely check what imagejpeg() returns - it'll return a boolean false on failure. – Marc B Sep 20 '11 at 19:42
  • OK that kind of helped narrow the problem down... `imagejpeg(): supplied argument is not a valid Image resource [file] => /www/students/touch/upload_picture.php [line]`. So for some reason it is not getting $image from the switch. – Jeff Thomas Sep 20 '11 at 20:56
  • imagejpeg is for GD, but earlier on you're using $image in OOP context, which implies it's not a GD image handle. – Marc B Sep 20 '11 at 21:04
  • Do you have a recommended fix for this? Thank you for all of your help. I am still learning the ins and outs of PHP – Jeff Thomas Sep 20 '11 at 21:07
  • Depends on what $image is and how you're setting it up. If you go pure GD, it'd be `$image = imagecreatefromjpeg('/path/to/image'); $rotated = imagerotate($image, 90, 0); imagejpeg($rotated, '/path/to/rotated/file');` – Marc B Sep 20 '11 at 21:09
  • Sorry for the delayed response. Thank you for all of your help with this issue. I have gotten it working with your help and suggestions. – Jeff Thomas Sep 22 '11 at 20:10
  • 1
    $exif['Orientation']; is working fine for me. It might be a better choice comparing to $exif['some_section']['Orientation']; – demosten Nov 25 '12 at 20:52

13 Answers13

85

Based on Daniel's code I wrote a function that simply rotates an image if necessary, without resampling.

GD

function image_fix_orientation(&$image, $filename) {
    $exif = exif_read_data($filename);
    
    if (!empty($exif['Orientation'])) {
        switch ($exif['Orientation']) {
            case 3:
                $image = imagerotate($image, 180, 0);
                break;
            
            case 6:
                $image = imagerotate($image, 90, 0);
                break;
            
            case 8:
                $image = imagerotate($image, -90, 0);
                break;
        }
    }
}

One line version (GD)

function image_fix_orientation(&$image, $filename) {
    $image = imagerotate($image, array_values([0, 0, 0, 180, 0, 0, -90, 0, 90])[@exif_read_data($filename)['Orientation'] ?: 0], 0);
}

ImageMagick

function image_fix_orientation($image) {
    if (method_exists($image, 'getImageProperty')) {
        $orientation = $image->getImageProperty('exif:Orientation');
    } else {
        $filename = $image->getImageFilename();
        
        if (empty($filename)) {
            $filename = 'data://image/jpeg;base64,' . base64_encode($image->getImageBlob());
        }
        
        $exif = exif_read_data($filename);
        $orientation = isset($exif['Orientation']) ? $exif['Orientation'] : null;
    }
    
    if (!empty($orientation)) {
        switch ($orientation) {
            case 3:
                $image->rotateImage('#000000', 180);
                break;
            
            case 6:
                $image->rotateImage('#000000', 90);
                break;
            
            case 8:
                $image->rotateImage('#000000', -90);
                break;
        }
    }
}
Firze
  • 3,939
  • 6
  • 48
  • 61
Jonathan
  • 6,572
  • 1
  • 30
  • 46
  • For Imagick I use getImageOrientation() to retrieve the orientation and then after rotating the image I set the correct Exif Orientation value via $image->setImageOrientation(\Imagick::ORIENTATION_TOPLEFT); – Tilman Jul 28 '14 at 23:27
  • Do you have a solution for WideImage? – Yami Medina Feb 11 '16 at 19:21
  • In some cases the imagick function `getImageOrientation()` did not work for me correctly even with converted raw images. The code above worked perfectly. – rokdd May 05 '17 at 13:01
  • In first version (GD) what should I pass for &$image where I am calling this function? – Bharat Maheshwari Jul 05 '17 at 11:46
  • 3
    for whom does not understand how to pass &$image parameter from the local file, use like that : $im = @imagecreatefromjpeg($local_filename); image_fix_orientation($im, $local_filename); if ($im) { imagejpeg($im, $local_filename); imagedestroy($im); } – woheras Oct 26 '17 at 11:45
  • This is much better in case the file isn't a jpeg – electrikmilk Nov 29 '19 at 17:18
  • 1
    For me, the rotation for `case 6` and `case 8` should be swapped. That is `case 6 = -90` and `case 8 = 90`. – Tigger Jan 23 '22 at 11:37
64

The documentation for imagerotate refers to a different type for the first parameter than you use:

An image resource, returned by one of the image creation functions, such as imagecreatetruecolor().

Here is a small example for using this function:

function resample($jpgFile, $thumbFile, $width, $orientation) {
    // Get new dimensions
    list($width_orig, $height_orig) = getimagesize($jpgFile);
    $height = (int) (($width / $width_orig) * $height_orig);
    // Resample
    $image_p = imagecreatetruecolor($width, $height);
    $image   = imagecreatefromjpeg($jpgFile);
    imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
    // Fix Orientation
    switch($orientation) {
        case 3:
            $image_p = imagerotate($image_p, 180, 0);
            break;
        case 6:
            $image_p = imagerotate($image_p, 90, 0);
            break;
        case 8:
            $image_p = imagerotate($image_p, -90, 0);
            break;
    }
    // Output
    imagejpeg($image_p, $thumbFile, 90);
}
Firze
  • 3,939
  • 6
  • 48
  • 61
Daniel Bleisteiner
  • 3,190
  • 1
  • 33
  • 47
  • From some reason, images created by android 4.1.2 does not need to be rotated, only load the image by "imagecreatefromjpen()" and then just save it back with "imagejpeg()". Do you know why? – doron Nov 16 '14 at 08:58
48

Simpler function for those uploading an image, it just autorotates if necessary.

function image_fix_orientation($filename) {
    $exif = exif_read_data($filename);
    if (!empty($exif['Orientation'])) {
        $image = imagecreatefromjpeg($filename);
        switch ($exif['Orientation']) {
            case 3:
                $image = imagerotate($image, 180, 0);
                break;

            case 6:
                $image = imagerotate($image, 90, 0);
                break;

            case 8:
                $image = imagerotate($image, -90, 0);
                break;
        }

        imagejpeg($image, $filename, 90);
    }
}
Firze
  • 3,939
  • 6
  • 48
  • 61
user462990
  • 5,472
  • 3
  • 33
  • 35
  • 1
    Made this answer into a simple composer package, which can be found on github (class with only one method): https://github.com/diversen/image-auto-rotate – dennis May 25 '16 at 12:06
  • 1
    You use the wrong degree values. In case 6 you need 90 and in case 8 you need -90 degree. – bernhardh Dec 12 '16 at 17:58
  • very useful function, if anyone see this warning **Illegal IFD size** you can use the [@ operator](http://php.net/manual/en/language.operators.errorcontrol.php) **e.g** : `$exif = @exif_read_data($filename);` – chebaby Dec 11 '17 at 11:46
  • @user462990 This function works well but for only images served locally. How will one go about passing an image url? I have an image on s3 that I needed to manipulate the orientation. – ultrasamad Jul 05 '18 at 12:23
27

Why is nobody considering mirrored cases 2,4,5,7? There are 4 more cases in exif orientation land:

enter image description here

Here is a complete solution taking a filename:

function __image_orientate($source, $quality = 90, $destination = null)
{
    if ($destination === null) {
        $destination = $source;
    }
    $info = getimagesize($source);
    if ($info['mime'] === 'image/jpeg') {
        $exif = exif_read_data($source);
        if (!empty($exif['Orientation']) && in_array($exif['Orientation'], [2, 3, 4, 5, 6, 7, 8])) {
            $image = imagecreatefromjpeg($source);
            if (in_array($exif['Orientation'], [3, 4])) {
                $image = imagerotate($image, 180, 0);
            }
            if (in_array($exif['Orientation'], [5, 6])) {
                $image = imagerotate($image, -90, 0);
            }
            if (in_array($exif['Orientation'], [7, 8])) {
                $image = imagerotate($image, 90, 0);
            }
            if (in_array($exif['Orientation'], [2, 5, 7, 4])) {
                imageflip($image, IMG_FLIP_HORIZONTAL);
            }
            imagejpeg($image, $destination, $quality);
        }
    }
    return true;
}
David Vielhuber
  • 3,253
  • 3
  • 29
  • 34
9

Just in case someone comes across this. From what I can make out some of the switch statements above are wrong.

Based on information here, it should be:

switch ($exif['Orientation']) {
    case 3:
        $image = imagerotate($image, -180, 0);
        break;
    case 6:
        $image = imagerotate($image, 90, 0);
        break;
    case 8:
        $image = imagerotate($image, -90, 0);
        break;
} 
Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
mr_crazy_pants
  • 121
  • 1
  • 3
6

It's probably worthwhile to mention that if you are using ImageMagick from command line, you can use the -auto-orient option which will auto rotate the image based on the existing EXIF orientation data.

convert -auto-orient /tmp/uploadedImage.jpg /save/to/path/image.jpg

Please note: If the EXIF data was stripped before the process, it will not work as described.

Cat
  • 396
  • 1
  • 5
  • 13
3

I hate to chime in with yet another set of orientation values, but in my experience using any of the values listed above, I always ended up with upside down images when uploading portrait orientation shots directly from an iPhone. Here's the switch statement I ended up with.

switch ($exif['Orientation']) {
        case 3:
            $image = imagerotate($image, -180, 0);
            break;

        case 6:
            $image = imagerotate($image, -90, 0);
            break;

        case 8:
            $image = imagerotate($image, 90, 0);
            break;
    }
Brad Root
  • 483
  • 5
  • 14
2

Here I'am explaining the whole thing, I use Laravel and use the Image Intervention Package.

First of all, I get my image and send it to my another function for resizing and some other functionality, if we do not need this, you can skip...

Grab the file with a method in my controller,

 public  function getImageFile(Request $request){
    $image = $request->image;
    $this->imageUpload($image);
}

Now, I send it to resize and getting the image name and extension...

public function  imageUpload($file){
    ini_set('memory_limit', '-1');
    $directory = 'uploads/';
    $name = str_replace([" ", "."], "_", $file->getClientOriginalName()) . "_";
    $file_name = $name . time() . rand(1111, 9999) . '.' . $file->getClientOriginalExtension();
    //path set
    $img_url = $directory.$file_name;
    list($width, $height) = getimagesize($file);
    $h = ($height/$width)*600;
    Image::make($file)->resize(600, $h)->save(public_path($img_url));
    $this->image_fix_orientation($file,$img_url);
    return $img_url;
}

Now I call my image orientation function,

 public function image_fix_orientation($file,$img_url ) {
    $data = Image::make($file)->exif();
    if (!empty($data['Orientation'])) {
        $image = imagecreatefromjpeg($file);
        switch ($data['Orientation']) {
            case 3:
                $image = imagerotate($image, 180, 0);
                break;

            case 6:
                $image = imagerotate($image, -90, 0);
                break;

            case 8:
                $image = imagerotate($image, 90, 0);
                break;
        }

        imagejpeg($image, $img_url, 90);
    }

}

And That's all...

MD. ABU TALHA
  • 683
  • 8
  • 13
2

Here is my PHP 7 function inspired by @user462990:

/**
 * @param string $filePath
 *
 * @return resource|null
 */
function rotateImageByExifOrientation(string $filePath)
{
    $result = null;

    $exif = exif_read_data($filePath);
    if (!empty($exif['Orientation'])) {
        $image = imagecreatefromjpeg($filePath);
        if (is_resource($image)) {
            switch ($exif['Orientation']) {
                case 3:
                    $result = imagerotate($image, 180, 0);
                    break;

                case 6:
                    $result = imagerotate($image, -90, 0);
                    break;

                case 8:
                    $result = imagerotate($image, 90, 0);
                    break;
            }
        }
    }

    return $result;
}

usage:

    $rotatedFile = rotateImageByExifOrientation($absoluteFilePath);
    if (is_resource($rotatedFile)) {
        imagejpeg($rotatedFile, $absoluteFilePath, 100);
    }
Sebastian Viereck
  • 5,455
  • 53
  • 53
1

jhead -autorot jpegfile.jpg

Is also a useful way to approach this.

jhead is a standard program in Linux (use 'sudo apt-get install jhead' to install), this option looks at the orientation and rotates the image correctly and losslessly only if it requires. It then also updates the EXIF data correctly.

In this way you can process a jpeg (or multiple jpegs in a folder) in a simple one-pass way that fixes rotation issues permanently.

E.g: jhead -autorot *.jpg will fix a whole folder of jpeg images in just the manner the OP requires in the initial question.

While it's not technically PHP I did read this thread and then used my jhead suggestion instead, called from a PHP system() call to achieve the results I was after which were coincident with the OPs: to rotate images so any software (like 'fbi' in Raspbian) could display them correctly.

In light of this I thought others may benefit from knowing how easily jhead solves this problem and posted the information here only for informative purposes - because no one had mentioned it previously.

G.P.W.
  • 11
  • 2
1

I've also used orientate() form Intervention, and it works flawlessly.

    $image_resize = Image::make($request->file('photo'));
    $image_resize->resize(1600, null,function ($constraint)
    {
        $constraint->aspectRatio();
    });
    $filename = $this->checkFilename();

    $image_resize->orientate()->save($this->photo_path.$filename,80);
c0ld
  • 11
  • 1
0

I think the best answer should be updated to take into account all possible exif orientation values (from 1 to 8):

function resample($jpgFile, $thumbFile, $width, $orientation) {
    // Get new dimensions
    list($width_orig, $height_orig) = getimagesize($jpgFile);
    $height = (int) (($width / $width_orig) * $height_orig);
    // Resample
    $image_p = imagecreatetruecolor($width, $height);
    $image   = imagecreatefromjpeg($jpgFile);
    imagecopyresampled($image_p, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
    // Fix Orientation
    switch($orientation) {
        case 3:
        case 4:
            $image_p = imagerotate($image_p, 180, 0);
            break;
        case 5:
        case 6:
            $image_p = imagerotate($image_p, 90, 0);
            break;
        case 7:
        case 8:
            $image_p = imagerotate($image_p, -90, 0);
            break;
    }
    // Output
    imagejpeg($image_p, $thumbFile, 90);
}

More information here.

Note that the image should be flipped for the following exif values: 2, 4, 6, and 8.

jo66
  • 79
  • 2
-1

Intervention Image has a method orientate():

$img = Image::make('foo.jpg')->orientate();

This method reads the EXIF image profile setting 'Orientation' and performs a rotation on the image to display the image correctly.

Damien
  • 1,140
  • 15
  • 22