JavaでEXIFを取得し、画像を正しい向きに回転させる


Posted on 2021/5/6 at 1:24


一般的にデジカメやiPhoneで撮影した写真をPCで見るときには、自動で画像の向きを読み取って正しい向きで表示されます。

しかし、先日iPhoneで撮影した写真をサーバーにアップロードした際、画像の向きが意図しない方向を向いてしまいその後の処理に困ってしまいました。

原因

私の使用しているiPhone7では横向き(ホームボタンを右にした)状態で撮影すると症状がでないようですが、できればそんなの気にしないで撮りたいと思い調べてみました。

Jpeg画像の中にはEXIFという写真の向きや撮影日などの情報が入っているとのこと。問題となっている写真の向きに関する情報はOrientationという属性に入っている模様です。 調べてみると以下のような結果となりました。

普通(ホームボタンを下にした)状態: Orientation = 6

横向き(ホームボタンを左にした)状態: Orientation = 3

横向き(ホームボタンを右にした)状態: Orientation = 1

対策

Apache Commons Imagingというライブラリを使用してJPEG画像のEXIFデータを読み取って(書き込みも可)、正しい向きに修正した状態で保存することにしました。

Apache Commons Imagingのダウンロード

Apache Commons Imagingのダウンロードページの「Binaries」から 「commons-imaging-1.0-alpha2-bin.zip」をダウンロード、解凍し、使用しているIDEのプロジェクトにcommons-imaging-1.0-alpha2.jarを追加します。

サンプルコード

jpgディレクトリ内のin.jpgファイルを読み込んで、回転後のBufferedImageを取得して、同ディレクトリ内にout.jpgとして出力します。

  
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.Imaging;
import org.apache.commons.imaging.common.ImageMetadata;
import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata;
import org.apache.commons.imaging.formats.tiff.TiffField;
import org.apache.commons.imaging.formats.tiff.TiffImageMetadata;
import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;

public class ImageRotator {

	public static void main(String[] args) {
		ImageRotator rotation = new ImageRotator();
		try {
			File jpg = new File("jpg/in.jpg");
			BufferedImage destImage = rotation.getRotateImage(jpg);
			ImageIO.write(destImage, "jpg", new File("jpg/out.jpg"));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public BufferedImage getRotateImage(File jpg) throws Exception {
		int orientation = readExifOrientation(jpg);
		AffineTransform t = new AffineTransform();
		BufferedImage image = ImageIO.read(jpg);
		int destWidth = image.getWidth();
		int destHeight = image.getHeight();
		BufferedImage destImage;
		switch (orientation) {
		case 1: // 無回転
			return image;
	    case 2: // 左右反転
	        t.scale(-1.0, 1.0);
	        t.translate(-image.getWidth(), 0);
	        break;
	    case 3: // 180度回転
	        t.translate(image.getWidth(), image.getHeight());
	        t.rotate(Math.PI);
	        break;
	    case 4: // 上下反転
	        t.scale(1.0, -1.0);
	        t.translate(0, -image.getHeight());
	        break;
	    case 5: // 90度+左右反転
	        t.rotate(-Math.PI / 2);
	        t.scale(-1.0, 1.0);
	        destWidth = image.getHeight();
	        destHeight = image.getWidth();
	        break;
	    case 6: // 90度回転
	        t.translate(image.getHeight(), 0);
	        t.rotate(Math.PI / 2);
	        destWidth = image.getHeight();
	        destHeight = image.getWidth();
	        break;
	    case 7: // 270度+左右反転
	        t.scale(-1.0, 1.0);
	        t.translate(-image.getHeight(), 0);
	        t.translate(0, image.getWidth());
	        t.rotate(  3 * Math.PI / 2);
	        destWidth = image.getHeight();
	        destHeight = image.getWidth();
	        break;
	    case 8: // 270度回転
	        t.translate(0, image.getWidth());
	        t.rotate(  3 * Math.PI / 2);
	        destWidth = image.getHeight();
	        destHeight = image.getWidth();
	        break;
		}
		AffineTransformOp op = new AffineTransformOp(t, AffineTransformOp.TYPE_BICUBIC);
		destImage = new BufferedImage(destWidth, destHeight, image.getType());
		op.filter(image, destImage);
		return destImage;
	}
	
    private int readExifOrientation(File jpg) throws Exception {
        TiffImageMetadata exif;
        exif = readExifMetadata(jpg);
        if (exif == null)
            return 1;
        TiffField field = exif.findField(TiffTagConstants.TIFF_TAG_ORIENTATION);
        if (field == null)
            return 1;
        else
            return field.getIntValue();
    }
	
    private TiffImageMetadata readExifMetadata(File jpg) throws ImageReadException, IOException {
        ImageMetadata imageMetadata = Imaging.getMetadata(jpg);
        if (imageMetadata == null)
            return null;
        JpegImageMetadata jpegMetadata = (JpegImageMetadata)imageMetadata;
        TiffImageMetadata exif = jpegMetadata.getExif();
        if (exif == null)
            return null;
        return exif;
     }
	
}
  

プログラムの実行結果

元画像

元画像です。iPhoneを普通(ホームボタンを下にした)状態にして撮影した画像ですが、EXIFのOrientation属性の値が6のため、90度回転してしまっています。

出力画像

出力後の画像です。無事正しい向きになってくれました。


関連記事

JavaでEXIF情報を取得するサンプルプログラム

デジカメやiPhoneで撮影した写真には、Exif情報という撮影日時や撮影に使用した設定内容が自動で付与されています。 今回はApache Commons Imagingというライブラリを使用してJPEG画像内のExif情報を読み込んでみます。

関連記事

Exif情報の中の位置情報を削除する方法

Windows標準機能により削除する方法と、プログラミングにより削除する方法を紹介します。