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

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

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 getimagesize() mixes up width and height

I use a PHP script that uploads an image, then gets the dimensions with PHP's getImageSize() and then does things to the image according to the pictures orientation (portrait or landscape).
However (PHP version 5.4.12) on some .jpg files it gets the height and width as they are, and in some (taken with an iPhone) it swaps them, thinking the portrait pictures are actually landscape.
It does not only happen on my local Wampserver, but also on a remote server (with a different PHP version).
Has anyone a clue how
1) to repair this or
2) find a way around the problem?
Some cameras include an orientation tag within the metadata section of the file itself. This is so the device itself can show it in the correct orientation every time regardless of the picture's orientation in its raw data.
It seems like Windows doesn't support reading this orientation tag and instead just reads the pixel data and displays it as-is.
A solution would be to either change the orientation tag in afflicted pictures' metadata on a per-image basis, OR
Use PHP's exif_read_data() function to read the orientation and orient your image accordingly like so:
<?php
$image = imagecreatefromstring(file_get_contents($_FILES['image_upload']['tmp_name']));
$exif = exif_read_data($_FILES['image_upload']['tmp_name']);
if(!empty($exif['Orientation'])) {
switch($exif['Orientation']) {
case 8:
$image = imagerotate($image,90,0);
break;
case 3:
$image = imagerotate($image,180,0);
break;
case 6:
$image = imagerotate($image,-90,0);
break;
}
}
// $image now contains a resource with the image oriented correctly
?>
References:
https://stackoverflow.com/a/10601175/1124793 (research as to why this is happening)
http://php.net/manual/en/function.exif-read-data.php#110894 (PHP Code)
Function getimagesize() changes width and height in photos that are landscape orientation (horizontal) .
You can use this code:
<?php
$img = "test.jpg";
$exif = exif_read_data($img);
if(empty($exif['Orientation'])) {
list($width, $height, $type, $attr) = getimagesize($img);
}else{
list($height, $width, $type, $attr) = getimagesize($img);
}
?>
But it was fixed automatically in PHP7 and above.

No orientation in exif data - PHP image upload

been trying to detect the image orientation of uploaded images from iPhones and then adjust their orientation from that.
I am trying to fix the issue where images taken in potrait, are uploaded with a -90 degree rotate. I tried numerous switch statements which were not working, so decided to return the exif data in my JSON return.
The issue i see is that their is no orientation in the exif data.
I am doing so:
$imagefile = $fileToUpload["tmp_name"];
$destinationImage = imagecreatefromstring(file_get_contents($imagefile));
$exif = exif_read_data($imagefile);
$moveUploadedFile = imagejpeg($destinationImage, $this->uploadDir . "/" . $newFileName, 100);
imagedestroy($destinationImage);
if ($moveUploadedFile) {
$return['ort'] = $exif;
echo json_encode($return);
}
What i am seeing in my return (using firebug) is:
FileName:"phpUQZFHh"
FileDateTime:1410465904
FileSize:473421
FileType:2
MimeType:"image/jpeg"
SectionsFound:"COMMENT"
Computed: OBJECT:
Height:700
Width:933
IsColor:1
Comment: ARRAY:
0:"CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 100"
I want to be able use the exif data like so:
if (!empty($exif['Orientation'])){
//get the orientation
$ort = $exif['Orientation'];
//determine what oreientation the image was taken at
switch($ort){
case 2: // horizontal flip
break;
case 3: // 180 rotate left
$destinationImage = imagerotate($destinationImage, 180, -1);
break;
}
}
Any help?
EDIT: After downloaded an image that had been uploaded and checking its properties it appears that all exif data was removed in the upload process.
This still baffles me as to why it is rotated before / during upload / how to fix this.
I guess the "Orientation" value presents in the returned data of exif_read_data function in case when you upload the picture from your iOS device only. It won't work in desktop browser. I might be wrong.
I ran into the same problem. Turns out some images really did not have any exif data on Orientation at all -- usually ones with the "correct" orientation do not have it. I tried one landscape image taken with an iPhone and there was.
In your case, the photos may have had no exif data in the first place. I had some photos like that as well (rotated -90 degrees but no Orientation info). I could be wrong but without exif data, there's no programmatic way to know if an image is incorrectly oriented.
For incorrectly oriented photos without Orientation info, I suggest you just make sure the user sees (gets a preview) of what about to be uploaded. IME, most users are more than willing to get out of their way to fire up paint/photoshop/etc. just to ensure they have good looking photos.
You can get Orientation value before move the file to the server directory (Worked with iPhone as well)
$image = $_FILES["image"]["tmp_name"];
$orientation = '';
if (function_exists('exif_read_data'))
{
$exif = exif_read_data(image);
if($exif && isset($exif['Orientation']))
$orientation = $exif['Orientation'];
}

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

transform a jpg into png and make a color transparent in it

In PHP I'm trying to process an image, that is, I'm trying to make the surrounding color transparent in a jpg file. I'm using the GD library by the way.
I can directly output the image by converting it into a png using imagecreatefromjpeg and imagepng functions. But I can't find a way to make the specified color transparent. Also, some images have lighter gray artifacts around black graphics, created during saving. Is there any way I can include those as well?
I'm kind of lost. I found some answers to make a color transparent on an image but I don't know how to first convert the image without saving it into the server and then process it.
Any ideas?
EDIT: Here's my code so far. I managed to make a specified color transparent but I can't make it detect the surrounding one yet.
Most of the time images will be closed because they'l be logos or texts, saved in the allowed image formats. So I don't think I will have a major issue with gradients but it would be great if I could manage to manipulate transparency in the surrounding gradients, if any, such as drop shadows.
Is there also any way to detect if the png/gif image is already transparent? My code paints the transparent parts into black for those files now.
$file = 'images/18.jpg';
$specs = getimagesize($file);
if($specs[2] == 1) $img = imagecreatefromgif($file); //gif
elseif($specs[2] == 2) $img = imagecreatefromjpeg($file); //jpg
elseif($specs[2] == 3) $img = imagecreatefrompng($file); //png
else exit('unsupported file type!');
$newimg = imagecreatetruecolor(imagesx($img), imagesy($img));
// create a new image with the size of the old one
imagecopy($newimg,$img,0,0,0,0,imagesx($img),imagesy($img));
// copy the old one
imagedestroy($img);
// free the memory
$white = imagecolorallocate($newimg,255,255,255);
imagecolortransparent($newimg,$white);
// make white pixels transparent
header('Content-Type: image/png');
imagepng($newimg);
imagedestroy($newimg);
// and finally output the new image
You can set the transparent color with the imagecolortransparent function.

Categories