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

Я что-то делаю не так с тем, как я читаю данные EXIF ​​из jpeg? Он не поворачивает изображения, как предполагалось.

Вот что происходит, когда я запускаю 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
Моя проблема сейчас в том, что изображения, которые нужно повернуть, не поворачиваются.
Джефф Томас,
Сделайте a, var_dump($exif)чтобы увидеть, что телефоны Android производят при вращении данных.
Marc B
1
Хорошо, я убрал там свалку. Очевидно. поле ориентации не находится в разделе 'IFD0', оно $exif['COMPUTED']['Orientation']имеет значение 6.
Marc B
1
$ exif ['Ориентация']; у меня работает нормально. Возможно, это лучший выбор по сравнению с $ exif ['some_section'] ['Orientation'];
demosten

Ответы:

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 ()». Ты знаешь почему?
doron
76

На основе кода Дэниела я написал функцию, которая при необходимости просто поворачивает изображение без повторной выборки.

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

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;
        }
    }
}
Джонатан
источник
Для Imagick я использую getImageOrientation () для получения ориентации, а затем после поворота изображения я устанавливаю правильное значение Exif Orientation с помощью $ image-> setImageOrientation (\ Imagick :: ORIENTATION_TOPLEFT);
Tilman
У вас есть решение для WideImage?
Ями Медина,
В некоторых случаях у getImageOrientation()меня неправильно работала функция imagick даже с преобразованными необработанными изображениями. Код выше работал отлично.
rokdd 05
Что я должен передать в первой версии (GD) для & $ image, где я вызываю эту функцию?
Bharat Maheshwari
2
для тех, кто не понимает, как передать параметр & $ image из локального файла, используйте так: $ im = @imagecreatefromjpeg ($ local_filename); image_fix_orientation ($ im, $ local_filename); если ($ im) {imagejpeg ($ im, $ local_filename); imagedestroy ($ им); }
woheras
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);
    }
}
пользователь462990
источник
1
Превратил этот ответ в простой пакет композитора, который можно найти на github (класс с одним методом): github.com/diversen/image-auto-rotate
dennis
Вы используете неправильные значения градуса. В случае 6 вам нужно 90, а в случае 8 вам нужно -90 градусов.
bernhardh
очень полезная функция, если кто-нибудь увидит это предупреждение Незаконный размер IFD, вы можете использовать оператор @, например : $exif = @exif_read_data($filename);
chebaby
@ user462990 Эта функция работает хорошо, но только для изображений, обслуживаемых локально. Как можно передать URL-адрес изображения? У меня есть изображение на s3, которое мне нужно для управления ориентацией.
ultrasamad 05
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;
} 
mr_crazy_pants
источник
6

Вероятно, стоит упомянуть, что если вы используете ImageMagick из командной строки, вы можете использовать параметр -auto-orient, который автоматически поворачивает изображение на основе существующих данных ориентации EXIF.

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

Обратите внимание: если данные EXIF ​​были удалены до процесса, они не будут работать, как описано.

Кот
источник
2

Здесь я все объясняю, я использую Laravel и пакет Image Intervention Package.

Прежде всего, я получаю свое изображение и отправляю его в другую функцию для изменения размера и некоторых других функций, если нам это не нужно, вы можете пропустить ...

Возьмите файл с методом в моем контроллере,

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

}

И это все...

MD. АБУ-ТАЛЬХА
источник
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 исправит всю папку изображений jpeg точно так же, как OP требует в начальном вопросе.

Хотя технически это не PHP, я прочитал эту ветку, а затем вместо этого использовал мое предложение jhead, вызванное из вызова PHP system (), чтобы достичь результатов, которые были у меня, после которых совпадали с OP: чтобы повернуть изображения, чтобы любое программное обеспечение (например, 'fbi 'в Raspbian) могли отображать их правильно.

В свете этого я подумал, что другим может быть полезно узнать, насколько легко jhead решает эту проблему, и разместил информацию здесь только в информационных целях - потому что никто не упоминал об этом ранее.

GPW
источник
Ваш ответ был отмечен как низкое качество, потому что он был коротким. Попробуйте объяснить свое решение более подробно.
Дерек Браун
1

Я также использовал orientate()форму Intervention, и она работает безупречно.

    $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);
c0ld
источник
1

Вот моя функция PHP 7, вдохновленная @ 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;
}

Применение:

    $rotatedFile = rotateImageByExifOrientation($absoluteFilePath);
    if (is_resource($rotatedFile)) {
        imagejpeg($rotatedFile, $absoluteFilePath, 100);
    }
Себастьян Вирек
источник
-1

У Intervention Image есть метод orientate().

$img = Image::make('foo.jpg')->orientate();
Дэмиен Безбородов
источник