---恢复内容开始---

最近在开发中用到了metadata-extractor-xxx.jar 和 xmpcore-xxx.jar这个玩意, 索性查阅大量文章了解学习,来分享分享。本身工作也是经常和处理大图片打交道,摸索摸索也是多多益善。

首先介绍一下什么是EXIF,EXIF是 Exchangeable Image File 的缩写,这是一种专门为数码相机照片设定的格式。这种格式可以用来记录数字照片的属性信息,如相机的品牌及型号、相片的拍摄时间、拍摄时所设置的光圈大小、快门速度、ISO等信息。除此之外它还能够记录拍摄数据,以及图片格式化方式,这样就可以输出到兼容EXIF格式的外设上,如照片打印机等。

目前最常见的支持EXIF信息的图片格式是JPG,很多的图像工具都可以直接显示图片的EXIF信息,包括现在的一些著名的相册网站也提供页面用于显示照片的EXIF信息。本文介绍Java如何读取图像的EXIF信息,包括如何根据EXIF信息对图像进行调整以便适合用户浏览。

用BufferedImage类来读的时候,过大的图片时常会抛出OutOfMemoryException异常,挺蛋疼的。

  1. BufferedImage image = ImageIO.read(File file);

目前最简单易用的EXIF信息处理的Java包是 Drew Noakes 写的 metadata-extractor。这是一个能够从图像文件中读取元数据(Exif, IPTC, XMP, ICC等)的简单的Java库,使用简单:

  1. Metadata metadata = ImageMetadataReader.readMetadata(imagePath);

该库能了解多种格式的元数据,其中许多可以存在于单个图像:
Exif、IPTC、XMP、JFIF / JFXX、ICC Profiles、Photoshop fields、PNG properties、BMP properties、GIF properties

它能处理类型的文件:JPEG、TIFF、PSD、PNG、BMP、GIF、Camera Raw (NEF/CR2/ORF/ARW/RW2/...)

注:并不是每个JPG图像文件都包含有EXIF信息,你可以在Windows资源管理器单击选中图片后,如果该图片包含EXIF信息,则在窗口状态栏会显示出相机的型号。

二、示例代码及描述

下面我们给出一些代码将含有EXIF的图片信息全部打印出来。

示例1):

  1. import java.io.File;
  2. import java.util.Iterator;
  3. import com.drew.imaging.jpeg.JpegMetadataReader;
  4. import com.drew.metadata.Directory;
  5. import com.drew.metadata.Metadata;
  6. import com.drew.metadata.Tag;
  7. import com.drew.metadata.exif.ExifDirectory;
  8. /**
  9. * 读取图片的EXIF信息
  10. */
  11. public class ExifTest {
  12. public static void main(String[] args) throws Exception {
  13. //包含EXIF信息的图片地址
  14. File jpegFile = new File("D:\\XXXX\\XXXX\\XXXX.JPG");
  15. Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
  16. Directory exif = metadata.getDirectory(ExifDirectory.class);
  17. Iterator tags = exif.getTagIterator();
  18. while (tags.hasNext()) {
  19. Tag tag = (Tag)tags.next();
  20. System.out.println(tag);
  21. }
  22. }
  23. }

示例2:)

  1. public static void main(String[] args) throws Exception {
  2. File mFile = new File("F:/XXX.JPG");
  3. Metadata metadata = ImageMetadataReader.readMetadata(mFile);
  4. for (Directory directory : metadata.getDirectories()) {
  5. if("ExifSubIFDDirectory".equalsIgnoreCase( directory.getClass().getSimpleName() )){
  6. //光圈F值=镜头的焦距/镜头光圈的直径
  7. System.out.println("光圈值: f/" + directory.getString(ExifSubIFDDirectory.TAG_FNUMBER) );
  8. System.out.println("曝光时间: " + directory.getString(ExifSubIFDDirectory.TAG_EXPOSURE_TIME)+ "秒" );
  9. System.out.println("ISO速度: " + directory.getString(ExifSubIFDDirectory.TAG_ISO_EQUIVALENT) );
  10. System.out.println("焦距: " + directory.getString(ExifSubIFDDirectory.TAG_FOCAL_LENGTH) + "毫米" );
  11. System.out.println("拍照时间: " + directory.getString(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL) );
  12. System.out.println("宽: " + directory.getString(ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH) );
  13. System.out.println("高: " + directory.getString(ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT) );
  14. }
  15. if("ExifIFD0Directory".equalsIgnoreCase( directory.getClass().getSimpleName() )){
  16. System.out.println("照相机制造商: " + directory.getString(ExifIFD0Directory.TAG_MAKE) );
  17. System.out.println("照相机型号: " + directory.getString(ExifIFD0Directory.TAG_MODEL) );
  18. System.out.println("水平分辨率: " + directory.getString(ExifIFD0Directory.TAG_X_RESOLUTION) );
  19. System.out.println("垂直分辨率: " + directory.getString(ExifIFD0Directory.TAG_Y_RESOLUTION) );
  20. }
  21. }
  22. }

