PHP read_exif_data i dostosuj orientację


79

Używam następującego kodu, aby obrócić przesłany obraz JPEG, jeśli orientacja jest wyłączona. Mam problemy tylko z obrazami przesłanymi z iPhone'ów i Androida.

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

Czy robię coś złego w sposobie odczytywania danych EXIF ​​z jpeg? Nie obraca obrazów tak, jak powinien.

Oto, co się dzieje, gdy uruchamiam 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) }

Zauważ, że ten kod ponownie skompresuje obraz źródłowy, nawet jeśli obrót nie był potrzebny.
Marc B

Mój problem polega na tym, że obrazy, które należy obrócić, nie są obracane.
Jeff Thomas,

Zrób, var_dump($exif)aby zobaczyć, co produkują telefony z Androidem w zakresie danych rotacji.
Marc B

1
Ok, posprzątałem tam wysypisko. Oczywiście. pole orientacji nie znajduje się w sekcji „IFD0”, $exif['COMPUTED']['Orientation']ma wartość 6.
Marc B

1
$ exif ['Orientacja']; działa dobrze dla mnie. To może być lepszy wybór w porównaniu do $ exif ['some_section'] ['Orientation'];
demosten

Odpowiedzi:


63

Dokumentacja imagerotate odnosi się do innego typu pierwszego parametru niż używany:

Zasób obrazu zwracany przez jedną z funkcji tworzenia obrazu, na przykład imagecreatetruecolor ().

Oto mały przykład użycia tej funkcji:

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

Z jakiegoś powodu obrazy utworzone przez Androida 4.1.2 nie muszą być obracane, wystarczy załadować je za pomocą funkcji „imagecreatefromjpen ()”, a następnie po prostu zapisać je z powrotem za pomocą funkcji „imagejpeg ()”. Wiesz dlaczego?
doron

76

Na podstawie kodu Daniela napisałem funkcję, która w razie potrzeby po prostu obraca obraz, bez ponownego próbkowania.

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

Wersja jednoprzewodowa (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;
        }
    }
}

W przypadku Imagick używam getImageOrientation (), aby pobrać orientację, a następnie po obróceniu obrazu ustawiam prawidłową wartość Exif Orientation za pomocą $ image-> setImageOrientation (\ Imagick :: ORIENTATION_TOPLEFT);
Tilman

Czy masz rozwiązanie dla WideImage?
Yami Medina

W niektórych przypadkach funkcja imagick getImageOrientation()nie działała poprawnie nawet w przypadku przekonwertowanych obrazów surowych. Powyższy kod działał idealnie.
rokdd

W pierwszej wersji (GD) co ​​powinienem przekazać & $ image, gdzie wywołuję tę funkcję?
Bharat Maheshwari,

2
dla kogo nie rozumie, jak przekazać parametr & $ image z pliku lokalnego, użyj w ten sposób: $ im = @imagecreatefromjpeg ($ local_filename); image_fix_orientation ($ im, $ local_filename); if ($ im) {imagejpeg ($ im, $ nazwa_lokalnego_pliku); imagedestroy ($ im); }
woheras

43

Prostsza funkcja dla osób przesyłających obraz, po prostu automatycznie obraca się, jeśli to konieczne.

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
Złożyłem tę odpowiedź w prostym pakiecie kompozytora, który można znaleźć na github (klasa z tylko jedną metodą): github.com/diversen/image-auto-rotate
dennis

Używasz niewłaściwych wartości stopni. W przypadku 6 potrzebujesz 90, aw przypadku 8 potrzebujesz -90 stopni.
bernhardh

bardzo przydatna funkcja, jeśli ktoś zobaczy to ostrzeżenie Nielegalny rozmiar IFD możesz użyć operatora @ np : $exif = @exif_read_data($filename);
chebaby

@ user462990 Ta funkcja działa dobrze, ale tylko w przypadku obrazów obsługiwanych lokalnie. Jak podejmie się przekazywania adresu URL obrazu? Mam obraz na s3, którego potrzebowałem, aby zmienić orientację.
ultrasamad

12

Dlaczego nikt nie bierze pod uwagę przypadków lustrzanych 2,4,5,7? W krainie orientacji exif są jeszcze 4 przypadki:

wprowadź opis obrazu tutaj

Oto kompletne rozwiązanie przyjmujące nazwę pliku:

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

Doskonałe rozwiązanie. To był dobry punkt, ponieważ wielu użytkowników przesyła obrazy lustrzane i mają problemy z ich ostatecznym obrazem.
Albert Thompson,

6

Na wypadek, gdyby ktoś się z tym spotkał. Z tego, co mogę stwierdzić, niektóre z powyższych stwierdzeń dotyczących przełącznika są błędne.

Bazując na informacjach tutaj , powinno to być:

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

Prawdopodobnie warto wspomnieć, że jeśli używasz ImageMagick z wiersza poleceń, możesz użyć opcji -auto-orient , która automatycznie obróci obraz na podstawie istniejących danych orientacji EXIF.

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

Uwaga: jeśli dane EXIF ​​zostały usunięte przed procesem, nie będą działać zgodnie z opisem.


2

Tutaj wyjaśniam całą sprawę, używam Laravel i używam pakietu interwencji obrazu.

Przede wszystkim dostaję swój obraz i wysyłam go do mojej innej funkcji w celu zmiany rozmiaru i innej funkcjonalności, jeśli tego nie potrzebujemy, możesz pominąć ...

Pobierz plik za pomocą metody w moim kontrolerze,

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

Teraz wysyłam go, aby zmienić rozmiar i uzyskać nazwę obrazu i rozszerzenie ...

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

Teraz nazywam moją funkcję orientacji obrazu,

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

}

I to wszystko...


1

Nienawidzę dzwonić z kolejnym zestawem wartości orientacji, ale z mojego doświadczenia przy korzystaniu z którejkolwiek z wartości wymienionych powyżej zawsze kończyło się na odwróconych obrazach podczas przesyłania zdjęć w orientacji pionowej bezpośrednio z iPhone'a. Oto instrukcja przełącznika, z którą skończyłem.

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

Jest to również przydatny sposób podejścia do tego.

jhead to standardowy program w Linuksie (użyj 'sudo apt-get install jhead', aby zainstalować), ta opcja sprawdza orientację i obraca obraz poprawnie i bezstratnie tylko wtedy, gdy tego wymaga. Następnie poprawnie aktualizuje dane EXIF.

W ten sposób możesz przetworzyć plik jpeg (lub wiele plików jpeg w folderze) w prosty, jednoprzebiegowy sposób, który trwale rozwiązuje problemy z obracaniem.

Np. Jhead -autorot * .jpg naprawi cały folder z obrazami jpeg w sposób, jakiego OP wymaga w pierwszym pytaniu.

Chociaż technicznie nie jest to PHP, przeczytałem ten wątek, a następnie zamiast tego użyłem mojej sugestii jhead, wywołanej z wywołania systemu PHP (), aby osiągnąć wyniki, po których byłem, a które były zbieżne z operacjami: aby obracać obrazy, więc dowolne oprogramowanie (takie jak 'fbi 'w Raspbian) może wyświetlać je poprawnie.

W świetle tego pomyślałem, że inni mogą skorzystać na wiedzy, jak łatwo jhead rozwiązuje ten problem i zamieścił tutaj informacje tylko w celach informacyjnych - ponieważ nikt wcześniej o tym nie wspomniał.


Twoja odpowiedź została oznaczona jako niskiej jakości, ponieważ była krótka. Spróbuj dokładniej wyjaśnić swoje rozwiązanie.
Derek Brown

1

Użyłem też orientate()formularza Intervention i działa bez zarzutu.

    $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

Oto moja funkcja PHP 7 zainspirowana @ 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;
}

stosowanie:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.