PHP read_exif_data und Ausrichtung anpassen

79

Ich verwende den folgenden Code, um ein hochgeladenes JPEG-Bild zu drehen, wenn die Ausrichtung nicht stimmt. Ich habe nur Probleme mit Bildern, die von iPhones und Android hochgeladen wurden.

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

Mache ich etwas falsch mit der Art und Weise, wie ich die EXIF-Daten vom JPEG lese? Es dreht die Bilder nicht so, wie es soll.

Dies passiert, wenn ich einen var_dump ($ exif) ausführe.

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) }
Jeff Thomas
quelle
Beachten Sie, dass dieser Code das Quellbild erneut komprimiert, auch wenn keine Drehung erforderlich war.
Marc B
Mein aktuelles Problem ist, dass die Bilder, die gedreht werden müssen, nicht gedreht werden.
Jeff Thomas
Machen Sie eine, um var_dump($exif)zu sehen, was die Android-Handys in Bezug auf Rotationsdaten produzieren.
Marc B
1
Ok, ich habe die Müllkippe dort aufgeräumt. Offensichtlich. Das Orientierungsfeld befindet sich nicht in einem 'IFD0'-Abschnitt, es $exif['COMPUTED']['Orientation']hat den Wert 6.
Marc B
1
$ exif ['Orientierung']; funktioniert gut für mich. Es könnte eine bessere Wahl sein als $ exif ['some_section'] ['Orientation'];
Demosten

Antworten:

63

Die Dokumentation für imagerotate bezieht sich auf einen anderen Typ für den ersten Parameter als Sie verwenden:

Eine Bildressource, die von einer der Bilderstellungsfunktionen zurückgegeben wird, z. B. imagecreatetruecolor ().

Hier ist ein kleines Beispiel für die Verwendung dieser Funktion:

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);
}
Daniel Bleisteiner
quelle
Aus irgendeinem Grund müssen mit Android 4.1.2 erstellte Bilder nicht gedreht werden. Laden Sie das Bild nur mit "imagecreatefromjpen ()" und speichern Sie es dann einfach mit "imagejpeg ()" zurück. Weißt du, warum?
Doron
75

Basierend auf Daniels Code habe ich eine Funktion geschrieben, die bei Bedarf einfach ein Bild dreht, ohne es erneut abzutasten.

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

Einzeilige 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;
        }
    }
}
Jonathan
quelle
Für Imagick verwende ich getImageOrientation (), um die Ausrichtung abzurufen, und nach dem Drehen des Bildes setze ich den korrekten Exif-Orientierungswert über $ image-> setImageOrientation (\ Imagick :: ORIENTATION_TOPLEFT);
Tilman
Haben Sie eine Lösung für WideImage?
Yami Medina
In einigen Fällen funktionierte die Imagick-Funktion getImageOrientation()bei mir selbst bei konvertierten Rohbildern nicht richtig. Der obige Code hat perfekt funktioniert.
Rokdd
Was soll ich in der ersten Version (GD) für & $ image übergeben, wo ich diese Funktion aufrufe?
Bharat Maheshwari
2
Für diejenigen, die nicht verstehen, wie der Parameter & $ image aus der lokalen Datei übergeben wird, verwenden Sie Folgendes: $ im = @imagecreatefromjpeg ($ local_filename); image_fix_orientation ($ im, $ local_filename); if ($ im) {imagejpeg ($ im, $ local_filename); imagedestroy ($ im); }
Personenas
43

Einfachere Funktion für diejenigen, die ein Bild hochladen. Bei Bedarf wird nur eine Autorotation durchgeführt.

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);
    }
}
user462990
quelle
1
Aus dieser Antwort wurde ein einfaches Komponistenpaket, das auf github (Klasse mit nur einer Methode) zu finden ist: github.com/diversen/image-auto-rotate
dennis
Sie verwenden die falschen Gradwerte. In Fall 6 benötigen Sie 90 und in Fall 8 benötigen Sie -90 Grad.
bernhardh
Sehr nützliche Funktion, wenn jemand diese Warnung sieht. Illegale IFD-Größe. Sie können den @ -Operator verwenden, z.$exif = @exif_read_data($filename);
B
@ user462990 Diese Funktion funktioniert gut, jedoch nur für lokal bereitgestellte Bilder. Wie wird man eine Bild-URL übergeben? Ich habe ein Bild auf s3, das ich brauchte, um die Ausrichtung zu manipulieren.
Ultrasamad
12

