Orientacja Exif po stronie klienta JS: Obracanie i lustrzane odbicie obrazów JPEG


154

Zdjęcia z aparatów cyfrowych są często zapisywane w formacie JPEG ze znacznikiem „orientacji” EXIF. Aby wyświetlać poprawnie, obrazy muszą być obracane / lustrzane w zależności od ustawionej orientacji, ale przeglądarki ignorują tę informację, renderując obraz. Nawet w dużych komercyjnych aplikacjach internetowych obsługa orientacji EXIF ​​może być nierówna 1 . To samo źródło zawiera również ładne podsumowanie 8 różnych orientacji, jakie może mieć JPEG:

Podsumowanie orientacji EXIF

Przykładowe obrazy są dostępne pod adresem 4 .

Pytanie brzmi, jak obrócić / odbić obraz po stronie klienta, aby wyświetlał się poprawnie i w razie potrzeby mógł być dalej przetwarzany?

Dostępne są biblioteki JS do analizowania danych EXIF, w tym atrybutu orientacji 2 . Flickr zauważył możliwy problem z wydajnością podczas analizowania dużych obrazów, co wymaga użycia pracowników sieci 3 .

Narzędzia konsoli mogą poprawnie zmienić orientację obrazów 5 . Skrypt PHP rozwiązujący ten problem jest dostępny pod numerem 6

Odpowiedzi:


145

Projekt JavaScript-Load-Image na githubie zapewnia kompletne rozwiązanie problemu orientacji EXIF, poprawnie obracając / odbierając lustrzane odbicie obrazów dla wszystkich 8 orientacji exif. Zobacz demo online z orientacją javascript exif

Obraz jest rysowany na płótnie HTML5. Jego poprawne renderowanie jest implementowane w js / load-image-Orientation.js poprzez operacje na płótnie.

Mam nadzieję, że to zaoszczędzi komuś trochę czasu i uczy wyszukiwarki o tym klejnocie open source :)


