Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中讀取圖片EXIF元數據之metadata-extractor的使用

Android中讀取圖片EXIF元數據之metadata-extractor的使用

編輯:關於Android編程

 

一、引言及介紹

 

最近在開發中用到了metadata-extractor-xxx.jar 和 xmpcore-xxx.jar這個玩意, 索性查閱大量文章了解學習,來分享分享。本身工作也是經常和處理大圖片打交道,摸索摸索也是多多益善。

 

首先介紹一下什麼是EXIF,EXIF是 Exchangeable Image File 的縮寫,這是一種專門為數碼相機照片設定的格式。這種格式可以用來記錄數字照片的屬性信息,如相機的品牌及型號、相片的拍攝時間、拍攝時所設置的光圈大小、快門速度、ISO等信息。除此之外它還能夠記錄拍攝數據,以及圖片格式化方式,這樣就可以輸出到兼容EXIF格式的外設上,如照片打印機等。


目前最常見的支持EXIF信息的圖片格式是JPG,很多的圖像工具都可以直接顯示圖片的EXIF信息,包括現在的一些著名的相冊網站也提供頁面用於顯示照片的EXIF信息。本文介紹Java如何讀取圖像的EXIF信息,包括如何根據EXIF信息對圖像進行調整以便適合用戶浏覽。

用BufferedImage類來讀的時候,過大的圖片時常會拋出OutOfMemoryException異常,挺蛋疼的。

 

BufferedImage image = ImageIO.read(File file);

 

目前最簡單易用的EXIF信息處理的Java包是 Drew Noakes 寫的 metadata-extractor。這是一個能夠從圖像文件中讀取元數據(Exif, IPTC, XMP, ICC等)的簡單的Java庫,使用簡單:

 

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):

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信息
     */
    public class ExifTest {
        
         public static void main(String[] args) throws Exception {
             
             //包含EXIF信息的圖片地址
             File jpegFile = new File(D:\XXXX\XXXX\XXXX.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);
                 
             }
         }
    }

 

示例2:)

public static void main(String[] args) throws Exception {

        File mFile = new File(F:/XXX.JPG);
        Metadata metadata = ImageMetadataReader.readMetadata(mFile);
        for (Directory directory : metadata.getDirectories()) {

            if(ExifSubIFDDirectory.equalsIgnoreCase( directory.getClass().getSimpleName() )){

                //光圈F值=鏡頭的焦距/鏡頭光圈的直徑
                System.out.println(光圈值: f/ + directory.getString(ExifSubIFDDirectory.TAG_FNUMBER) );

                System.out.println(曝光時間:  + directory.getString(ExifSubIFDDirectory.TAG_EXPOSURE_TIME)+ 秒 );
                System.out.println(ISO速度:  + directory.getString(ExifSubIFDDirectory.TAG_ISO_EQUIVALENT) );
                System.out.println(焦距:  + directory.getString(ExifSubIFDDirectory.TAG_FOCAL_LENGTH) + 毫米 );

                System.out.println(拍照時間:  + directory.getString(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL) );
                System.out.println(寬:  + directory.getString(ExifSubIFDDirectory.TAG_EXIF_IMAGE_WIDTH) );
                System.out.println(高:  + directory.getString(ExifSubIFDDirectory.TAG_EXIF_IMAGE_HEIGHT) );

            }

            if(ExifIFD0Directory.equalsIgnoreCase( directory.getClass().getSimpleName() )){
                System.out.println(照相機制造商:  + directory.getString(ExifIFD0Directory.TAG_MAKE) );
                System.out.println(照相機型號:  + directory.getString(ExifIFD0Directory.TAG_MODEL) );
                System.out.println(水平分辨率:  + directory.getString(ExifIFD0Directory.TAG_X_RESOLUTION) );
                System.out.println(垂直分辨率:  + directory.getString(ExifIFD0Directory.TAG_Y_RESOLUTION) );
            }
        }
    }

 

示例3):

 

File mFilePath=C://XXX.jpg;

    Metadata metadata = com.drew.imaging.jpeg.JpegMetadataReader.readMetadata(mFilePath);

    JpegDirectory jd = (JpegDirectory)metadata.getDirectory(JpegDirectory.class);
    System.out.println(------------ + jd.getImageHeight()); //圖片的高
    System.out.println(------------ + jd.getImageWidth());  //圖片的寬
    
    //由於只是讀取圖片的頭信息,所以無論多大的圖片都能讀取,而且速度很快.

 

 

從執行的中可以看到照片的詳細拍攝時間,拍攝用的相機型號,曝光時間,光圈值,焦距,ISO值 等等。


你也可以直接指定讀取其中任意參數的值,ExifDirectory 類中定義了很多以 TAG_ 開頭的整數常量,這些常量代表特定的一個參數值,例如要讀取相機的型號,可以用下面代碼來獲取。

 

Metadata metadata = JpegMetadataReader.readMetadata(jpegFile);
        
Directory exif = metadata.getDirectory(ExifDirectory.class);
        
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(),該方法代碼如下:

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);
        }
    }

 

從這個方法可以清楚看到各個返回值的意思,如此我們便可以根據實際的返回值來對圖像進行旋轉或者是鏡像處理了。

 

下面給出代碼用以旋轉圖片,其他的關於圖片的鏡像等處理讀者可以依此類推:

 

String mPath = D:\XXX.JPG;
    
    File img = new File(mPath);
    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:\XXX2.jpg);
    try{
        ImageIO.write(new_img, JPG, out);
    }finally{
        out.close();
    }

 

注:利用上面的代碼旋轉照片後,原有照片包含的EXIF信息就不存在了。關於該問題需要在照片旋轉之前先把EXIF信息讀出,然後再在旋轉後寫入新的照片中,可以使用 MediaUtil 包來寫EXIF信息到圖片文件中,關於這個包的使用可參考最後的鏈接。

照片的鏡面翻轉可以直接利用Graphic2D 的 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);

 

三、補充說明

 

解釋部分參數的實際含義:

 

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) 圖像高度 指縱向像素數

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved