29

Currently working with PHP and iMagick to develop a poster printing Web application.

This is the example image I am using to test upload/image editing features of the application:

alt text

The image contains the following EXIF data:

[FileName] => 1290599108_IMG_6783.JPG
    [FileDateTime] => 1290599109
    [FileSize] => 4275563
    [FileType] => 2
    [MimeType] => image/jpeg
    [SectionsFound] => ANY_TAG, IFD0, THUMBNAIL, EXIF, INTEROP, MAKERNOTE
    [COMPUTED] => Array
        (
            [html] => width="3504" height="2336"
            [Height] => 2336
            [Width] => 3504
            [IsColor] => 1
            [ByteOrderMotorola] => 0
            [CCDWidth] => 22mm
            [ApertureFNumber] => f/5.6
            [UserComment] => 
            [UserCommentEncoding] => UNDEFINED
            [Thumbnail.FileType] => 2
            [Thumbnail.MimeType] => image/jpeg
        )

    [Make] => Canon
    [Model] => Canon EOS 30D
    [Orientation] => 6
    [XResolution] => 72/1
    [YResolution] => 72/1
    [ResolutionUnit] => 2
    [DateTime] => 2009:08:31 08:23:49
    [YCbCrPositioning] => 2
    [Exif_IFD_Pointer] => 196

However - iMagick, when __construct'ed with this image, automatically rotates it an additional 90 degrees CCW as per [Orientation] => 6 (I think!). Resulting in this...

alt text

What I'd like to know is...

How can I maintain the original orientation of the image seen at the top of the page? And is this possible through disabling the auto-rotation performed by iMagick?

Many thanks

UPDATE: Here's the solution I've come up with... It will fix the orientation based on the orientation in the EXIF data

   public function fixOrientation() {

       $exif = exif_read_data($this->imgSrc);
       $orientation = $exif['Orientation'];
       switch($orientation) {

           case 6: // rotate 90 degrees CW
               $this->image->rotateimage("#FFF", 90);
           break;

           case 8: // rotate 90 degrees CCW
              $this->image->rotateimage("#FFF", -90);
           break;

       }

 }
kaese
  • 10,249
  • 8
  • 29
  • 35
  • You should consider the sample here: http://php.net/manual/en/imagick.getimageorientation.php as you're missing a 180 degree rotate if the image is upside down. – Fmstrat May 11 '18 at 13:58

5 Answers5

61

"However - iMagick, when __construct'ed with this image, automatically rotates it an additional 90 degrees CCW as per [Orientation] => 6 (I think!)."

The problem is actually the opposite of that. Imagick doesn't auto rotate the image. You're only seeing it correctly in other software / your web browser because those programs do auto rotate it based on the EXIF info. Certain operations in Imagick will cause you to lose that correct EXIF info (copying the image, thumbnailImage(), stripImage(), and other manipulations). So what you need to do in that case is actually physically rotate the image.

The answer from ajmicek is good, but it could be improved a bit by using Imagick's own built in functions rather than the PHP EXIF functions. Also that snippet seems to have been a part of a class, so it can't be used as a separate function as-is. It's also a good idea to set the correct EXIF orientation with setImageOrientation() after you rotate it.

// Note: $image is an Imagick object, not a filename! See example use below.
function autoRotateImage($image) {
    $orientation = $image->getImageOrientation();

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

        case imagick::ORIENTATION_RIGHTTOP:
            $image->rotateimage("#000", 90); // rotate 90 degrees CW
            break;

        case imagick::ORIENTATION_LEFTBOTTOM: 
            $image->rotateimage("#000", -90); // rotate 90 degrees CCW
            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);
}

Example use:

$image = new Imagick('my-image-file.jpg');
autoRotateImage($image);
// - Do other stuff to the image here -
$image->writeImage('result-image.jpg');
orrd
  • 9,469
  • 5
  • 39
  • 31
4

Try Imagick::setImageOrientation. Experiment with the available constants.

Sam Texas
  • 1,245
  • 14
  • 30
izym
  • 143
  • 5
  • thanks izym. Have you any more info on how these constants may be used? – kaese Nov 24 '10 at 14:24
  • 2
    Be aware that Imagick::setImageOrientation() doesn't actually rotate the image, it just changes the EXIF rotation info that will be saved with the image. In some cases that may be what you want to do, but usually it's better to actually physically rotate the image (see the code sample I posted as an answer). The problem with relying on the EXIF rotation info is that not all web browsers and image viewing software will automatically orient it correctly. – orrd Feb 20 '13 at 21:07
