Android中讀取圖片EXIF中繼資料之metadata-extractor的使用

來源:互聯網
上載者:User

標籤:開啟   下載   getheight   顯示   壓縮   2.0   finally   .class   max   


一、引言及介紹


近期在開發中用到了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 ResolutionX/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) 映像高度 指縱向像素數


四、參考資料及資源下載

項目的最新版本號碼及其原始碼下載:

GitHub首頁:https://github.com/drewnoakes/metadata-extractor

http://www.drewnoakes.com/code/exif/ 

MediaUtil   http://mediachest.sourceforge.net/mediautil/

Android中讀取圖片EXIF中繼資料之metadata-extractor的使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.