1
Korzystam z tej biblioteki, ale ostatnio zepsuła się na iOS 8.3, gdzie potrzebuję jej najbardziej do pracy :(
Gordon niedz.

2
Naprawdę staram się zobaczyć, jak to jest pomocne. Bawię się tym, próbując się tego nauczyć, więc mogę albo przeanalizować stronę i obrócić obrazy, gdy trzeba je obrócić, albo wykryć orientację i obrócić plik (w jakiś sposób) przed wysłaniem go. Demo też nie. Po prostu pobiera plik z pliku wejściowego i wyświetla go we właściwy sposób, kiedy jest to przydatne w prawdziwym świecie? Kiedy analizuję moją stronę i przesyłam adresy URL z tagów obrazu do biblioteki loadImage, nie ma danych exif, więc nie mogę tego zrobić. Do przesłania zwraca obiekt płótna, więc nie mogę wysłać go na serwer ani nic.
igneozaur

3
@igneosaur: możesz wysyłać dane zakodowane w base64 do serwera za pomocą, canvas.toDataURL()dekodować i zapisywać po stronie serwera.
br4nnigan

1
zamiast używać projektu load-image, możesz użyć części jego kodu do wypalenia własnego - po prostu zajrzyj na github.com/blueimp/JavaScript-Load-Image/blob/master/js/… .
Andy Lorenz

1
Czy istnieje sposób, aby płótno, które tworzy ta biblioteka, było responsywne jak zwykły <img>tag?
Erik Berkun-Drevnig

103

Transformacja kontekstu Mederr działa doskonale. Jeśli chcesz wyodrębnić orientację, użyj tylko tej funkcji - nie potrzebujesz żadnych bibliotek do odczytu EXIF. Poniżej znajduje się funkcja ponownego ustawiania orientacji w obrazie base64. Oto skrzypce do tego . Przygotowałem też skrzypce z demo ekstrakcji orientacji .

function resetOrientation(srcBase64, srcOrientation, callback) {
  var img = new Image();    

  img.onload = function() {
    var width = img.width,
        height = img.height,
        canvas = document.createElement('canvas'),
        ctx = canvas.getContext("2d");

    // set proper canvas dimensions before transform & export
    if (4 < srcOrientation && srcOrientation < 9) {
      canvas.width = height;
      canvas.height = width;
    } else {
      canvas.width = width;
      canvas.height = height;
    }

    // transform context before drawing image
    switch (srcOrientation) {
      case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
      case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
      case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
      case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
      case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
      case 7: ctx.transform(0, -1, -1, 0, height, width); break;
      case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
      default: break;
    }

    // draw image
    ctx.drawImage(img, 0, 0);

    // export base64
    callback(canvas.toDataURL());
  };

  img.src = srcBase64;
};

7
Domyślny przypadek w orientacji switchnie jest potrzebny, ponieważ ta transformacja nic nie robi. Rozważ również użycie srcOrientation > 4 && srcOrientation < 9zamiast [5,6,7,8].indexOf(srcOrientation) > -1, ponieważ jest szybszy i mniej zasobochłonny (zarówno pamięć RAM, jak i procesor). Nie ma potrzeby posiadania tam tablicy. Jest to ważne podczas grupowania wielu obrazów, gdzie liczy się każdy bit. W przeciwnym razie całkiem dobra odpowiedź. Głosowano!
Ryan Casas

4
@RyanCasas Nie zdawałem sobie sprawy, jak ciężkie może indexOfbyć w porównaniu z tym, co proponujesz. Uruchomiłem prostą pętlę z iteracjami 10M i była o 1400% szybsza. Fajnie: D Wielkie dzięki!
WunderBart

1
Użyłem tej odpowiedzi w Android WebView i okazało się, że jest kilka urządzeń z Androidem, które nie obsługują WebGL w WebView (patrz bugs.chromium.org/p/chromium/issues/detail?id=555116 ) obrót może zająć bardzo dużo czasu na takich urządzeniach w zależności od rozmiaru obrazu.
ndreisg

1
W przypadku użycia z przywołanym materiałem getOrientationjestem ciekawy, czy jest to wydajne pod względem wydajności. getOrientationwywołania fileReader.readAsArrayBuffer, a następnie wywołujemy URL.createObjectURLi przekazujemy wynik do resetOrientation, który ładuje ten adres URL jako obraz. Czy to oznacza, że ​​plik obrazu zostanie „załadowany” / odczytany przez przeglądarkę nie raz, ale dwa razy, czy źle rozumiem?
Oliver Joseph Ash

1
Co zrobić z przeglądarkami, w których orientacja będzie już przestrzegana? Uważam, że tak jest w przypadku iOS Safari i przeglądarek obsługujących CSS, image-orientation: from-imagegdy jest używany.
Oliver Joseph Ash

40

Jeśli

width = img.width;
height = img.height;
var ctx = canvas.getContext('2d');

Następnie możesz użyć tych przekształceń, aby obrócić obraz do orientacji 1

Od orientacji:

  1. ctx.transform(1, 0, 0, 1, 0, 0);
  2. ctx.transform(-1, 0, 0, 1, width, 0);
  3. ctx.transform(-1, 0, 0, -1, width, height);
  4. ctx.transform(1, 0, 0, -1, 0, height);
  5. ctx.transform(0, 1, 1, 0, 0, 0);
  6. ctx.transform(0, 1, -1, 0, height, 0);
  7. ctx.transform(0, -1, -1, 0, height, width);
  8. ctx.transform(0, -1, 1, 0, 0, width);

Przed narysowaniem obrazu na ctx


58
co się tu w ogóle dzieje?
Muhammad Umer

1
Dzięki temu wystarczy znać orientację (np. Przez EXIF), a następnie obracać w razie potrzeby. Połowa tego, czego szukam.
Brenden

2
te transformacje nie zadziałały dla mnie - zamiast tego użyłem kodu z projektu load-image na github.com/blueimp/JavaScript-Load-Image/blob/master/js/ ... aby uzyskać to, co uważam za dobrze sprawdzony kod która używa operacji tłumaczenia i obracania w kontekście płótna oraz zamiany szerokości / wysokości. Dał mi 100% wyników po wielu godzinach eksperymentowania z innymi próbami transformacji itp.
Andy Lorenz

1
Dlaczego ma to znaczenie, kiedy rysujesz obraz na ctx? Nie możesz obrócić płótna po narysowaniu na nim obrazu?
AlxVallejo,

Obrót dla orientacji 8 działa u mnie w ten sposób: ctx.transform (0, -1, 1, 0, 0, height);
Giorgio Barchiesi

24

ok oprócz odpowiedzi @ user3096626 myślę, że będzie bardziej pomocne, jeśli ktoś poda przykład kodu, poniższy przykład pokaże, jak naprawić orientację obrazu pochodzącą z adresu URL (zdalne obrazy):


Rozwiązanie 1: użycie javascript (zalecane)

  1. ponieważ biblioteka load-image nie wyodrębnia tagów exif tylko z obrazów url (plik / blob), użyjemy zarówno bibliotek javascript exif-js, jak i load-image , więc najpierw dodaj te biblioteki do swojej strony w następujący sposób:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/exif-js/2.1.0/exif.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-scale.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-load-image/2.12.2/load-image-orientation.min.js"></script>

    Zauważ, że wersja 2.2 exif-js wydaje się mieć problemy, więc użyliśmy 2.1

  2. to w zasadzie to, co zrobimy, jest

    a - załaduj obraz za pomocą window.loadImage()

    b - czytaj znaczniki exif używając window.EXIF.getData()

    c - przekonwertuj obraz na płótno i napraw orientację obrazu za pomocą window.loadImage.scale()

    d - umieść płótno w dokumencie

proszę bardzo :)