示例3):

  1. File mFilePath="C://XXX.jpg";
  2. Metadata metadata = com.drew.imaging.jpeg.JpegMetadataReader.readMetadata(mFilePath);
  3. JpegDirectory jd = (JpegDirectory)metadata.getDirectory(JpegDirectory.class);
  4. System.out.println("------------" + jd.getImageHeight()); //图片的高
  5. System.out.println("------------" + jd.getImageWidth());  //图片的宽
  6. //由于只是读取图片的头信息,所以无论多大的图片都能读取,而且速度很快.

从执行的中可以看到照片的详细拍摄时间,拍摄用的相机型号,曝光时间,光圈值,焦距,ISO值 等等。

你也可以直接指定读取其中任意参数的值,ExifDirectory 类中定义了很多以 TAG_ 开头的整数常量,这些常量代表特定的一个参数值,例如要读取相机的型号,可以用下面代码来获取。

  1. Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
  2. Directory exif = metadata.getDirectory(ExifDirectory.class);
  3. String model = exif.getString(ExifDirectory.TAG_MODEL);

上述提到的是如何获取照片的EXIF信息,其中包含一个很重要的信息就是——拍摄方向。例如所用的图片拍摄方向是:Orientation - Top, left side (Horizontal / normal)。我们在拍照的时候经常会根据场景的不同来选择相机的方向,例如拍摄一颗高树,我们会把相机竖着拍摄,使景物刚好适合整个取景框,但是这样得到的图片如果用普通的图片浏览器看便是倒着的,需要调整角度才能得到一个正常的图像。

通过读取图片的EXIF信息,可以得到关于拍摄方向的这样一个结果:Orientation - Left side, bottom (Rotate 270 CW)

而直接读取 ExitDirectory.TAG_ORIENTATION 标签的值是8。

来看下这个项目是如何来定义这些返回值的,打开源码包中的ExifDescriptor类的getOrientationDescription(),该方法代码如下:

  1. public String getOrientationDescription() throws MetadataException{
  2. if (!_directory.containsTag(ExifDirectory.TAG_ORIENTATION)) return null;
  3. int orientation = _directory.getInt(ExifDirectory.TAG_ORIENTATION);
  4. switch (orientation) {
  5. case 1: return "Top, left side (Horizontal / normal)";
  6. case 2: return "Top, right side (Mirror horizontal)";
  7. case 3: return "Bottom, right side (Rotate 180)";
  8. case 4: return "Bottom, left side (Mirror vertical)";
  9. case 5: return "Left side, top (Mirror horizontal and rotate 270 CW)";
  10. case 6: return "Right side, top (Rotate 90 CW)";
  11. case 7: return "Right side, bottom (Mirror horizontal and rotate 90 CW)";
  12. case 8: return "Left side, bottom (Rotate 270 CW)";
  13. default:
  14. return String.valueOf(orientation);
  15. }
  16. }

从这个方法可以清楚看到各个返回值的意思,如此我们便可以根据实际的返回值来对图像进行旋转或者是镜像处理了。

