Java讀取圖片exif資訊實現圖片方向自動糾正

起因

一個對試卷進行OCR識別需求,需要實現一個功能,一個章節下的題目圖片需要上下拼接合成一張大圖,起初寫了一個工具實現圖片的合併,程式一直很穩定的運行著,有一回饋合成的圖片方向不對,起初懷疑是本身圖片方向有問題,但是用windows圖片查看器打開圖片方向是正常「顯示」的

定位

exif資訊

查閱相關資料,圖片資訊中有個exif標準,exif資訊如下:

圖蟲exif資訊查看器://exif.tuchong.com/
關注IFD0節點方向,Rotate 270 CW,意思圖片需要順時針旋轉270°方向正常,windows默認的圖片查看器已經幫我們自動旋轉展示了,我們在手機橫排或者掃描儀、數位相機輸出的圖片通常包含此類資訊,但是我們java讀取的是圖片的真實方向,所以在生成圖片方向自然也就不對了

程式碼

添加依賴

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

自旋轉程式碼

旋轉圖片

private static void rotateImage(List<String> stringList) {
    stringList.forEach(s -> {
        File file = new File(s);
        try {
            Metadata metadata = ImageMetadataReader.readMetadata(file);
            StringBuilder description = new StringBuilder();
            metadata.getDirectories().forEach(directory -> {
                directory.getTags().forEach(tag -> {
                    if (tag.getTagType() == ExifDirectoryBase.TAG_ORIENTATION) {
                        description.append(tag.getDescription().replaceAll(" ", ""));
                    }
                });
            });
            if (description.length() > 0) {
                int rotateIndex = description.indexOf("Rotate");
                int cwIndex = description.indexOf("CW");
                if (rotateIndex >= 0 && cwIndex > 0 && rotateIndex < cwIndex) {
                    int angel = Integer.valueOf(description.substring(rotateIndex + 6, cwIndex));
                    log.info("============圖片方向糾正,順時針旋轉{}°,圖片路徑:{}===========", angel, s);
                    BufferedImage oldImage = ImageIO.read(file);
                    BufferedImage newImage = RotateImage.Rotate(oldImage, angel);
                    ImageIO.write(newImage, "jpg", file);
                    newImage.getGraphics().dispose();
                    oldImage.getGraphics().dispose();
                }
            }
        } catch (ImageProcessingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    });
}

圖片旋轉工具類

import java.awt.*;
import java.awt.image.BufferedImage;

public class RotateImage {
    /**
     * 對圖片進行旋轉
     *
     * @param src   被旋轉圖片
     * @param angel 旋轉角度
     * @return 旋轉後的圖片
     */
    public static BufferedImage Rotate(Image src, int angel) {
        int srcWidth = src.getWidth(null);
        int srcHeight = src.getHeight(null);
        // 計算旋轉後圖片的尺寸
        Rectangle rect_des = CalcRotatedSize(new Rectangle(new Dimension(
            srcWidth, srcHeight)), angel);
        BufferedImage res = null;
        res = new BufferedImage(rect_des.width, rect_des.height,
            BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = res.createGraphics();
        // 進行轉換
        g2.translate((rect_des.width - srcWidth) / 2,
            (rect_des.height - srcHeight) / 2);
        g2.rotate(Math.toRadians(angel), srcWidth / 2, srcHeight / 2);

        g2.drawImage(src, null, null);
        return res;
    }

    /**
     * 計算旋轉後的圖片
     *
     * @param src   被旋轉的圖片
     * @param angel 旋轉角度
     * @return 旋轉後的圖片
     */
    public static Rectangle CalcRotatedSize(Rectangle src, int angel) {
        // 如果旋轉的角度大於90度做相應的轉換
        if (angel >= 90) {
            if (angel / 90 % 2 == 1) {
                int temp = src.height;
                src.height = src.width;
                src.width = temp;
            }
            angel = angel % 90;
        }

        double r = Math.sqrt(src.height * src.height + src.width * src.width) / 2;
        double len = 2 * Math.sin(Math.toRadians(angel) / 2) * r;
        double angelAlpha = (Math.PI - Math.toRadians(angel)) / 2;
        double angelDeltaWidth = Math.atan((double) src.height / src.width);
        double angelDeltaHeight = Math.atan((double) src.width / src.height);

        int lenDeltaWidth = (int) (len * Math.cos(Math.PI - angelAlpha
            - angelDeltaWidth));
        int lenDeltaHeight = (int) (len * Math.cos(Math.PI - angelAlpha
            - angelDeltaHeight));
        int desWidth = src.width + lenDeltaWidth * 2;
        int desHeight = src.height + lenDeltaHeight * 2;
        return new Rectangle(new Dimension(desWidth, desHeight));
    }
}

測試程式碼

public static void main(String[] args) throws ImageProcessingException, IOException {
    File file = new File("D:\\temp\\error_direction");
    List<String> stringList = Arrays.asList(file.listFiles()).stream().map(File::getAbsolutePath).collect(Collectors.toList());
    rotateImage(stringList);
}

控制台日誌如下:

18:52:40.066 [main] INFO ============圖片方向糾正,順時針旋轉270°,圖片路徑:D:\temp\error_direction\185.jpg===========
18:52:40.879 [main] INFO ============圖片方向糾正,順時針旋轉90°,圖片路徑:D:\temp\error_direction\186.jpg===========

至此,我們原始圖片的方向被糾正了,我們再來看下糾正之後的圖片的exif資訊,發現旋轉資訊已經沒有了