window.loadImage("/your-image.jpg", function (img) {
  if (img.type === "error") {
    console.log("couldn't load image:", img);
  } else {
    window.EXIF.getData(img, function () {
        var orientation = EXIF.getTag(this, "Orientation");
        var canvas = window.loadImage.scale(img, {orientation: orientation || 0, canvas: true});
        document.getElementById("container").appendChild(canvas); 
        // or using jquery $("#container").append(canvas);

    });
  }
});

oczywiście możesz również pobrać obraz jako base64 z obiektu canvas i umieścić go w atrybucie img src, więc używając jQuery możesz to zrobić;)

$("#my-image").attr("src",canvas.toDataURL());

tutaj jest pełny kod na: github: https://github.com/digital-flowers/loadimage-exif-example


Rozwiązanie 2: Używanie html (hack przeglądarki)

jest bardzo szybki i łatwy hack, większość przeglądarek wyświetla obraz we właściwej orientacji, jeśli obraz jest otwierany bezpośrednio w nowej karcie bez kodu HTML (LOL nie wiem dlaczego), więc w zasadzie możesz wyświetlić obraz za pomocą iframe umieszczając atrybut iframe src bezpośrednio jako adres URL obrazu:

<iframe src="/my-image.jpg"></iframe>

Rozwiązanie 3: Korzystanie z CSS (tylko Firefox i Safari na iOS)

istnieje atrybut css3 do naprawy orientacji obrazu, ale problem, z którym działa tylko na Firefox i Safari / iOS, jest nadal warty uwagi, ponieważ wkrótce będzie dostępny dla wszystkich przeglądarek (informacje o obsłudze przeglądarek z caniuse )

img {
   image-orientation: from-image;
}

Rozwiązanie 1 nie działa dla mnie. Zwraca okno.loadImage jest niezdefiniowane
Perry,

Stało się tak, jeśli nie uwzględniono bibliotek, o których wspominam w kroku 1
Fareed Alnamrouti

Włączyłem biblioteki. Podoba mi się prostota twojego przykładu, ale ciągle otrzymuję ten komunikat o błędzie.
Perry,

1
wygląda na to, że exif-js został uszkodzony, sprawdź moją edycję Dodałem również pełny kod na github: github.com/digital-flowers/loadimage-exif-example
Fareed Alnamrouti

1
ok sprawdź projekt na githubie jest nowy plik "upload.html", nie ma za co :)
Fareed Alnamrouti

10

