Bootstrap

Java 纠正上传图片自动旋转与镜像的问题

Java 纠正上传图片自动旋转与镜像的问题

遇到一个图片看着是方向是正的,但是用特定的工具打开后自动旋转与镜像。

查看这篇文章后:https://www.cnblogs.com/csonezp/p/5564809.html
原文中的一句话:原来是因为相机给图片的exif信息加上了一个Orientation,然后图片浏览器会对这个属性做出兼容,让图片以竖图的形式显示出来。下面我来对Orientation这个属性做一些解释。
为什么我们在一些软件上,或者浏览器中看到是正的,是因为这些软件,浏览器自动的纠正了这个图片。
然而我们的Java并没有自动的为我们去纠正这个图片当我们使用BufferImage去绘制图片的时候发现高与居然是相反的。

如何解决?

引入

<dependency>
    <groupId>com.drewnoakes</groupId>
    <artifactId>metadata-extractor</artifactId>
    <version>2.7.0</version>
</dependency>

这个包可以帮我去获取图片的exif信息。

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifIFD0Directory;

// 1.获取图片的元数据
Metadata metadata = ImageMetadataReader.readMetadata(file);
// 2.图片元数据处理那种方向
int orientation = 0;

for (Directory directory : metadata.getDirectories()) {
    // 3.只需要TAG_ORIENTATION的数据即可。
    if(directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)){
        for (Tag tag : directory.getTags()) {
            System.out.println(tag);
        }
        orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
    }
}

// 输出[Exif IFD0] Orientation - Right side, top (Rotate 90 CW)

理解

EXIF Orientation ValueRow #0 is:Column #0 is:对应的方向应该如何纠正
1TopLeft side无需纠正
2TopRight side水平翻转(镜像)
3BottomRight side垂直翻转(旋转180°)
4BottomLeft side水平翻转+垂直翻转
5Left sideTop水平翻转+旋转90°
6Right sideTop旋转90°
7Right sideBottom水平翻转+旋转270°
8Left sideBottom+旋转270°

可以参考下图一起理解

实现

import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifIFD0Directory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;

public class ImageRotate {

    /**
     * 纠正图片方法
     *
     * @param srcImgPath
     */
    /**
     * 纠正图片旋转
     *
     * @param srcImgPath
     */
    public static void correctImg(String srcImgPath) {
        FileOutputStream fos = null;
        try {
            // 原始图片
            File srcFile = new File(srcImgPath);
            // 1.获取纠正
            RectifyDirection rotateAngle = getRectifyDirection(srcFile);
            // 2.无需纠正直接返回
            if(rotateAngle == null){
                return;
            }
            System.out.println(rotateAngle.angel+":"+rotateAngle.isMirror);
            
            // 原始图片缓存
            BufferedImage srcImg = ImageIO.read(srcFile);

            // 原始宽度
            int imgWidth = srcImg.getWidth();
            // 原始高度
            int imgHeight = srcImg.getHeight();

            // 3.如果不是垂直的就代表他们的宽高需要互换
            if (rotateAngle.angel != 180) {
                int temp = imgWidth;
                imgWidth = imgHeight;
                imgHeight = temp;
            }

            // 中心点位置
            double centerWidth = ((double) imgWidth) / 2;
            double centerHeight = ((double) imgHeight) / 2;

            // 图片缓存
            BufferedImage targetImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);

            // 旋转对应角度
            Graphics2D g = targetImg.createGraphics();
            // 4.以中心点位置 向左旋转
            g.rotate(Math.toRadians(rotateAngle.angel), centerWidth, centerHeight);
            // 5.旋转后图片的宽高就又替换了。
            System.out.println(srcImg.getWidth());
            System.out.println(srcImg.getHeight());
            // 6.这里就需要找到替换后原有的宽高与现有的宽高,从这个坐标开始绘制图片
            int x = (imgWidth - srcImg.getWidth()) / 2;
            int y = (imgHeight - srcImg.getHeight()) / 2;
            g.drawImage(srcImg, x, y, null);
            g.dispose();
            // 7.是否需要水平翻转
            if (rotateAngle.isMirror){
                BufferedImage tempImage = new BufferedImage(targetImg.getWidth(), targetImg.getHeight(), BufferedImage.TYPE_INT_RGB);
                g = tempImage.createGraphics();
                // 使用 AffineTransform 进行水平翻转
                AffineTransform transform = new AffineTransform();
                // 8.平移现在图片的宽度后
                transform.translate(targetImg.getWidth() , 0);
                // 9. 缩放
                // 因为横坐标缩放是-1,所以是水平翻转
                // 翻转过程见水平翻转图片
                transform.scale(-1, 1);
                g.setTransform(transform);

                g.drawImage(targetImg,0, 0, null);
                g.dispose();
                targetImg = tempImage;
            }

            // 输出图片
            fos = new FileOutputStream(new File(srcFile.getAbsolutePath() + "." + srcImgPath.substring(srcImgPath.lastIndexOf(".") + 1)));
            ImageIO.write(targetImg, srcImgPath.substring(srcImgPath.lastIndexOf(".") + 1), fos);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.flush();
                    fos.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取纠正方向
     * @param file
     * @return
     * @throws Exception
     */
    public static RectifyDirection getRectifyDirection(File file) throws Exception {
        // 1.获取图片的元数据
        Metadata metadata = ImageMetadataReader.readMetadata(file);
        // 2.图片元数据处理那种方向
        int orientation = 0;
        
        for (Directory directory : metadata.getDirectories()) {
            // 3.只需要TAG_ORIENTATION的数据即可。
            if(directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)){
            	// 如果想看下其他内容,取消注释即可
                // for (Tag tag : directory.getTags()) {
                //     System.out.println(tag);
                // }
                orientation = directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
            }
        }
        // 4.根据不同的orientation创建不同的处理方式
        switch (orientation){
            case 2:
                return new RectifyDirection(0,true);
            case 3:
                return new RectifyDirection(180,false);
            case 4:
                return new RectifyDirection(180, true);
            case 5:
                return new RectifyDirection(90,true);
            case 6:
                return new RectifyDirection(90,false);
            case 7:
                return new RectifyDirection(270,true);
            case 8:
                return new RectifyDirection(270,false);
            default:
                return null;
        }
    }
	
    static class RectifyDirection{
        /**
         * 角度
         */
        public int angel;
        /**
         * 是否镜像
         */
        public boolean isMirror;

        public RectifyDirection(int angel, boolean isMirror) {
            this.angel = angel;
            this.isMirror = isMirror;
        }
    }
}
旋转后移动坐标

水平翻转

;