ImageMagick and PHP to properly rotate an image - php

I am trying to use Imagick in my php code to properly orient images, so that I can strip the metadata from them and have them properly show up. I tried the methodology in this post:
Detect EXIF Orientation and Rotate Image using ImageMagick
But it is not working. Using the autorotate function in that post, this is what I am doing:
$working_image = new \Imagick();
$working_image->readImageBlob( $source_data);
$working_image->setImageFormat('jpeg');
autorotate($working_image);
... some resizing code:
$working_image->resizeImage( $width, $height, \Imagick::FILTER_CATROM, .7);
// get rid of metadata
$working_image->stripImage();
$working_image->writeImage( <unique filename> );
$working_image->getImageBlob();
... write out to data file to google
We're using Google's cloud stuff to store our files, hence the "getImageBlob" call.
Problem is that this doesn't seem to work. The resultant images are still oriented the same way they were, but now have no metadata, so the tag won't "fix" them.
What am I doing wrong here? I am using this version of the Imagick PHP object:
[versionNumber] => 1673
[versionString] => ImageMagick 6.8.9-9 Q16 x86_64 2015-01-05 http://www.imagemagick.org
In response to your answer:
Thanks for the clarifications. Looking at one of the specific images that I am having problems with, identify -verbose shows multiple places defining orientation:
Orientation: RightTop
exif:Orientation: 6
exif:thumbnail:Orientation: 6
And this is reflected in my call to Imagick::getImageOrientation in PHP, which returns the "6" value, which then causes my code to call functions like "Imagick::rotateImage("#000", 180);". The problem is that the image is unchanged by these calls. 8-(
The core problem is this: We get these images by the thousands every day from MLS's all over the country. We currently process them (resizing currently) via an automatic process. When they are displayed on our site, everything is currently fine, because the <img> tag seems to be happy to interpret the EXIF data and show the image in proper orientation. BUT... we want to start optimizing images, which means stripping the EXIF info out. So I need to figure out why Imagick->rotateImage() isn't working.

Ok, let's write a whole answer:)
What I meant with the link to ImageMagick's -auto-orient, was to point out, that this kind of auto-orientation depends on EXIF profile and "Orientation" setting. If it's missing or is wrong the auto-orientation will not work as expected. To check whether your images has the EXIF profile with Orientation in tact, you can use several ways:
PHP Imagick (check it with image before you call ->stripImage())
echo 'Orientation is: ', $working_image->getImageOrientation();
PHP (if you have local file)
$exif = exif_read_data('input.jpg');
echo 'Orientation is: ', isset($exif['Orientation']) ? $exif['Orientation'] : 'missing';
Online EXIF reader.
The values you get are described for example here or more about image auto-rotation topic and sample images are here
Or you can use programs like Gimp or Photoshop.
So you're doing nothing wrong, without EXIF this won't work. It's camera that is writing the tags into images, so there is no guarantee all your photos have it.
Perhaps offer image rotation for visitors in your website instead? There is now CSS3 transform: rotate(deg) property that makes it really easy, see....

We can get the current image orientation and can easily update to the original one with Imagick
public function fix_image_orientation()
{
$working_image = new \Imagick();
$working_image->readImageBlob($source_data);
$working_image->setImageFormat('jpeg');
$orientation = $working_image->getImageOrientation();
if (!empty($orientation)) {
switch ($orientation) {
case imagick::ORIENTATION_BOTTOMRIGHT:
$working_image->rotateimage("#000", 180);
break;
case imagick::ORIENTATION_RIGHTTOP:
$working_image->rotateimage("#000", 90);
break;
case imagick::ORIENTATION_LEFTBOTTOM:
$working_image->rotateimage("#000", -90);
break;
}
}
$working_image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
$working_image->writeImage( <unique filename> );
}

#Satendra Rawat posted a good, but incomplete answer. There is the GitHub repository https://github.com/ianare/exif-samples, where you can see 8 orientation examples. Only 3, 5 and 8 worked correctly.
So here is a update for all the orientations:
public function fix_image_orientation()
{
$working_image = new \Imagick();
$working_image->readImageBlob($source_data);
$working_image->setImageFormat('jpeg');
$orientation = $working_image->getImageOrientation();
if (!empty($orientation)) {
switch ($orientation) {
default:
case imagick::ORIENTATION_TOPLEFT:
break;
case imagick::ORIENTATION_TOPRIGHT:
$working_image->flipImage();
$working_image->rotateImage("#000", 180);
break;
case imagick::ORIENTATION_BOTTOMRIGHT:
$working_image->rotateImage("#000", 180);
break;
case imagick::ORIENTATION_BOTTOMLEFT:
$working_image->flipImage();
break;
case imagick::ORIENTATION_LEFTTOP:
$working_image->rotateImage("#000", -90);
$working_image->flipImage();
break;
case imagick::ORIENTATION_RIGHTTOP:
$working_image->rotateImage("#000", 90);
break;
case imagick::ORIENTATION_RIGHTBOTTOM:
$working_image->rotateImage("#000", 90);
$working_image->flipImage();
break;
case imagick::ORIENTATION_LEFTBOTTOM:
$working_image->rotateImage("#000", -90);
break;
}
}
$working_image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
$working_image->writeImage( <unique filename> );
}

