PHP read_exif_data和调整方向


79

如果方向关闭,我正在使用以下代码旋转上载的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) }

请注意,即使不需要旋转,此代码也将重新压缩源图像。
Marc B

我现在的问题是没有旋转需要旋转的图像。
杰夫·托马斯

进行一次操作,var_dump($exif)以查看Android手机以旋转数据的方式生成的内容。
Marc B

1
好吧,我清理了那里的垃圾场。明显。取向字段未处于“IFD0”部分,它是$exif['COMPUTED']['Orientation']与具有值6
马克乙

1
$ exif ['Orientation']; 对我来说很好。与$ exif ['some_section'] ['Orientation'];相比,这可能是一个更好的选择。
demosten 2012年

Answers:


63

imagerotate文档为第一个参数引用的类型与您使用的类型不同:

由图像创建功能之一(例如imagecreatetruecolor())返回的图像资源。

这是使用此功能的一个小例子:

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

由于某些原因,由android 4.1.2创建的图像不需要旋转,只需通过“ imagecreatefromjpen()”加载图像,然后仅将其保存为“ imagejpeg()”即可。你知道为什么吗?
多伦2014年

75

根据Daniel的代码,我编写了一个函数,该函数仅在必要时旋转图像,而无需重新采样。

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

一线版本(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);
}

图像魔术

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

对于Imagick,我使用getImageOrientation()来获取方向,然后在旋转图像后,通过$ image-> setImageOrientation(\ Imagick :: ORIENTATION_TOPLEFT)设置正确的Exif Orientation值;
Tilman 2014年

您有WideImage解决方案吗?
亚米麦地那

在某些情况下,getImageOrientation()即使使用转换后的原始图像,imagick功能也无法正常工作。上面的代码运行完美。
rokdd

在第一个版本(GD)中,调用此函数时应为&$ image传递什么?
巴拉特(Bharat Maheshwari)

2
对于不了解如何从本地文件传递&$ image参数的用户,可以这样使用:$ im = @imagecreatefromjpeg($ local_filename); image_fix_orientation($ im,$ local_filename); 如果($ im){imagejpeg($ im,$ local_filename); imagedestroy($ im); }
woheras 17-10-26

43

对于上传图像的人来说,功能更简单,它会在必要时自动旋转。

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

1
取得这样的回答到一个简单的作曲家包,它可以在GitHub上找到(类只有一个方法):github.com/diversen/image-auto-rotate
丹尼斯

您使用了错误的度值。在情况6中,您需要90度,在情况8中,您需要-90度。
bernhardh '16

非常有用的功能,如果有人看到此警告,表示IFD大小非法,则可以使用@运算符, 例如$exif = @exif_read_data($filename);
chebaby

@ user462990此功能效果很好,但仅适用于本地提供的图像。如何传递图片网址?我在s3上有一张需要操纵方向的图像。
ultrasamad

12

为什么没人考虑镜像案例2,4,5,7?exif定向土地还有4种情况:

在此处输入图片说明

这是使用文件名的完整解决方案:

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

优秀的解决方案。这是一个好点,因为许多用户上载了镜像图像,但最终图像存在问题。
阿尔伯特·汤普森

6

以防万一有人碰到这个。根据我的判断,上面的一些switch语句是错误的。

根据此处的信息,应为:

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

6

可能值得一提的是,如果从命令行使用ImageMagick,则可以使用-auto-orient选项,该选项将基于现有的EXIF方向数据自动旋转图像。

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

请注意:如果在处理之前删除了EXIF数据,它将无法按说明工作。


2

在这里,我在解释整个过程,我使用Laravel并使用了Image Intervention软件包。

首先,我得到我的图像并将其发送到另一个用于调整大小的功能以及其他一些功能,如果我们不需要此功能,则可以跳过...

使用控制器中的方法来抓取文件,

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

现在,我发送它来调整大小并获取图像名称和扩展名...

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

现在我调用图像定位功能,

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

}

就这样...


1

我不喜欢使用另一组方向值,但是根据我使用上面列出的任何值的经验,当直接从iPhone上传人像方向照片时,我总是会倒置图像。这是我最后得到的switch语句。

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

1

jhead -autorot jpegfile.jpg

也是解决此问题的有用方法。

jhead是Linux中的标准程序(使用'sudo apt-get install jhead'进行安装),此选项查看方向并仅在需要时正确无损地旋转图像。然后,它还会正确更新EXIF数据。

通过这种方式,您可以通过简单的一遍式处理jpeg(或一个文件夹中的多个jpeg)的方法来永久解决旋转问题。

例如:jhead -autorot * .jpg将按照OP在最初问题中要求的方式修复整个jpeg图像文件夹。

虽然从技术上讲它不是PHP,但我确实读过此线程,然后改用我的jhead建议,从PHP system()调用中调用以实现与OP一致的结果:旋转图像,以便任何软件(例如'fbi (在Raspbian中)可以正确显示它们。

有鉴于此,我认为其他人可能会因为了解jhead解决该问题的难易程度而受益,并且仅出于提供信息的目的而在此处发布信息-因为以前没有人提到过。


您的回答很短,因此被标记为低质量。尝试更深入地解释您的解决方案。
德里克·布朗

1

我还使用了orientate()干预形式,它可以完美地工作。

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

1

这是受@ user462990启发的PHP 7函数:

/**
 * @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;
}

用法:

    $rotatedFile = rotateImageByExifOrientation($absoluteFilePath);
    if (is_resource($rotatedFile)) {
        imagejpeg($rotatedFile, $absoluteFilePath, 100);
    }

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.