preface

In the development of picture uploading, we sometimes encounter a normal picture, which will inexplicably rotate after uploading. For example, in mobile terminal development, this situation will occur in the lower version of IOS. When we take a picture and render it to canvas, the canvas rendered picture will be rotated. So what’s going on here? This involves something called Exif.

What is the Exif

Exif (Exchangeable Image file format) is an image file format. It is specially used to record digital photo attribute information and shooting data. Exif information contains a lot of metadata, such as: 1, image related: image ID, resolution, resolution unit, image direction, image width and height 2, camera related: camera manufacturer, camera model, aperture value, focal length, object distance, aperture value, etc. 3, others: Saturation, brightness, sharpness, EXIF version, etc. The obvious one that plays a role in the image rotation we encounter is image orientation.

How to solve

We found that the image direction has several values respectively:

Value orientation Rotation Angle
1 0
2 Turn around
3 180 degrees clockwise
4 Upside down
5 After turning 90 degrees clockwise, flip left to right
6 90 degrees clockwise
7 After 90 degrees counterclockwise, flip left to right
8 Counterclockwise 90 degrees

The problem that causes this phenomenon is that there may be 3, 6, 8 situations, the other several left and right flip situations do not know when will appear (there are friends who know welcome to comment in the comment section). So what are we going to do about it. There are two intuitive responses: 1. Since this value is problematic, let’s just change it. 2. Since it has been rotated, the rotation direction of the image should be corrected according to the orientation value of the image. Let’s investigate each of these options.

Project research

Plan a

Directly modify the orientation value. Here we can use piexifJS, which can get exIF information and modify exIF information. Finally, a base64 image is generated. Here we have made four elements for comparison: 1. Directly display the original image; 2. Render the original image to canvas; 3. The code is as follows:

<html>

<head>
    <title>Modify exIF information directly</title>
</head>

<body>
    <input type="file" accept="image/*" onchange="upload(this)" />
    <div>
        <img src="" id="origin"  width="200"/>
        <p>The original image</p>
        <canvas id="originImage" ></canvas>
        <p>Canvas render original</p>
    </div>
    <div>
        <img src="" id="image"  width="200"/>
        <p>Modify exIF information after the picture</p>
        <canvas id="resultImage" style="width: 200px;"></canvas>
        <p>Canvas renders images that handle exIF information</p>
    </div>
    <script src="./piexif.js"></script>
    <script>
        var imageDom = document.getElementById('image')
        var originDom = document.getElementById('origin')
        function getOri(exif) {
            return exif["0th"][piexif.ImageIFD.Orientation]
        }
        function initCanvas(image, id) {
            var { width, height } = image;
            var canvas = document.getElementById(id)
            var ctx = canvas.getContext("2d");
            canvas.width = width;
            canvas.height = height;
            ctx.drawImage(image, 0.0, width, height);
            return canvas;
        }
        function upload(target) {
            var file = target.files[0];
            var reader = new FileReader();
            reader.onload = function (e) {
                var result = e.target.result
                var originExif = piexif.load(result) // Get exIF information
                const orientation = getOri(originExif) // orientation
                imageDom.onload = function() {
                    initCanvas(imageDom, 'resultImage')}if(orientation && orientation ! =1) {
                    let exif = {};
                    exif[piexif.ImageIFD.Orientation] = 2; / / set the orientation
                    var exifObj = {"0th":exif};
                    var exifbytes = piexif.dump(exifObj);
                    var inserted = piexif.insert(exifbytes, result); // Write a new exif
                    imageDom.src = inserted;
                    var resultExif = piexif.load(inserted);
                    console.log(resultExif, 'result')
                    console.log(originExif, 'origin')}else {
                    imageDom.src = result;
                }
                originDom.onload = function(){
                    var canvas = initCanvas(originDom, 'originImage');
                    var url = canvas.toDataURL("image/jpeg".0.8);;
                    var canvasResultExif = piexif.load(url)
                    console.log(canvasResultExif, 'canvasResult')
                }
                originDom.src = result
            };
            reader.readAsDataURL(file);
        }

    </script>
</body>

</html>
Copy the code

After making various pictures and modifying various exIF information. Here we show the image with “orientation” set to 6 and “orientation” set to 2 in exif, so that our comparison effect is more obvious:

A, Ios 14.0.1



B. Display of ios 12.5 (different orientation will be obtained when shooting in different directions with mobile phone)



A The result gives us the following phenomena:

1. The original picture is displayed normally

2. The original image is also displayed normally when rendered to canvas

3. The picture of modified ExIF information was flipped when the orientation of the original picture was 1, that is, the orientation was changed to 2 (of course, we set 2 here, and the result was in line with our expectations).

4. Rendering to canvas after modifying exIF is the same as modifying exIF information directly

B The results give us the following phenomena:

1. The original picture is displayed normally

2. The orientation of the original image rendered to canvas is 1

3. Change the orientation of the image of exIF information to 2 (because we changed its value to 2)

4. After the modification of exif, rendering to canvas results in orientation of 1

To print the exIF information,It was found that the exif information of the canvas rendered image was lost.