Related

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:
get the picture out of the zip as a string
save the pic in a tempfile for .png handling
init an imagick object with the picture string from 1.
check if the pic is a png
is png: read the exif orientation via helper class PNGMetadata
is not png: use imagick imageproperty to get the exif orientation
call autoRotateImage to turn the pic into the right orientation
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..
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.

PHP ImageMagick functions rotating phone images inexplicably

I was using this call to imageMagick in PHP to merge multiple images horizontally
$combined_image = $images2merge->appendImages(false);
And it works great when I upload the images from my laptop. But when I go to the same page with my android phone browser and use the camera to take the pics to upload, this function ends up merging them vertically.
So I tried to swap it out with this function
$combined_image = $images2merge->montageImage($draw, '6×6+0+0', '+1+1', Imagick::MONTAGEMODE_UNFRAME, 0);
And that does now merge them horizontally when I use my phone, but it rotates them 90 degrees counterclockwise. Again that's different from what happens when I upload from my laptop, in which case it works as expected. It's hard to fix something that behaves inconsistently.
Any suggestions on how to fix this would be greatly appreciated!
After taking photos from any smartphone, please check the orientation value and rotate the image accordingly before further image processing.
So you can use the following autoRotateImage function to handle the auto-rotation:
<?php
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);
}
?>
For example:
<?php
$image = new Imagick('my-image-file.jpg');
autoRotateImage($image);
// - Do other stuff to the image here -
$image->writeImage('result-image.jpg');
?>

PHP - Detect if image has been manually rotated, EXIF Orientation flag wrong

I’m experiencing some strange issues with image rotation.
Originally we were having the problem of some users uploading images onto our site but they were appearing the wrong way around. We therefore wanted to implement some code to check the EXIF Orientation flag and rotate the image if necessary.
Here is what the simplified function looks like:
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, 100);
}
}
This seems to work fine for the images which have the Orientation flag set, were rotated and causing the original issue.
However, bizarrely I have found a number of images previously uploaded onto our server (we retain the original image with EXIF data intact) from before we have implemented the above code which appear to contain an Orientation flag (of say ‘3’) but their raw picture is the correct way up. If I feed one of these images into my new upload code it reads the Orientation flag and rotates the image, but the image was the correct way around to begin with.
I believe the user has likely manually rotated the image prior to upload yet it’s still retaining the Orientation flag value. For example, if I rotate an image in Paint it seems to retain the same Orientation value. Here is an example image http://s16.postimg.org/cv5ejchqt/exig.jpg (arrow points to the true top of the image), yet it has the Orientation flag value of 3 so is rotated when using my new upload code.
Is there a way to detect if the user has manually rotated, somehow?
Thanks

php iphone/IOS6 upload rotation issue: what is best way to save rotated image

Using the safari mobile browser with IOS6, the file upload function gives users the option to snap a photo. Unfortunately, upon snapping the photo, while the photo thumb shows up properly in the browser, when you upload to a server, the file is rotated 90 degrees. This appears to be due to the exif data that the iphone sets. I have code that fixes the orientation by rotating the image when serving. However, I suspect it would be better to save the rotated, properly oriented, image so I no longer have to worry about orientation. Many of my other photos do not even have exif data and i don't want to mess with it if I can avoid it.
Can anyone suggest code to save the image so it is properly oriented?
Here is the code that rotates the image. The following code will display the properly oriented image, however, what I want to do is save it so I can then serve it whenever I want without worrying about orientation.
Also I would like to replace impagejpeg call in code below so that any code works for gifs as well as jpgs.
Thanks for suggestions/code!
PHP
//Here is sample image after uploaded to server and moved to a directory
$target = "pics/779_pic.jpg";
$source = imagecreatefromstring(file_get_contents($target));
$exif = exif_read_data($target);
if(!empty($exif['Orientation'])) {
switch($exif['Orientation']) {
case 8:
$image = imagerotate($source,90,0);
//echo 'It is 8';
break;
case 3:
$image = imagerotate($source,180,0);
//echo 'It is 3';
break;
case 6:
$image = imagerotate($source,-90,0);
//echo 'It is 6';
break;
}
}
// $image now contains a resource with the image oriented correctly
//This is where I want to save resource properly oriented instead of display.
header('Content-type: image/jpg');
imagejpeg($image);
?>
Only JPEG or TIFF files can carry EXIF metadata, so there's no need to worry about handling GIFs (or PNGs, for that matter) with your code.
From page 9 of what I believe is the official specification:
Compressed files are recorded as JPEG (ISO/IEC 10918-1) with application marker segments (APP1 and APP2) inserted. Uncompressed files are recorded in TIFF Rev. 6.0 format.
http://www.cipa.jp/english/hyoujunka/kikaku/pdf/DC-008-2010_E.pdf
To save your image just use the same function imagejpeg and the next parameter to save the image, something like:
imagejpeg($image, $target, 100);
In this case you don't need the specify the header, because you are not showing nothing.
Reference:
http://sg3.php.net/manual/en/function.imagejpeg.php

