web-dev-qa-db-fra.com

Orientation Exif côté client JS: Rotation et miroir des images JPEG

Les photos d'appareils photo numériques sont souvent enregistrées au format JPEG avec une balise EXIF ​​"d'orientation". Pour que l'affichage soit correct, les images doivent être pivotées/inversées en fonction de l'orientation définie, mais les navigateurs ignorent ces informations lors du rendu de l'image. Même dans les grandes applications Web commerciales, la prise en charge de l'orientation EXIF ​​peut être inégale 1 . La même source fournit également un bon résumé des 8 orientations différentes qu'un JPEG peut avoir:

Summary of EXIF Orientations

Des exemples d'images sont disponibles à l'adresse 4

La question est de savoir comment faire pivoter/refléter l'image côté client afin qu'elle s'affiche correctement et puisse être traitée ultérieurement si nécessaire.

Des bibliothèques JS sont disponibles pour analyser les données EXIF, notamment l'attribut d'orientation 2 . Flickr a noté un problème de performance possible lors de l'analyse d'images volumineuses, nécessitant l'utilisation de travailleurs Web 3 .

Les outils de la console peuvent correctement réorienter les images 5 . Un script PHP permettant de résoudre le problème est disponible à l'adresse 6

109
flexponsive

Le projet github JavaScript-Load-Image fournit une solution complète au problème d’orientation EXIF, en effectuant correctement la rotation/symétrie des images pour les 8 orientations exif. Voir la démo en ligne de javascript exif orientation

L'image est dessinée sur une toile HTML5. Son rendu correct est implémenté dans js/load-image-orientation.js via des opérations de canevas.

J'espère que cela fera gagner un peu de temps à quelqu'un d'autre et qu'il renseignera les moteurs de recherche sur ce bijou open source :)

116
flexponsive

La transformation du contexte de Mederr fonctionne parfaitement. Si vous avez besoin d'extraire l'orientation uniquement, utilisez this function - vous n'avez pas besoin de bibliothèques à lecture EXIF. Vous trouverez ci-dessous une fonction permettant de redéfinir l’orientation dans une image base64 . Voici un violon pour elle . J'ai également préparé un violon avec une démonstration d'extraction d'orientation .

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

ok en plus de @ user3096626 answer, je pense qu'il sera plus utile si quelqu'un a fourni un exemple de code, l'exemple suivant vous montrera comment corriger l'orientation de l'image à partir de l'URL (images distantes):


Solution 1: utilisation de javascript (recommandé) 

  1. comme la bibliothèque load-image ne permet pas d'extraire les tags exif à partir d'images url (fichier/blob), nous utiliserons à la fois exif-js et load-image bibliothèques javascript, ajoutez d’abord ces bibliothèques à votre page comme suit:

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

    Note la version 2.2 d’exif-js semble avoir des problèmes, nous avons donc utilisé la version 2.1

  2. alors fondamentalement ce que nous ferons est

    a - chargez l'image en utilisant window.loadImage()

    b - lit les tags exif en utilisant window.EXIF.getData()

    c - convertissez l'image en canevas et corrigez l'orientation de l'image à l'aide de window.loadImage.scale()

    d - place la toile dans le document 

voici :)

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

    });
  }
});

bien sûr, vous pouvez aussi obtenir l'image en base64 à partir de l'objet canvas et la placer dans l'attribut img src, vous pouvez donc utiliser jQuery;) 

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

voici le code complet sur: github: https://github.com/digital-flowers/loadimage-exif-example


Solution 2: utilisation de HTML (piratage du navigateur) 

il y a un hack très rapide et facile, la plupart des navigateurs affichent l'image dans l'orientation correcte si l'image est ouverte dans un nouvel onglet directement sans html (LOL, je ne sais pas pourquoi), vous pouvez donc afficher votre image en utilisant iframe en mettant l'attribut iframe src directement dans l'URL de l'image:

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

Solution 3: utilisation de css (seulement firefox et safari sur ios) 

il y a un attribut css3 pour corriger l'orientation de l'image mais le problème c'est qu'il ne fonctionne que sur firefox et safari/ios, cela vaut encore la peine d'être mentionné car bientôt il sera disponible pour tous les navigateurs (Informations de support du navigateur de caniuse)

img {
   image-orientation: from-image;
}
21
Fareed Alnamrouti

La réponse de WunderBart était la meilleure pour moi. Notez que vous pouvez accélérer beaucoup si vos images sont souvent le bon sens, simplement en testant d'abord l'orientation et en contournant le reste du code si aucune rotation n'est requise.

Rassembler toutes les informations de wunderbart, quelque chose comme ceci;

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

Pour ceux qui ont un fichier d'un contrôle d'entrée, ne savent pas quelle est son orientation, sont un peu fainéants et ne veulent pas inclure une grande bibliothèque. Le code fourni par @WunderBart est associé à la réponse à laquelle il renvoie ( https://stackoverflow.com/a/32490603 ) qui trouve l'orientation.

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

qui peut facilement s'appeler comme tel

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

En plus de la réponse de @fareed namrouti,

Ceci devrait être utilisé si l'image doit être parcourue à partir d'un élément input de fichier

<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
Perry

J'utilise une solution mixte (php + css).

Les conteneurs sont nécessaires pour:

  • div.imgCont2 conteneur nécessaire pour la rotation;
  • div.imgCont1 conteneur nécessaire pour zoomOut - width:150%;
  • div.imgCont conteneur nécessaire pour les barres de défilement, lorsque l'image est 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>
0
Sergey S

J'ai écrit un petit script php qui fait pivoter l'image. Assurez-vous de stocker l'image au lieu de la recalculer à chaque demande.

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

?>

À votre santé

0
Thom