Dla tych, którzy mają plik z kontrolki wejściowej, nie wiedzą, jaka jest jego orientacja, są nieco leniwi i nie chcą dołączać dużej biblioteki poniżej, kod dostarczony przez @WunderBart połączony z odpowiedzią, do której prowadzi ( https://stackoverflow.com/a/32490603 ), który znajdzie orientację.

function getDataUrl(file, callback2) {
        var callback = function (srcOrientation) {
            var reader2 = new FileReader();
            reader2.onload = function (e) {
                var srcBase64 = e.target.result;
                var img = new Image();

                img.onload = function () {
                    var width = img.width,
                        height = img.height,
                        canvas = document.createElement('canvas'),
                        ctx = canvas.getContext("2d");

                    // set proper canvas dimensions before transform & export
                    if (4 < srcOrientation && srcOrientation < 9) {
                        canvas.width = height;
                        canvas.height = width;
                    } else {
                        canvas.width = width;
                        canvas.height = height;
                    }

                    // transform context before drawing image
                    switch (srcOrientation) {
                        case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
                        case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
                        case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
                        case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
                        case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
                        case 7: ctx.transform(0, -1, -1, 0, height, width); break;
                        case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
                        default: break;
                    }

                    // draw image
                    ctx.drawImage(img, 0, 0);

                    // export base64
                    callback2(canvas.toDataURL());
                };

                img.src = srcBase64;
            }

            reader2.readAsDataURL(file);
        }

        var reader = new FileReader();
        reader.onload = function (e) {

            var view = new DataView(e.target.result);
            if (view.getUint16(0, false) != 0xFFD8) return callback(-2);
            var length = view.byteLength, offset = 2;
            while (offset < length) {
                var marker = view.getUint16(offset, false);
                offset += 2;
                if (marker == 0xFFE1) {
                    if (view.getUint32(offset += 2, false) != 0x45786966) return callback(-1);
                    var little = view.getUint16(offset += 6, false) == 0x4949;
                    offset += view.getUint32(offset + 4, little);
                    var tags = view.getUint16(offset, little);
                    offset += 2;
                    for (var i = 0; i < tags; i++)
                        if (view.getUint16(offset + (i * 12), little) == 0x0112)
                            return callback(view.getUint16(offset + (i * 12) + 8, little));
                }
                else if ((marker & 0xFF00) != 0xFF00) break;
                else offset += view.getUint16(offset, false);
            }
            return callback(-1);
        };
        reader.readAsArrayBuffer(file);
    }

które można łatwo nazwać w ten sposób

getDataUrl(input.files[0], function (imgBase64) {
      vm.user.BioPhoto = imgBase64;
});

2
Dziękuję po ponad 2-godzinnym wyszukiwaniu w google znajduję odpowiednie rozwiązanie.
Pan Trieu,

2
dlaczego ktoś byłby leniwy, gdyby wolał dużo lżejsze rozwiązanie?
dewd

4
Przeniesiono do Typescript i zoptymalizowano od około 4 sekund do ~ 20 milisekund. Kodowanie Base64 było wąskim gardłem - użycie image/jpegzamiast domyślnego image/pngi zmiana rozmiaru obrazu wyjściowego do maksymalnej szerokości (w moim przypadku 400px) spowodowało ogromną różnicę. (działa to dobrze w przypadku miniatur, ale jeśli musisz wyświetlić pełny obraz, sugerowałbym unikanie base64 i po prostu wstrzyknięcie elementu canvas bezpośrednio na stronę ...)
mindplay.dk

8

Odpowiedź WunderBart była dla mnie najlepsza. Zwróć uwagę, że możesz to znacznie przyspieszyć, jeśli obrazy są często we właściwej kolejności, po prostu najpierw testując orientację i pomijając resztę kodu, jeśli nie jest wymagana rotacja.

Zbieranie wszystkich informacji z wunderbart razem, coś takiego;

var handleTakePhoto = function () {
    let fileInput: HTMLInputElement = <HTMLInputElement>document.getElementById('photoInput');
    fileInput.addEventListener('change', (e: any) => handleInputUpdated(fileInput, e.target.files));
    fileInput.click();
}

var handleInputUpdated = function (fileInput: HTMLInputElement, fileList) {
    let file = null;

    if (fileList.length > 0 && fileList[0].type.match(/^image\//)) {
        isLoading(true);
        file = fileList[0];
        getOrientation(file, function (orientation) {
            if (orientation == 1) {
                imageBinary(URL.createObjectURL(file));
                isLoading(false);
            }
            else 
            {
                resetOrientation(URL.createObjectURL(file), orientation, function (resetBase64Image) {
                    imageBinary(resetBase64Image);
                    isLoading(false);
                });
            }
        });
    }

    fileInput.removeEventListener('change');
}


// from http://stackoverflow.com/a/32490603
export function getOrientation(file, callback) {
    var reader = new FileReader();

    reader.onload = function (event: any) {
        var view = new DataView(event.target.result);

        if (view.getUint16(0, false) != 0xFFD8) return callback(-2);

        var length = view.byteLength,
            offset = 2;

        while (offset < length) {
            var marker = view.getUint16(offset, false);
            offset += 2;

            if (marker == 0xFFE1) {
                if (view.getUint32(offset += 2, false) != 0x45786966) {
                    return callback(-1);
                }
                var little = view.getUint16(offset += 6, false) == 0x4949;
                offset += view.getUint32(offset + 4, little);
                var tags = view.getUint16(offset, little);
                offset += 2;

                for (var i = 0; i < tags; i++)
                    if (view.getUint16(offset + (i * 12), little) == 0x0112)
                        return callback(view.getUint16(offset + (i * 12) + 8, little));
            }
            else if ((marker & 0xFF00) != 0xFF00) break;
            else offset += view.getUint16(offset, false);
        }
        return callback(-1);
    };

    reader.readAsArrayBuffer(file.slice(0, 64 * 1024));
};

export function resetOrientation(srcBase64, srcOrientation, callback) {
    var img = new Image();

    img.onload = function () {
        var width = img.width,
            height = img.height,
            canvas = document.createElement('canvas'),
            ctx = canvas.getContext("2d");

        // set proper canvas dimensions before transform & export
        if (4 < srcOrientation && srcOrientation < 9) {
            canvas.width = height;
            canvas.height = width;
        } else {
            canvas.width = width;
            canvas.height = height;
        }

        // transform context before drawing image
        switch (srcOrientation) {
            case 2: ctx.transform(-1, 0, 0, 1, width, 0); break;
            case 3: ctx.transform(-1, 0, 0, -1, width, height); break;
            case 4: ctx.transform(1, 0, 0, -1, 0, height); break;
            case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
            case 6: ctx.transform(0, 1, -1, 0, height, 0); break;
            case 7: ctx.transform(0, -1, -1, 0, height, width); break;
            case 8: ctx.transform(0, -1, 1, 0, 0, width); break;
            default: break;
        }

        // draw image
        ctx.drawImage(img, 0, 0);

        // export base64
        callback(canvas.toDataURL());
    };

    img.src = srcBase64;
}

1
Świetne podejście. Powinieneś także rozważyć obsługę -2i -1powrót z, getOrientationaby nie próbować obracać plików innych niż jpg lub obrazów bez danych dotyczących obrotu.
Steven Lambert

Zrobiłem więcej optymalizacji, zobacz mój komentarz powyżej - właśnie dodałem również twoją file.slice()optymalizację, ale prawdziwy wzrost polega na użyciu mniejszych obrazów i image/jpegformatu wyjściowego do tworzenia mniejszych adresów URL danych. (nie ma zauważalnego opóźnienia nawet dla zdjęć 10-megapikselowych.)
mindplay.dk

Ostrożnie file.slice(), ponieważ zapobiegniesz obsłudze źródeł obrazu base64.
Mark

Dziękuję - chcesz udostępnić zmianę dla alternatywy?
statler

7

Ktoś ma jedną wkładkę?

Nie widziałem nikogo wspominającego o browser-image-compressionbibliotece . Ma funkcję pomocniczą idealną do tego.

Stosowanie: const orientation = await imageCompression.getExifOrientation(file)

Takie przydatne narzędzie również na wiele innych sposobów.


To daje orientację. Ale jak przy użyciu tych informacji utworzyć nowy obiekt pliku .jpg po stronie klienta?
masterxilo

FileO ile wiem, nie możesz tworzyć obiektów. Następną najlepszą rzeczą byłoby wysłanie obrazu i orientacji na serwer i wykonanie tam manipulacji plikami. Lub możesz wygenerować ciąg base64 za pomocą canvasi toDataURLmetody.
gilbert-v

1
Nie, możesz stworzyć obiekt File z Bloba. Plik Plik (części tablicy, nazwa pliku ciągu, właściwości BlobPropertyBag); developer.mozilla.org/de/docs/Web/API/File
masterxilo

2
Złota naklejka! @masterxilo
gilbert-v

2
To działało świetnie dla mnie! Od 2 dni zmagałem się z orientacjami! Wszystkie opublikowane oświadczenia dotyczące przełączania transformacji z jakiegoś powodu nie obsługiwałyby zdjęć portretowych z mojego telefonu, byłyby czarne paski. Prawdopodobnie dlatego, że próbowałem kompresować i obracać w tym samym czasie.
cjd82187


1

Używam rozwiązania mieszanego (php + css).

Pojemniki są potrzebne do:

  • div.imgCont2 pojemnik potrzebny do obracania;
  • div.imgCont1 kontener potrzebny do zoomOut - width:150% ;
  • div.imgCont kontener potrzebny dla pasków przewijania, gdy obraz jest zoomOut.

.

<?php
    $image_url = 'your image url.jpg';
    $exif = @exif_read_data($image_url,0,true);
    $orientation = @$exif['IFD0']['Orientation'];
?>

<style>
.imgCont{
    width:100%;
    overflow:auto;
}
.imgCont2[data-orientation="8"]{
    transform:rotate(270deg);
    margin:15% 0;
}
.imgCont2[data-orientation="6"]{
    transform:rotate(90deg);
    margin:15% 0;
}
.imgCont2[data-orientation="3"]{
    transform:rotate(180deg);
}
img{
    width:100%;
}
</style>

<div class="imgCont">
  <div class="imgCont1">
    <div class="imgCont2" data-orientation="<?php echo($orientation) ?>">
      <img src="<?php echo($image_url) ?>">
    </div>
  </div>
</div>

Dzięki, to wydaje się być najlepszym rozwiązaniem, mimo wszystko na moje potrzeby. Nie ma potrzeby korzystania z zewnętrznych bibliotek i długich bloków niepotrzebnego kodu. Po prostu określ orientację z exif za pomocą php, wyślij go do widoku i użyj go do uruchomienia klas CSS, które obracają się o odpowiednią liczbę stopni. Ładnie i czysto! Edycja: spowoduje to nadmierne obrócenie obrazów w przeglądarkach, które faktycznie odczytują dane exif i odpowiednio się obrócą. AKA, to naprawi problem na komputerze, ale utworzy nowy na iOS Safari.
cwal

0

Oprócz odpowiedzi @fareed namrouti,

Powinno to być używane, jeśli obraz ma być przeglądany z elementu wejściowego pliku

<input type="file" name="file" id="file-input"><br/>
image after transform: <br/>
<div id="container"></div>

<script>
    document.getElementById('file-input').onchange = function (e) {
        var image = e.target.files[0];
        window.loadImage(image, function (img) {
            if (img.type === "error") {
                console.log("couldn't load image:", img);
            } else {
                window.EXIF.getData(image, function () {
                    console.log("load image done!");
                    var orientation = window.EXIF.getTag(this, "Orientation");
                    var canvas = window.loadImage.scale(img,
                        {orientation: orientation || 0, canvas: true, maxWidth: 200});
                    document.getElementById("container").appendChild(canvas);
                    // or using jquery $("#container").append(canvas);
                });
            }
        });
    };
</script>

0

Napisałem mały skrypt php, który obraca obraz. Pamiętaj, aby zapisać obraz na korzyść po prostu przelicz go przy każdym żądaniu.

<?php

header("Content-type: image/jpeg");
$img = 'IMG URL';

$exif = @exif_read_data($img,0,true);
$orientation = @$exif['IFD0']['Orientation'];
if($orientation == 7 || $orientation == 8) {
    $degrees = 90;
} elseif($orientation == 5 || $orientation == 6) {
    $degrees = 270;
} elseif($orientation == 3 || $orientation == 4) {
    $degrees = 180;
} else {
    $degrees = 0;
}
$rotate = imagerotate(imagecreatefromjpeg($img), $degrees, 0);
imagejpeg($rotate);
imagedestroy($rotate);

?>

Twoje zdrowie

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.