PHP image gallery- dealing with iPhone / iPad images

Ive created an image gallery with php and mysql, incorporating several ways to add images & ability to sort by addition method and/or category. After noticing some images from apple devices showed up with the 'wrong' orientation, I created another page to edit orientation and other file info, then save said changes back to file and db. Only after I thought I had solved this problem did I view the altered images on an apple device, only to realize that image was now in 'wrong' orientation on said device. I've been googling this, but can't quite figure out exactly what I need to learn now to deal with images from apple devices in this situation. A shove in the right direction would be greatly appreciated.
Thanks!
Seeing the same problem on my "gallery3" photo gallery. Like machouinard, I am using "jhead -norot filename.jpg" to strip the orientation header from the images. This fixes the Apple rotation problem, and it does not seem to mess up the other browsers.
To edit a bunch of the files in place, I go directly to the album storage area in (gallerytop)/lib/albums and do find -type f | xargs sudo jhead -norot. It is smart enough to only modify the files that need to be modified, and it'll print out a list of them to stdout as it is working.
To get gallery3 to update the thumbnails, I go into the database and set the "dirty" flags like this: echo "update items set thumb_dirty=1,resize_dirty=1 where relative_path_cache like 'ALBUMNAME/%';" | mysql -u root -p gallery3 . Then I go into the gallery maintenance mode and run the "rebuild images" utility.
Forgot I posted this. I figured out how to handle the orientation issue so I thought I'd share. Seems simple now. I've since recreated this project using Codeigniter. CI is great and saves a lot of time, but I'm glad I wrote it all myself the first time. I sure learned more that way.
First, if the file is a jpg, I get the EXIF data with PEL .
$new is the uploaded file to be checked for orientation.
$this->load->library('pel/PelJpeg');
if($ext == 'jpg'){
$pdw= new PelDataWindow(file_get_contents($new));
if(PelJpeg::isValid($pdw)){
$input_jpg = new PelJpeg($new);
$exif = $input_jpg->getExif();
}
}
Then if EXIF exists, get the orientation value and run it through a switch statement, rotate it accordingly and then reset the orientation value. I'm using image_moo and Codeigniter, but this can obviously be changed to use any image manipulation library.
I'm honestly not sure if all those IF statements need to be there, but I kept running into trouble with jpg's that only included some EXIF info and would blow up the script without them.
if($exif !== NULL){
if($tiff = $exif->getTiff()){
if($ifd0 = $tiff->getIfd()){
if($orient = $ifd0->getEntry(PelTag::ORIENTATION)){
$this->image_moo->load($new);
//find the orientation value from the orientation tag. May be a better way, but this works for me.
$orientation = str_replace(' ', '', $orient);
//The orientation value from the orientation tag is after 'Value:'
if (($tmp = strstr($orientation, 'Value:')) !== false) {
$str = substr($tmp, 6, 1);
}
switch ($str)
{
// up is pointing to the right
case 8:
$this->image_moo->rotate(90);
$orient->setValue(1);
break;
// image is upside-down
case 3:
$this->image_moo->rotate(180);
$orient->setValue(1);
break;
// up is pointing to the left
case 6:
$this->image_moo->rotate(270);
$orient->setValue(1);
break;
// correct orientation
case 1:
break;
}
$this->image_moo->save($new,TRUE);
if ($this->image_moo->errors) print $this->image_moo->display_errors();
$this->image_moo->clear();
}
}
}
}
Hope this will be helpful to someone else struggling with the same issues.
If you see anything that could be improved, please let me know. But this works great for me.
Mark

Categories