问题:手机照片上传时,会发现ios手机上传竖拍照片会逆时针旋转90度,横拍照片无此问题;而Android不会出现这种现象。
原因:ios系统默认Orientation属性为1,与常规机器拍摄图片的Orientation属性不一致。
方案:读取文件Exif信息,获取Orientation属性,修改Orientation属性为0。
Orientation属性属性说明如下:
旋转角度 | 参数 |
0° | 1 |
顺时针90° | 6 |
逆时针90° | 8 |
180° | 3 |
读取Exif信息:
(依赖:metadata-extractor-2.10.1.jar,xmpcore-5.1.2.jar)
package com.minivision.gmepartner.test;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.Tag;
import java.io.File;
import java.io.IOException;
/**
* Description: <br/>
* <p>
* Author:liuyinfei
* ===============================
* Created with IntelliJ IDEA.
* Date: 2019/1/24
* ================================
*/
public class RotateAngleUtil {
/**
* 图片数据元提取
*/
public static void metadataExtractor(){
File jpegFile = new File("C:\\Users\\80613\\Desktop\\svn\\download-wps图片.jpg");
try {
Metadata metadata = ImageMetadataReader.readMetadata(jpegFile);
for (Directory directory : metadata.getDirectories()) {
for (Tag tag : directory.getTags()) {
System.out.println(tag);
}
}
} catch (ImageProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
metadataExtractor();
}
}
package img;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.media.jai.PlanarImage;
import com.alibaba.simpleimage.ImageFormat;
import com.alibaba.simpleimage.ImageWrapper;
import com.alibaba.simpleimage.SimpleImageException;
import com.alibaba.simpleimage.render.CropParameter;
import com.alibaba.simpleimage.render.WriteParameter;
import com.alibaba.simpleimage.util.ImageCropHelper;
import com.alibaba.simpleimage.util.ImageWriteHelper;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifImageDirectory;
/**
* 测试 获取图片旋转信息并处理
* 图片旋转和剪切
* @date 2018年1月30日 下午6:48:30
*/
public class CatchEXIF2 {
public static void main(String[] args) throws Exception {
// 输入输出文件路径/文件
String src = "D:\\test\\微信图片_20180130193655.jpg";
String dest = "D:\\test\\teswwwwt.jpg";
File srcFile = new File(src);
File destFile = new File(dest);
// 将输入文件转换为字节数组
byte[] bytes = getByte(srcFile);
// 构造输入输出字节流
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
/*处理图片*/
// 判断是否旋转并纠正
BufferedImage bufferedImage = getExifAndTurn(bytes);
// 缩放和剪切
zoomAndCut(bufferedImage, os, 400, 400);
// 将字节输出流写到输出文件路径下
writeFile(os, destFile);
os.close();
is.close();
}
/**
* 获取图片信息,并判断是否要做旋转操作
* (ios图片可能会发生旋转现象)
* @param is
*/
public static BufferedImage getExifAndTurn(byte[] bytes){
ByteArrayInputStream is = null;
ByteArrayInputStream its = null;
BufferedImage result = null;
try {
is = new ByteArrayInputStream(bytes);
Metadata metadata = ImageMetadataReader.readMetadata(is);
Iterable<Directory> directories = metadata.getDirectories();
Iterator<Directory> iterator = directories.iterator();
int orientation = 0;
int angel = 360;
while (iterator.hasNext()) {
Directory exif = iterator.next();
// 获取图片旋转信息
if (exif.containsTag(ExifImageDirectory.TAG_ORIENTATION)) {
String description = exif.getDescription(ExifImageDirectory.TAG_ORIENTATION);
orientation = getOrientation(description);
}
}
if(orientation == 0 || orientation == 1) {
angel = 360;
}
else if(orientation == 3) {
angel = 180;
}
else if(orientation == 6) {
angel = 90;
}
else if(orientation == 8) {
angel = 270;
}
its = new ByteArrayInputStream(bytes);
result = ImageIO.read(its);
// 旋转
if(angel != 360){
result = turn(result, angel);
}
} catch (ImageProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
its.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 获取图片旋转标志
* @param description 图片旋转描述信息
* @return
*/
public static int getOrientation(String description) {
int orientation = 0;
if ("Top, left side (Horizontal / normal)".equalsIgnoreCase(description)) {
orientation = 1;
} else if ("Top, right side (Mirror horizontal)".equalsIgnoreCase(description)) {
orientation = 2;
} else if ("Bottom, right side (Rotate 180)".equalsIgnoreCase(description)) {
orientation = 3;
} else if ("Bottom, left side (Mirror vertical)".equalsIgnoreCase(description)) {
orientation = 4;
} else if ("Left side, top (Mirror horizontal and rotate 270 CW)".equalsIgnoreCase(description)) {
orientation = 5;
} else if ("Right side, top (Rotate 90 CW)".equalsIgnoreCase(description)) {
orientation = 6;
} else if ("Right side, bottom (Mirror horizontal and rotate 90 CW)".equalsIgnoreCase(description)) {
orientation = 7;
} else if ("Left side, bottom (Rotate 270 CW)".equalsIgnoreCase(description)) {
orientation = 8;
}
return orientation;
}
/**
* 图片旋转
* @param degree 旋转角度
*/
public static BufferedImage turn(BufferedImage bi, int degree) {
BufferedImage result = null;
try {
int swidth = 0; // 旋转后的宽度
int sheight = 0; // 旋转后的高度
int x; // 原点横坐标
int y; // 原点纵坐标
// 处理角度--确定旋转弧度
degree = degree % 360;
if (degree < 0) {
degree = 360 + degree;// 将角度转换到0-360度之间
}
double theta = Math.toRadians(degree);// 将角度转为弧度
// 确定旋转后的宽和高
if (degree == 180 || degree == 0 || degree == 360) {
swidth = bi.getWidth();
sheight = bi.getHeight();
} else if (degree == 90 || degree == 270) {
sheight = bi.getWidth();
swidth = bi.getHeight();
} else {
swidth = (int) (Math.sqrt(bi.getWidth() * bi.getWidth() + bi.getHeight() * bi.getHeight()));
sheight = (int) (Math.sqrt(bi.getWidth() * bi.getWidth() + bi.getHeight() * bi.getHeight()));
}
// 确定原点坐标
x = (swidth / 2) - (bi.getWidth() / 2);
y = (sheight / 2) - (bi.getHeight() / 2);
result = new BufferedImage(swidth, sheight, bi.getType());
// 设置图片背景颜色
Graphics2D gs = (Graphics2D) result.getGraphics();
gs.setColor(Color.white);
gs.fillRect(0, 0, swidth, sheight);// 以给定颜色绘制旋转后图片的背景
AffineTransform at = new AffineTransform();
at.rotate(theta, swidth / 2, sheight / 2);// 旋转图象
at.translate(x, y);
AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_BICUBIC);
result = op.filter(bi, result);
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 将file文件转为字节数组
* @param file
*/
public static byte[] getByte(File file){
byte[] bytes = null;
try {
FileInputStream fis = new FileInputStream(file);
bytes = new byte[fis.available()];
fis.read(bytes);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
/**
* 将字节流写到指定文件
* @param os
* @param file
*/
public static void writeFile(ByteArrayOutputStream os, File file){
FileOutputStream fos = null;
try {
byte[] bytes = os.toByteArray();
if (file.exists()) {
file.delete();
}
fos = new FileOutputStream(file);
fos.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 先等比例缩放,小边缩放至指定长度后, 大边直接裁剪指指定长度
* @param bufferedImage
* @param os
* @param width
* @param height
*/
public static void zoomAndCut(BufferedImage bufferedImage, OutputStream os, Integer width, Integer height) {
try {
int w = bufferedImage.getWidth();
int h = bufferedImage.getHeight();
// 获取缩放比例
double wRatio = 1.0 * width / w;
double hRatio = 1.0 * height / h;
double ratio = Math.max(wRatio, hRatio);
// 缩放
AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
bufferedImage = ato.filter(bufferedImage, null);
// 对象转换
ImageWrapper imageWrapper = new ImageWrapper(bufferedImage);
// 获得裁剪偏移量
int w2 = imageWrapper.getWidth();
int h2 = imageWrapper.getHeight();
float x = (w2 - width) / 2.0f;
float y = (h2 - height) / 2.0f;
// 裁剪参数 如果图片宽和高都小于目标图片则处理
CropParameter cropParameter = new CropParameter(x, y, width, height);
if (x < 0 && y < 0) {
cropParameter = new CropParameter(0, 0, width, height);
}
PlanarImage crop = ImageCropHelper.crop(imageWrapper.getAsPlanarImage(), cropParameter);
imageWrapper = new ImageWrapper(crop);
// 写文件
ImageWriteHelper.write(imageWrapper, os, ImageFormat.getImageFormat("jpg"), new WriteParameter());
os.close();
} catch (IOException e) {
e.printStackTrace();
} catch (SimpleImageException e) {
e.printStackTrace();
}
}
}