下面给出代码用以旋转图片,其他的关于图片的镜像等处理读者可以依此类推:

  1. String mPath = "D:\\XXX.JPG";
  2. File img = new File(mPath);
  3. BufferedImage old_img = (BufferedImage)ImageIO.read(img);
  4. int w = old_img.getWidth();
  5. int h = old_img.getHeight();
  6. BufferedImage new_img = new BufferedImage(h,w,BufferedImage.TYPE_INT_BGR);
  7. Graphics2D g2d =new_img.createGraphics();
  8. AffineTransform origXform = g2d.getTransform();
  9. AffineTransform newXform = (AffineTransform)(origXform.clone());
  10. // center of rotation is center of the panel
  11. double xRot = w/2.0;
  12. newXform.rotate(Math.toRadians(270.0), xRot, xRot); //旋转270度
  13. g2d.setTransform(newXform);
  14. // draw image centered in panel
  15. g2d.drawImage(old_img, 0, 0, null);
  16. // Reset to Original
  17. g2d.setTransform(origXform);
  18. //写到新的文件
  19. FileOutputStream out = new FileOutputStream("D:\\XXX2.jpg");
  20. try{
  21. ImageIO.write(new_img, "JPG", out);
  22. }finally{
  23. out.close();
  24. }

注:利用上面的代码旋转照片后,原有照片包含的EXIF信息就不存在了。关于该问题需要在照片旋转之前先把EXIF信息读出,然后再在旋转后写入新的照片中,可以使用 MediaUtil 包来写EXIF信息到图片文件中,关于这个包的使用可参考最后的链接。

照片的镜面翻转可以直接利用Graphic2D 的 drawImage 方法来实现:

  1. public abstract boolean drawImage(Image img,
  2. int dx1,int dy1,
  3. int dx2,int dy2,
  4. int sx1,int sy1,
  5. int sx2,int sy2,
  6. ImageObserver observer);

三、补充说明

解释部分参数的实际含义:

Make 生产者 指产品生产厂家

Model 型号 指设备型号

Orientation  方向 有的相机支持,有的不支持

X Resolution/Y Resolution X/Y方向分辨率 本栏目已有专门条目解释此问题

ResolutionUnit  分辨率单位 一般为PPI

Software  软件 显示固件Firmware版本

DateTime  日期和时间

YCbCrPositioning  色相定位

ExifOffsetExif  信息位置,定义Exif在信息在文件中的写入,有些软件不显示。

ExposureTime 曝光时间 即快门速度

FNumber  光圈系数

ISO speed ratings  感光度

ExifVersionExif  版本

DateTimeOriginal  创建时间

DateTimeDigitized  数字化时间

ComponentsConfiguration  图像构造(多指色彩组合方案)

CompressedBitsPerPixel(BPP)  压缩时每像素色彩位 指压缩程度

ExposureBiasValue  曝光补偿。

MaxApertureValue  最大光圈

MeteringMode  测光方式, 平均式测光、中央重点测光、点测光等。

Lightsource  光源 指白平衡设置

Flash  是否使用闪光灯。

FocalLength  焦距,一般显示镜头物理焦距,有些软件可以定义一个系数,显示相当于35mm相机的焦距 MakerNote(User Comment)作者标记、说明、记录

FlashPixVersionFlashPix  版本 (个别机型支持)

ColorSpace 色域、色彩空间

ExifImageWidth(Pixel X Dimension)  图像宽度 指横向像素数

ExifImageLength(Pixel Y Dimension)  图像高度 指纵向像素数

---恢复内容结束---