2

Good start -- a few additions to make the function more robust. First, case 3 occurs when the image appears upside down. There's a GREAT illustration of the different orientation codes by Calvin Hass. It's possible that orientation information may appear at a different part of the exif_read_data array (depending on camera model, I think), so I've tried to take that into account in my example code.

Something like this:

public function fixOrientation() {

    $exif = exif_read_data($this->imgSrc);

    if( isset($exif['Orientation']) )
        $orientation = $exif['Orientation'];
    elseif( isset($exif['IFD0']['Orientation']) )
        $orientation = $exif['IFD0']['Orientation'];
    else
        return false;

    switch($orientation) {
        case 3: // rotate 180 degrees
            $this->image->rotateimage("#FFF", 180);
        break;

        case 6: // rotate 90 degrees CW
            $this->image->rotateimage("#FFF", 90);
        break;

        case 8: // rotate 90 degrees CCW
            $this->image->rotateimage("#FFF", -90);
        break;
    }
}

The transformation & save leaves you without the previous EXIF information, including Orientation. The lack of Orientation in the transformed image will prevent further processing from attempting to 'correct' things by rotating again. I do wish Imagick had support for ImageMagick's -auto-orient, but oh well.

Oh, also: the rotation is a lossy operation (unless you use jpegtran), so you should try to only do it in conjunction with a resize or other transformation.

ajmicek
  • 756
  • 6
  • 8
  • That first argument is just the canvas color. – ajmicek Feb 21 '17 at 04:03
  • 1
    A mere 9 years later... "the rotation is a lossy operation" - actually rotating by multiples of 90 degrees is special cased, and should be lossless. https://github.com/ImageMagick/ImageMagick/blob/341249be9340fc3382c86045779b756dae43fe87/MagickCore/distort.c#L2983 – Danack Sep 06 '21 at 14:49
0

This code in orrd's excellent answer requires iMagick version 6.3+.:

$image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);

Works perfectly and takes care of os/device orientation differences. Would not work with 6.2.

I had coded to get device. Here in case someone needs it.

$ua = $_SERVER['HTTP_USER_AGENT'];
$strcut = stristr($ua, '(')."<br>";
$textlen = strpos($strcut,";");
$deviceos = substr($strcut,1,($textlen-1));
echo "Device O/S: * $deviceos"."<br>";
CliffPR
  • 11
  • 2
0
function thumbnailImage($imagePath,$color,$quality)
{   

    $fileType = pathinfo($imagePath, PATHINFO_EXTENSION);

    if (!empty($fileType))
    {
        switch($fileType)
        {
            case "gif":
                $im = imagecreatefromgif($imagePath);
                break;

            case "jpg":
                $im = imagecreatefromjpeg($imagePath);
                break;

            case "jpeg":
                $im = imagecreatefromjpeg($imagePath);
                break;

            case "png":
                $im = imagecreatefrompng($imagePath);
                break;
        }
    }

    $imagick = new Imagick();
    $imagick->readImage($imagePath);
    $compression_type = Imagick::COMPRESSION_JPEG; 
    $imagick->setImageCompression($compression_type);
    $imagick->setImageCompressionQuality($quality);

    if ($fileType == "jpeg" || $fileType == "jpeg" )
    {
        $exif = exif_read_data($imagePath);
        if (!empty($exif['Orientation'])) {
            switch ($exif['Orientation']) {
                case 3:
                    $imagick = imagerotate($imagick, 180, 0);
                    break;

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

                case 8:
                    $imagick = imagerotate($imagick, 90, 0);
                    break;
            }
        }
    }
    $imagick->setImageBackgroundColor($color);
    $imagick->thumbnailImage(150, 150, true, true);
    header("Content-type: image/jpg");
    echo $imagick->getImageBlob();
}

Super easy thumbnailer. Call with

thumbnailImage('file.ext','#ffffff',50); // full path, color, quality 1-100

Works with jpg, gif and png but obviously only does the EXIF stuff on jpeg

The thumbnail will load on the page and you can call it by URL if you specify your $_GETs

enjoy

E. Zeytinci
  • 2,642
  • 1
  • 20
  • 37