PHP read_exif_data và Điều chỉnh hướng


79

Tôi đang sử dụng mã sau để xoay hình ảnh jpeg đã tải lên nếu hướng bị tắt. Tôi chỉ gặp sự cố với hình ảnh được tải lên từ iPhone và 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';
        }

Tôi có đang làm gì đó sai với cách đọc dữ liệu EXIF ​​từ jpeg không? Nó không xoay các hình ảnh như nó được cho là.

Đây là những gì sẽ xảy ra khi tôi chạy 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) }

Lưu ý rằng mã này sẽ nén lại hình ảnh nguồn, ngay cả khi không cần xoay.
Marc B

Vấn đề của tôi bây giờ là những hình ảnh cần được xoay không được xoay.
Jeff Thomas

Làm một var_dump($exif)để xem những gì điện thoại Android đang tạo ra theo cách xoay vòng dữ liệu.
Marc B

1
Được rồi, tôi đã dọn dẹp bãi rác ở đó. Chắc chắn. trường định hướng không nằm trong phần 'IFD0', nó $exif['COMPUTED']['Orientation']và có giá trị 6.
Marc B

1
$ exif ['Định hướng']; đang làm việc tốt cho tôi. Nó có thể là một lựa chọn tốt hơn so với $ exif ['some_section'] ['Orientation'];
demosten

Câu trả lời:


63

Tài liệu cho imagerotate đề cập đến một loại khác cho tham số đầu tiên mà bạn sử dụng:

Tài nguyên hình ảnh, được trả về bởi một trong các hàm tạo hình ảnh, chẳng hạn như imageecreatetruecolor ().

Đây là một ví dụ nhỏ để sử dụng chức năng này:

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

Vì một lý do nào đó, hình ảnh được tạo bởi android 4.1.2 không cần phải xoay, chỉ tải hình ảnh bằng "fantasyfromjpen ()" và sau đó chỉ cần lưu lại bằng "imagejpeg ()". Bạn có biết tại sao?
doron

75

Dựa trên mã của Daniel, tôi đã viết một hàm đơn giản là xoay hình ảnh nếu cần thiết mà không cần lấy mẫu lại.

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

Phiên bản một dòng (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;
        }
    }
}

Đối với Imagick, tôi sử dụng getImageOrientation () để truy xuất hướng và sau khi xoay hình ảnh, tôi đặt giá trị Định hướng Exif chính xác qua $ image-> setImageOrientation (\ Imagick :: ORIENTATION_TOPLEFT);
Tilman

Bạn có giải pháp cho WideImage không?
Yami Medina

Trong một số trường hợp, chức năng getImageOrientation()chụp ảnh không hoạt động chính xác đối với tôi ngay cả với hình ảnh thô được chuyển đổi. Đoạn mã trên hoạt động hoàn hảo.
rokdd

Trong phiên bản đầu tiên (GD), tôi nên chuyển những gì cho & $ image nơi tôi đang gọi hàm này?
Bharat Maheshwari

2
ai không hiểu cách truyền tham số & $ image từ tệp cục bộ thì sử dụng như thế: $ im = @imagecreatefromjpeg ($ local_filename); image_fix_orientation ($ im, $ local_filename); if ($ im) {imagejpeg ($ im, $ local_filename); imagedestroy ($ im); }
woheras

43

Chức năng đơn giản hơn cho những người tải lên một hình ảnh, nó chỉ tự động thay đổi nếu cần thiết.

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
Đã tạo câu trả lời này thành một gói trình soạn nhạc đơn giản, có thể tìm thấy trên github (lớp chỉ có một phương thức): github.com/diversen/image-auto-rotate
dennis

Bạn sử dụng các giá trị độ sai. Trong trường hợp 6 bạn cần 90 độ và trong trường hợp 8 bạn cần -90 độ.
bernhardh

chức năng rất hữu ích, nếu có ai thấy điều này cảnh báo kích thước IFD bất hợp pháp bạn có thể sử dụng các toán tử @ ví dụ : $exif = @exif_read_data($filename);
chebaby

@ user462990 Chức năng này hoạt động tốt nhưng chỉ đối với những hình ảnh được cung cấp cục bộ. Làm thế nào để chuyển một url hình ảnh? Tôi có một hình ảnh trên s3 mà tôi cần để thao tác định hướng.
ultrasamad

