今天上传几张照片到人人相册,发现可以看到我相机的信息,当然很多图片软件都能看到,所以搜索了下Java如何操作,以后备用。
原文地址:http://yijianfengvip.blog.163.com/blog/static/1752734322010103084230677
首先介绍一下什么是EXIF,EXIF是 Exchangeable Image File的缩写,这是一种专门为数码相机照片设定的格式。这种格式可以用来记录数字照片的属性信息,例如相机的品牌及型号、相片的拍摄时间、拍摄时所设置的光圈大小、快门速度、ISO等等信息。除此之外它还能够记录拍摄数据,以及照片格式化方式,这样就可以输出到兼容EXIF格式的外设上,例如照片打印机等。
目前最常见的支持EXIF信息的图片格式是JPG,很多的图像工具都可以直接显示图片的EXIF信息,包括现在的一些著名的相册网站也提供页面用于显示照片的EXIF信息。本文主要介绍Java语言如何读取图像的EXIF信息,包括如何根据EXIF信息对图像进行调整以适合用户浏览。
目前最简单易用的EXIF信息处理的Java包是Drew Noakes写的metadata-extractor,该项目最新的版本是2.3.0,支持EXIF 2.2版本。你可以直接从http://www.drewnoakes.com/code/exif/ 下载该项目的最新版本包括其源码。
需要注意的是,并不是每个JPG图像文件都包含有EXIF信息,你可以在Windows资源管理器单击选中图片后,如果该图片包含EXIF信息,则在窗口状态栏会显示出相机的型号,如下图所示:
拍摄设备的型号便是EXIF信息中的其中一个。下面我们给出一段代码将这个图片的所有的EXIF信息全部打印出来。
package com.liusoft.dlog4j.test; import java.io.File; import java.util.Iterator; import com.drew.imaging.jpeg.JpegMetadataReader; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; import com.drew.metadata.Tag; import com.drew.metadata.exif.ExifDirectory; /** * 测试用于读取图片的EXIF信息 * @author Winter Lau */ public class ExifTester { public static void main(String[] args) throws Exception { File jpegFile = new File("D:\\我的文档\\我的相册\\DSCF1749.JPG"); Metadata metadata = JpegMetadataReader.readMetadata(jpegFile); Directory exif = metadata.getDirectory(ExifDirectory.class); Iterator tags = exif.getTagIterator(); while (tags.hasNext()) { Tag tag = (Tag)tags.next(); System.out.println(tag); } } }
把metadata-extractor-2.3.0.jar文件加入到类路径中编译并执行上面这段代码后可得到下面的运行结果:
[Exif] Make - FUJIFILM [Exif] Model - FinePix A205S [Exif] Orientation - Top, left side (Horizontal / normal) [Exif] X Resolution - 72 dots per inch [Exif] Y Resolution - 72 dots per inch [Exif] Resolution Unit - Inch [Exif] Software - Digital Camera FinePix A205S Ver1.00 [Exif] Date/Time - 2005:05:13 22:18:49 [Exif] YCbCr Positioning - Datum point [Exif] Copyright - [Exif] Exposure Time - 1/60 sec [Exif] F-Number - F3 [Exif] Exposure Program - Program normal [Exif] ISO Speed Ratings - 320 [Exif] Exif Version - 2.20 [Exif] Date/Time Original - 2005:05:13 22:18:49 [Exif] Date/Time Digitized - 2005:05:13 22:18:49 [Exif] Components Configuration - YCbCr [Exif] Compressed Bits Per Pixel - 3 bits/pixel [Exif] Shutter Speed Value - 1/63 sec [Exif] Aperture Value - F3 [Exif] Brightness Value - -61/100 [Exif] Exposure Bias Value - 0 EV [Exif] Max Aperture Value - F3 [Exif] Metering Mode - Multi-segment [Exif] Light Source - Unknown [Exif] Flash - Flash fired, auto [Exif] Focal Length - 5.5 mm [Exif] FlashPix Version - 1.00 [Exif] Color Space - sRGB [Exif] Exif Image Width - 1280 pixels [Exif] Exif Image Height - 960 pixels [Exif] Focal Plane X Resolution - 1/2415 cm [Exif] Focal Plane Y Resolution - 1/2415 cm [Exif] Focal Plane Resolution Unit - cm [Exif] Sensing Method - One-chip color area sensor [Exif] File Source - Digital Still Camera (DSC) [Exif] Scene Type - Directly photographed image [Exif] Custom Rendered - Normal process [Exif] Exposure Mode - Auto exposure [Exif] White Balance - Auto white balance [Exif] Scene Capture Type - Standard [Exif] Sharpness - None [Exif] Subject Distance Range - Unknown [Exif] Compression - JPEG (old-style) [Exif] Thumbnail Offset - 1252 bytes [Exif] Thumbnail Length - 7647 bytes [Exif] Thumbnail Data - [7647 bytes of thumbnail data]
Metadata metadata = JpegMetadataReader.readMetadata(jpegFile); Directory exif = metadata.getDirectory(ExifDirectory.class); String model = exif.getString(ExifDirectory.TAG_MODEL);
public String getOrientationDescription() throws MetadataException { if (!_directory.containsTag(ExifDirectory.TAG_ORIENTATION)) return null; int orientation = _directory.getInt(ExifDirectory.TAG_ORIENTATION); switch (orientation) { case 1: return "Top, left side (Horizontal / normal)"; case 2: return "Top, right side (Mirror horizontal)"; case 3: return "Bottom, right side (Rotate 180)"; case 4: return "Bottom, left side (Mirror vertical)"; case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)"; case 6: return "Right side, top (Rotate 90 CW)"; case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)"; case 8: return "Left side, bottom (Rotate 270 CW)"; default: return String.valueOf(orientation); } }
从这个方法我们可以清楚看到各个返回值的意思,如此我们便可以根据实际的返回值来对图像进行旋转或者是镜像处理了。在这个例子中我们需要将图片顺时针旋转270度,或者是逆时针旋转90度方可得到正常的图片。
虽然图片的旋转不在本文范畴内,但为了善始善终,下面给出代码用以旋转图片,其他的关于图片的镜像等处理读者可以依此类推。
String path = "D:\\TEST.JPG"; File img = new File(path); BufferedImage old_img = (BufferedImage)ImageIO.read(img); int w = old_img.getWidth(); int h = old_img.getHeight(); BufferedImage new_img = new BufferedImage(h,w,BufferedImage.TYPE_INT_BGR); Graphics2D g2d =new_img.createGraphics(); AffineTransform origXform = g2d.getTransform(); AffineTransform newXform = (AffineTransform)(origXform.clone()); // center of rotation is center of the panel double xRot = w/2.0; newXform.rotate(Math.toRadians(270.0), xRot, xRot); //旋转270度 g2d.setTransform(newXform); // draw image centered in panel g2d.drawImage(old_img, 0, 0, null); // Reset to Original g2d.setTransform(origXform); //写到新的文件 FileOutputStream out = new FileOutputStream("D:\\test2.jpg"); try{ ImageIO.write(new_img, "JPG", out); }finally{ out.close(); }
旋转后的照片如下:
但是利用上面的代码旋转照片后,原有照片包含的EXIF信息不复存在了。至于照片的镜面翻转可以直接利用Graphic2D的drawImage方法来实现,方法原形如下:
drawImage
public abstract boolean drawImage(Image img,
int dx1,
int dy1,
int dx2,
int dy2,
int sx1,
int sy1,
int sx2,
int sy2,
ImageObserver observer)
该方法的使用请参考JDK的API文档。关于照片旋转后丢失EXIF信息的问题,需要在照片旋转之前先把EXIF信息读出,然后再在旋转后写入新的照片中,你可以使用MediaUtil包来写EXIF信息到图片文件中,关于这个包的使用请参考该项目所给出的例子,本文不再叙述。