Warum erwägt niemand gespiegelte Fälle 2,4,5,7? Es gibt 4 weitere Fälle im Exif-Orientierungsland:

Geben Sie hier die Bildbeschreibung ein

Hier ist eine vollständige Lösung mit einem Dateinamen:

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;
}
David Vielhuber
quelle
Hervorragende Lösung. Dies war ein guter Punkt, da viele Benutzer gespiegelte Bilder hochladen und Probleme mit ihrem endgültigen Bild haben.
Albert Thompson
6

Nur für den Fall, dass jemand darauf stößt. Soweit ich das beurteilen kann, sind einige der obigen switch-Anweisungen falsch.

Basierend auf Informationen hier sollte es sein:

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
quelle
6

Es ist wahrscheinlich erwähnenswert, dass Sie, wenn Sie ImageMagick über die Befehlszeile verwenden, die Option -auto-orient verwenden können, mit der das Bild basierend auf den vorhandenen EXIF-Orientierungsdaten automatisch gedreht wird.

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

Bitte beachten Sie: Wenn die EXIF-Daten vor dem Vorgang entfernt wurden, funktionieren sie nicht wie beschrieben.

Katze
quelle
2

Hier erkläre ich das Ganze, ich benutze Laravel und benutze das Image Intervention Package.

Zunächst erhalte ich mein Bild und sende es an meine andere Funktion zum Ändern der Größe und für einige andere Funktionen. Wenn wir dies nicht benötigen, können Sie ...

Holen Sie sich die Datei mit einer Methode in meinem Controller,

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

Jetzt sende ich es, um die Größe zu ändern und den Bildnamen und die Erweiterung zu erhalten ...

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

Jetzt rufe ich meine Bildorientierungsfunktion auf,

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

}

Und das ist alles...

MD. ABU TALHA
quelle
1

Ich hasse es, noch einen weiteren Satz von Orientierungswerten zu verwenden, aber meiner Erfahrung nach habe ich beim Hochladen von Porträt-Orientierungsaufnahmen direkt von einem iPhone immer verkehrte Bilder erhalten. Hier ist die switch-Anweisung, mit der ich gelandet bin.

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;
    }
Brad Root
quelle
1

jhead -autorot jpegfile.jpg

Ist auch ein nützlicher Weg, um dies zu erreichen.

jhead ist ein Standardprogramm unter Linux (verwenden Sie zum Installieren 'sudo apt-get install jhead'). Diese Option überprüft die Ausrichtung und dreht das Bild nur dann korrekt und verlustfrei, wenn dies erforderlich ist. Anschließend werden auch die EXIF-Daten korrekt aktualisiert.

Auf diese Weise können Sie ein JPEG (oder mehrere JPEGs in einem Ordner) auf einfache Weise in einem Durchgang verarbeiten, um Rotationsprobleme dauerhaft zu beheben.

Beispiel: jhead -autorot * .jpg repariert einen ganzen Ordner mit JPEG-Bildern genau so, wie es das OP in der ersten Frage benötigt.

Obwohl es technisch gesehen kein PHP ist, habe ich diesen Thread gelesen und stattdessen meinen jhead-Vorschlag verwendet, der von einem PHP-System () -Aufruf aufgerufen wurde, um die Ergebnisse zu erzielen, nach denen ich mit den OPs übereinstimmte: Bilder so zu drehen, dass jede Software (wie 'fbi 'in Raspbian) könnte sie korrekt anzeigen.

Vor diesem Hintergrund dachte ich, andere könnten davon profitieren, zu wissen, wie einfach jhead dieses Problem löst, und veröffentlichte die Informationen hier nur zu Informationszwecken - weil sie zuvor noch niemand erwähnt hatte.

GPW
quelle
Ihre Antwort wurde als minderwertig gekennzeichnet, da sie kurz war. Erklären Sie Ihre Lösung ausführlicher.
Derek Brown
1

Ich habe auch orientate()Form Intervention verwendet und es funktioniert einwandfrei.

    $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
quelle
1

Hier ist meine PHP 7-Funktion, inspiriert von @ 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;
}

Verwendung:

    $rotatedFile = rotateImageByExifOrientation($absoluteFilePath);
    if (is_resource($rotatedFile)) {
        imagejpeg($rotatedFile, $absoluteFilePath, 100);
    }
Sebastian Viereck
quelle
-1

Interventionsbild hat eine Methode orientate().

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