12

Tại sao không ai xem xét các trường hợp được nhân đôi 2,4,5,7? Còn 4 trường hợp nữa ở đất định hướng exif:

nhập mô tả hình ảnh ở đây

Đây là một giải pháp hoàn chỉnh để lấy tên tệp:

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

Giải pháp tuyệt vời. Đây là một điểm tốt vì nhiều người dùng tải lên hình ảnh được phản chiếu và họ gặp vấn đề với hình ảnh cuối cùng của mình.
Albert Thompson

6

Đề phòng trường hợp ai đó bắt gặp điều này. Từ những gì tôi có thể đưa ra một số câu lệnh switch ở trên là sai.

Dựa trên thông tin ở đây , nó phải là:

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

Có lẽ điều đáng nói là nếu bạn đang sử dụng ImageMagick từ dòng lệnh, bạn có thể sử dụng tùy chọn -auto-direction sẽ tự động xoay hình ảnh dựa trên dữ liệu định hướng EXIF ​​hiện có.

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

Xin lưu ý: Nếu dữ liệu EXIF ​​bị loại bỏ trước quá trình, nó sẽ không hoạt động như mô tả.


2

Ở đây tôi sẽ giải thích toàn bộ vấn đề, tôi sử dụng Laravel và sử dụng Gói can thiệp hình ảnh.

Trước hết, tôi lấy hình ảnh của mình và gửi nó đến một chức năng khác của tôi để thay đổi kích thước và một số chức năng khác, nếu chúng tôi không cần điều này, bạn có thể bỏ qua ...

Lấy tệp bằng một phương thức trong bộ điều khiển của tôi,

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

Bây giờ, tôi gửi nó để thay đổi kích thước và lấy tên hình ảnh và phần mở rộng ...

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

Bây giờ tôi gọi chức năng định hướng hình ảnh của mình,

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

}

Và đó là tất cả...


1

Tôi không thích kết hợp với một bộ giá trị định hướng khác, nhưng theo kinh nghiệm của tôi khi sử dụng bất kỳ giá trị nào được liệt kê ở trên, tôi luôn kết thúc với hình ảnh lộn ngược khi tải lên ảnh chụp hướng dọc trực tiếp từ iPhone. Đây là tuyên bố chuyển đổi mà tôi đã kết thúc.

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

Cũng là một cách hữu ích để tiếp cận điều này.

jhead là một chương trình tiêu chuẩn trong Linux (sử dụng 'sudo apt-get install jhead' để cài đặt), tùy chọn này xem xét hướng và xoay hình ảnh một cách chính xác và không mất mát chỉ khi nó yêu cầu. Sau đó, nó cũng cập nhật dữ liệu EXIF ​​một cách chính xác.

Bằng cách này, bạn có thể xử lý một jpeg (hoặc nhiều jpeg trong một thư mục) theo cách một chiều đơn giản để khắc phục các sự cố xoay vòng vĩnh viễn.

Ví dụ: jhead -autorot * .jpg sẽ sửa toàn bộ thư mục ảnh jpeg theo cách OP yêu cầu trong câu hỏi ban đầu.

Mặc dù nó không phải là PHP về mặt kỹ thuật, tôi đã đọc luồng này và sau đó sử dụng đề xuất jhead của tôi thay thế, được gọi từ lệnh gọi hệ thống PHP () để đạt được kết quả mà tôi đã đạt được sau đó trùng hợp với OP: để xoay hình ảnh nên bất kỳ phần mềm nào (như 'fbi 'trong Raspbian) có thể hiển thị chúng một cách chính xác.

Vì vậy, tôi nghĩ những người khác có thể được lợi khi biết jhead giải quyết vấn đề này dễ dàng như thế nào và chỉ đăng thông tin ở đây với mục đích cung cấp thông tin - vì trước đây chưa ai đề cập đến nó.


Câu trả lời của bạn đã bị gắn cờ là chất lượng thấp vì nó ngắn. Hãy thử giải thích giải pháp của bạn sâu hơn.
Derek Brown

1

Tôi cũng đã sử dụng orientate()Can thiệp biểu mẫu và nó hoạt động hoàn hảo.

    $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

Đây là hàm PHP 7 của tôi được lấy cảm hứng từ @ 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;
}

sử dụng:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.