读取图片信息(exif),使用com.drew.metadata.Metadata的更多相关文章

  1. Android中读取图片EXIF元数据之metadata-extractor的使用

    一.引言及介绍 近期在开发中用到了metadata-extractor-xxx.jar 和 xmpcore-xxx.jar这个玩意, 索性查阅大量文章了解学习,来分享分享. 本身工作也是常常和处理大图 ...

  2. Java读取图片exif信息实现图片方向自动纠正

    起因 一个对试卷进行OCR识别需求,需要实现一个功能,一个章节下的题目图片需要上下拼接合成一张大图,起初写了一个工具实现图片的合并,程序一直很稳定的运行着,有一反馈合成的图片方向不对,起初怀疑是本身图 ...

  3. 利用Python读取图片exif敏感信息

    众所周知,现在很多的照相机等软件,拍摄会有选项,是否包含位置信息等. 当然有的人会说,我在微信中查看图片exif信息并没有啊,这是因为你发送到微信服务器的时候,微信帮你完成了保密工作. 常见的图片中包 ...

  4. C#读取图片Exif信息

    Exif是可交换图像文件的缩写,是专门为数码相机的照片设定的,可以记录数码照片的属性和拍摄数据 ////调用 //string strFile="fffff.jpg";//文件名 ...

  5. 小程序 读取照片 EXIF 元信息

    安装 exif.js npm install exif-js --save UI <button type="primary" @click="onExif&quo ...

  6. html5 filereader 读取图片信息

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. Android之读取 AndroidManifest.xml 中的数据:版本号、应用名称、自定义K-V数据(meta-data)

    AndroidManifest.xml中的定义如下: <manifest xmlns:android="http://schemas.android.com/apk/res/andro ...

  8. java读取图片的(尺寸、拍摄日期、标记)等EXIF信息

    1.metadata-extractor是 处理图片EXIF信息的开源项目,最新代码及下载地址:https://github.com/drewnoakes/metadata-extractor 2.本 ...

  9. 使用Java程序读取JPG Tif等格式图片的exif信息

    package com.util; import java.io.File;import java.util.Iterator; import com.drew.imaging.ImageProces ...

随机推荐

  1. 新手C#构造函数、继承、组合的学习2018.08.06/07

    构造函数,是一种特殊的方法.主要用来在创建对象时初始化对象,即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中.特别的一个类可以有多个构造函数,可根据其参数个数的不同或参数类型的不同 ...

  2. SpringBoot配置Shiro时@RequiresRoles不起作用

    在SpringBoot中配置Shiro,结果@RequiresRoles.@RequiresPermissions等注解都无效 解决方法: 在ShiroConfiguration中注入开启支持aop. ...

  3. 2015年传智播客JavaEE 第168期就业班视频教程06-权限校验子系统介绍

    没整过论坛你也整过淘宝,其实淘宝登录的也分商家和个人,卖家和买家,不同的人登录显示的东西是不一样的.权限系统要分两大过程,第四天上午下午分开,分为授权与校验.我把某一个职务给你叫做授权,例如封你为征西 ...

  4. iOS App图标和启动画面尺寸

    注意:iOS所有图标的圆角效果由系统生成,给到的图标本身不能是圆角的. 1. 桌面图标 (app icon) for iPhone6 plus(@3x) : 180 x 180 for iPhone ...

  5. mybatis整合spring的完整过程

    1.1 整合思路 1.SqlSessionFactory对象应该放到spring容器中作为单例存在. 2.传统dao的开发方式中,应该从spring容器中获得sqlsession对象. 3.Mappe ...

  6. Python爬虫进阶五之多线程的用法

    前言 我们之前写的爬虫都是单个线程的?这怎么够?一旦一个地方卡到不动了,那不就永远等待下去了?为此我们可以使用多线程或者多进程来处理. 首先声明一点! 多线程和多进程是不一样的!一个是 thread ...

  7. C语言中的nan和inf使用

    本文总结nan和inf在C语言当中的含义.产生和判定方法. C语言当中的nan 表示not a number,等同于 #IND:indeterminate (windows) 产生: 对浮点数进行了未 ...

  8. JavaScript 语法总结3

    1. 数组初始化可以跳着来  var s = [1,2,,,,6]; // 中间省略的元素为undefined 2. 函数定义表达式:  var f = function(args){ return ...

  9. wamp如何设置数据库的密码

    WAMP安装好后,MySQL密码是为空的,那么要如何修改呢?其实很简单,通过几条指令就行了,下面我就一步步来操作. 首先,通过WAMP打开mysql控制台. 提示输入密码,因为现在是空,所以直接按回车 ...

  10. 构建搞性能可扩展asp.net网站文摘

    第1章 原则与方法 网页加载的过程: 关注感知性能,减少阻塞调用,减少往返,在所有架构层次采用缓存,优化硬盘I/O 了解浏览器的工作方式,使用ajax,silverlight和纯javascript避 ...