How to draw photo with correct orientation in canvas after capture photo by using input[type='file'] in mobile web browser?

StackOverflow https://stackoverflow.com/questions/19463126

Question

I am making a simple web app in mobile which allow visitor to capture photo by using html5 input[type=file] element. Then I will display it on the web for preview, and then visitor can choose to upload the photo to my server for other purpose(ie: upload to FB)

I find a problem on the orientation of photo when I take photo using my iPhone and hold vertically.The photo is in a correct orientation in tag. However, when I try to draw it into canvas by using drawImage() method, it is drawn 90 degree rotated.

I have tried to take photo in 4 orientations, only one of them can draw a correct image in canvas, others are rotated or even flipped upside down.

Well, I am confused to get the correct orientation to fix this problem... Thanks for helping...

here is my code, mostly copy from MDN

<div class="container">
            <h1>Camera API</h1>

            <section class="main-content">
                <p>A demo of the Camera API, currently implemented in Firefox and Google Chrome on Android. Choose to take a picture with your device's camera and a preview will be shown through createObjectURL or a FileReader object (choosing local files supported too).</p>

                <p>
                    <form method="post" enctype="multipart/form-data" action="index.php">
                        <input type="file" id="take-picture" name="image" accept="image/*">
                        <input type="hidden" name="action" value="submit">
                        <input type="submit" >
                    </form>
                </p>

                <h2>Preview:</h2>
                <div style="width:100%;max-width:320px;">
                    <img src="about:blank" alt="" id="show-picture" width="100%">
                </div>

                <p id="error"></p>
                <canvas id="c" width="640" height="480"></canvas>
            </section>

        </div>


        <script>
            (function () {
                var takePicture = document.querySelector("#take-picture"),
                    showPicture = document.querySelector("#show-picture");

                if (takePicture && showPicture) {
                    // Set events
                    takePicture.onchange = function (event) {
                        showPicture.onload = function(){
                            var canvas = document.querySelector("#c");
                            var ctx = canvas.getContext("2d");
                            ctx.drawImage(showPicture,0,0,showPicture.width,showPicture.height);
                        }
                        // Get a reference to the taken picture or chosen file
                        var files = event.target.files,
                            file;
                        if (files && files.length > 0) {
                            file = files[0];
                            try {
                                // Get window.URL object
                                var URL = window.URL || window.webkitURL;

                                // Create ObjectURL
                                var imgURL = URL.createObjectURL(file);

                                // Set img src to ObjectURL
                                showPicture.src = imgURL;

                                // Revoke ObjectURL
                                URL.revokeObjectURL(imgURL);
                            }
                            catch (e) {
                                try {
                                    // Fallback if createObjectURL is not supported
                                    var fileReader = new FileReader();
                                    fileReader.onload = function (event) {
                                        showPicture.src = event.target.result;

                                    };
                                    fileReader.readAsDataURL(file);
                                }
                                catch (e) {
                                    // Display error message
                                    var error = document.querySelector("#error");
                                    if (error) {
                                        error.innerHTML = "Neither createObjectURL or FileReader are supported";
                                    }
                                }
                            }
                        }
                    };
                }
            })();
        </script>
Was it helpful?

Solution

You'll need to read the exif data and check if exif.Orientation is one of the following:

fileReader.onloadend = function() {

    var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));

    switch(exif.Orientation){

       case 8:
           ctx.rotate(90*Math.PI/180);
           break;
       case 3:
           ctx.rotate(180*Math.PI/180);
           break;
       case 6:
           ctx.rotate(-90*Math.PI/180);
           break;


    }
};

OTHER TIPS

Ben's great answer pointed me in the right direction but as far as I can tell the actual rotations are incorrect (at least they were for me) and don't cover all possible cases. The solution below worked for me. It is based on the one found in the JavaScript-Load-Image library (which I found via this great SO question). Note that I also had to translate the Canvas context to the center as it originates from the top left corner when rotating).

fileReader.onloadend = function() {

    var exif = EXIF.readFromBinaryFile(new BinaryFile(this.result));

    switch(exif.Orientation){

        case 2:
            // horizontal flip
            ctx.translate(canvas.width, 0);
            ctx.scale(-1, 1);
            break;
        case 3:
            // 180° rotate left
            ctx.translate(canvas.width, canvas.height);
            ctx.rotate(Math.PI);
            break;
        case 4:
            // vertical flip
            ctx.translate(0, canvas.height);
            ctx.scale(1, -1);
            break;
        case 5:
            // vertical flip + 90 rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.scale(1, -1);
            break;
        case 6:
            // 90° rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(0, -canvas.height);
            break;
        case 7:
            // horizontal flip + 90 rotate right
            ctx.rotate(0.5 * Math.PI);
            ctx.translate(canvas.width, -canvas.height);
            ctx.scale(-1, 1);
            break;
        case 8:
            // 90° rotate left
            ctx.rotate(-0.5 * Math.PI);
            ctx.translate(-canvas.width, 0);
            break;


    }
};

add exif.js to your project, then:

EXIF.getData(file,function() {
  var orientation = EXIF.getTag(this,"Orientation");
  var can = document.createElement("canvas");
  var ctx = can.getContext('2d');
  var thisImage = new Image;
  thisImage.onload = function() {
    can.width  = thisImage.width;
    can.height = thisImage.height;
    ctx.save();
    var width  = can.width;  var styleWidth  = can.style.width;
    var height = can.height; var styleHeight = can.style.height;
    if (orientation) {
      if (orientation > 4) {
        can.width  = height; can.style.width  = styleHeight;
        can.height = width;  can.style.height = styleWidth;
      }
      switch (orientation) {
      case 2: ctx.translate(width, 0);     ctx.scale(-1,1); break;
      case 3: ctx.translate(width,height); ctx.rotate(Math.PI); break;
      case 4: ctx.translate(0,height);     ctx.scale(1,-1); break;
      case 5: ctx.rotate(0.5 * Math.PI);   ctx.scale(1,-1); break;
      case 6: ctx.rotate(0.5 * Math.PI);   ctx.translate(0,-height); break;
      case 7: ctx.rotate(0.5 * Math.PI);   ctx.translate(width,-height); ctx.scale(-1,1); break;
      case 8: ctx.rotate(-0.5 * Math.PI);  ctx.translate(-width,0); break;
      }
    }

    ctx.drawImage(thisImage,0,0);
    ctx.restore();
    var dataURL = can.toDataURL();

    // at this point you can save the image away to your back-end using 'dataURL'
  }

  // now trigger the onload function by setting the src to your HTML5 file object (called 'file' here)
  thisImage.src = URL.createObjectURL(file);

});

The orientation block (using translate and rotate) is copied from https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js and so I consider it well proven. It certainly worked perfectly for me, whereas other approaches didn't.

If you just want the Orientation tag, using exif.js:

EXIF.getData(file, function () {
    alert(this.exifdata.Orientation);
});

In my tests, iOS camera only returns 1,3,6 or 8.

Based on your answers, I created a function to auto rotate iphone photo to right direction.
Just pass in an input.files[0] and an optional max width or height, it'll output a blob used for form submit.
https://github.com/gonnavis/iphone_photo_rotation_adjust

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top