PHP read_exif_data 和调整方向

我使用下面的代码来旋转上传的 jpeg 图像,如果方向是关闭的。我只是对从 iPhone 和 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';
}

我从 jpeg 中读取 EXIF 数据的方式有什么问题吗?它没有像预期的那样旋转图像。

这是运行 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) }
108391 次浏览

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);
}

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;
}
}
}

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;
}

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.

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);
}
}

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;
}

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...

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.

Intervention Image has a method orientate().

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

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;
}

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);

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);
}