By comparing the results of the two groups 1, 2, 3 and 4,It is not difficult for us to conclude that the rendering of canvas in the lower version of IOS is carried out according to the image orientation of 1.

In older versions the canvas rendering case sets the actual exif value for the image.

It can be seen that the first scheme is not feasible.

Scheme 2

According to the orientation value of the image, the rotation direction of the image is modified. We can use piexifjs or exifjs to read the orientation value and then process it. While PiexifJS and ExifJS have a different API for getting orientation, the way we’re going to process images is irrelevant. But since we used piexifj above, let’s stick with it here. We also compared the groups according to plan 1.

<html>

<head>
    <title>Plan two, rotation</title>
</head>

<body>
    <input type="file" accept="image/*" onchange="upload(this)" />
    <div>
        <img src="" id="origin" width="200" />
        <canvas id="originImage" style="width: 200px;"></canvas>
        <p>The original image</p>
    </div>
    <div>
        <img src="" id="image" width="200" />
        <canvas id="resultImage" style="width: 200px;"></canvas>
        <p>The results of</p>
    </div>
    <script src="./piexif.js"></script>
    <script>
        var imageDom = document.getElementById('image')
        var originDom = document.getElementById('origin')
        function getOri(image) {
            var exif = piexif.load(image);
            return exif["0th"][piexif.ImageIFD.Orientation]
        }
        function initCanvas(image, id) {
            var { width, height } = image;
            var canvas = null
            if(id) {
                canvas = document.getElementById(id)
            } else {
                canvas = document.createElement('canvas')}if(! canvas)return ;
            var ctx = canvas.getContext("2d");
            canvas.width = width;
            canvas.height = height;
            ctx.drawImage(image, 0.0, width, height);
            return {
                canvas,
                ctx
            }
        }
        function drawImageInPage(src, id) {
            var image = new Image;
            image.onload = function(){
                initCanvas(image, id)
            }
            image.src = src;
        }
        function upload(target) {
            var file = target.files[0];
            var reader = new FileReader();
            reader.onload = function (e) {
                var result = e.target.result
                var orientation = getOri(result);
                var image = new Image;
                image.onload = function () {
                    var { canvas, ctx } = initCanvas(image)
                    rotateImage(orientation, canvas, ctx, image)
                    const dataurl = canvas.toDataURL("image/jpeg".0.8);
                    imageDom.src = dataurl
                    drawImageInPage(dataurl, 'resultImage')
                }

                image.src = result;
                originDom.src = result
                drawImageInPage(result, 'originImage')}; reader.readAsDataURL(file); }const Ori = Object.freeze({
            zero: 1.clockwise180: 3.clockwise90: 6.antiClockwise90: 8
        })
        // Rotate the image
        function rotateImage(orientation, canvas, ctx, image) {
            if(orientation && orientation ! = Ori.zero) {switch (orientation) {
                    case Ori.clockwise90:
                        canvas.width = image.height;
                        canvas.height = image.width;
                        ctx.rotate(Math.PI / 2);
                        ctx.drawImage(image, 0, -image.height, image.width, image.height);
                        break;
                    case Ori.clockwise180:
                        ctx.rotate(Math.PI);
                        ctx.drawImage(image, -image.width, -image.height, image.width, image.height);
                        break;
                    case Ori.antiClockwise90:
                        canvas.width = image.height;
                        canvas.height = image.width;
                        ctx.rotate((3 * Math.PI) / 2);
                        ctx.drawImage(image, -image.width, 0, image.width, image.height);
                        break;
                }
                ctx.drawImage(image, 0.0); }}</script>
</body>

</html>
Copy the code

Display result:

A, Ios 14.0.1



B. Ios 12.5



A The result gives us the following phenomena:

1. The original picture is displayed normally

2. The original image is also displayed normally when rendered to canvas

3. Since the orientation is 6, the picture is logically rotated, that is, rotated 90 degrees at the Angle of orientation is 6

Theta 4 is generated in the same direction as theta 3

B The results give us the following phenomena:

1. The original picture is displayed normally

2. The orientation of the original image rendered to canvas is 1

3. The same image is displayed, that is, the rotation is 90 degrees under the orientation of 1

4, display the same as 3, because the exif information has been lost, so the result of rendering according to 3 must be the same as 3

The results of scheme 1 were verified again by comparison of results 1, 2, 3 and 4 of the two groups. But there was a new problem, and we found that on older versions of ios the rotation was wrong.

At this point, we can set a hidden picture to determine whether the picture has been rotated, that is, whether the 2 reference group is the same as the 1 to determine whether the canvas draws the picture as expected. To determine whether the display diagram can be rotated in this environment.

conclusion

1. In IOS of a lower version, canvas rendering is performed according to the orientation of the image as 1. In older versions, canvas renders the actual exIF value of the image. 2. Picture exif information is lost after canvas renders the picture. 3. In high version, you can decide whether to rotate the display picture by judging whether the preset picture has been rotated. This time we have a preliminary understanding of how to deal with IOS photo problems by obtaining exIF information. Next time, we will learn the implementation principle of ExIFJS and explore the method of obtaining exIF information.