自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android實(shí)現(xiàn)JPEG圖片壓縮后同時(shí)保留圖片的EXIF信息

移動(dòng)開發(fā) Android
在實(shí)際開發(fā)中,對(duì)于圖片數(shù)據(jù)不論是緩存在本地磁盤還是上傳到后端,都需要先對(duì)圖片進(jìn)行壓縮處理。在圖片壓縮的過程中,為了減小文件大小,一些不重要的元數(shù)據(jù)(包括EXIF信息)可能會(huì)被移除或修改。如果圖片經(jīng)過壓縮處理,其原始的EXIF信息可能會(huì)丟失或不完整。

EXIF信息是可交換圖像文件格式(Exchangeable Image File Format)的縮寫,是在JPEG格式的基礎(chǔ)上發(fā)展起來的,其中包含了一系列按照一定標(biāo)準(zhǔn)制定的有關(guān)圖像拍攝信息的數(shù)據(jù)和索引,包括快門速度、光圈、ISO感光度、曝光偏移、日期和時(shí)間、閃光使用情況、焦距、GPS定位數(shù)據(jù)等。

在實(shí)際開發(fā)中,對(duì)于圖片數(shù)據(jù)不論是緩存在本地磁盤還是上傳到后端,都需要先對(duì)圖片進(jìn)行壓縮處理。在圖片壓縮的過程中,為了減小文件大小,一些不重要的元數(shù)據(jù)(包括EXIF信息)可能會(huì)被移除或修改。如果圖片經(jīng)過壓縮處理,其原始的EXIF信息可能會(huì)丟失或不完整。

EXIF信息附加于JPEG、TIFF、RIFF等文件之中,可以記錄數(shù)碼照片的屬性信息和拍攝數(shù)據(jù)。比如記錄以下信息:

項(xiàng)目

資訊(舉例)

制造廠商

Canon

相機(jī)型號(hào)

Canon EOS-1Ds Mark III

影像方向

正常(upper-left)

影像解析度X

300

影像解析度Y

300

解析度單位

dpi

軟件

Adobe Photoshop CS Macintosh

最后異動(dòng)時(shí)間

2005:10:06 12:53:19

YCbCrPositioning

2

曝光時(shí)間

0.00800 (1/125) sec

光圈值

F22

拍攝模式

光圈優(yōu)先

ISO感光值

100

Exif資訊版本

30,32,32,31

影像拍攝時(shí)間

2005:09:25 15:00:18

影像存入時(shí)間

2005:09:25 15:00:18

曝光補(bǔ)償(EV+-)

0

測(cè)光模式

點(diǎn)測(cè)光(Spot)

閃光燈

關(guān)閉

鏡頭實(shí)體焦長(zhǎng)

12 mm

Flashpix版本

30,31,30,30

影像色域空間

sRGB

影像尺寸X

5616 pixel

影像尺寸Y

3744 pixel

有一些壓縮工具或軟件提供了保留EXIF信息的選項(xiàng)。在使用這些工具進(jìn)行壓縮時(shí),可以選擇保留EXIF信息,以確保壓縮后的圖片仍然包含完整的元數(shù)據(jù)。在實(shí)際開發(fā)中我們?nèi)绾芜M(jìn)行保留EXIF信息的同時(shí)進(jìn)行圖片壓縮呢?

使用ExifInterface方案

ExifInterface是Android系統(tǒng)中用于描述多媒體文件(如JPG格式圖片)附加信息的一個(gè)類。它主要涵蓋了拍攝時(shí)的光圈、快門、白平衡、ISO、焦距、日期時(shí)間等各種拍攝條件,以及相機(jī)品牌、型號(hào)、色彩編碼、拍攝時(shí)錄制的聲音以及全球定位系統(tǒng)(GPS)和縮略圖等信息。簡(jiǎn)單來說,ExifInterface就是JPEG圖像文件+拍攝參數(shù)的結(jié)合。

ExifInterface類主要提供了讀取、寫入和縮略圖處理這三個(gè)方面的功能。通過ExifInterface,可以獲取到圖片的多種屬性,如方向(orientation)、拍攝時(shí)間(dateTime)、設(shè)備制造商(make)、設(shè)備型號(hào)(model)等。

ExifInterface類只提供了 getXXX() 和 setAttributes(String tag, String value) 這種操作單個(gè)屬性的方法,如果想將原圖片文件中的所有EXIF信息完整復(fù)制到另一個(gè)圖片中會(huì)非常繁瑣。因此有人通過反射,對(duì)所有屬性名進(jìn)行遍歷,從而實(shí)現(xiàn)了批量操作。也算是一種解決方案,具體如下:

public static void saveExif(String oldFilePath, String newFilePath) throws Exception {
    ExifInterface oldExif = new ExifInterface(oldFilePath);
    ExifInterface newExif = new ExifInterface(newFilePath);
    Class<ExifInterface> cls = ExifInterface.class;
    Field[] fields = cls.getFields();
    for (int i = 0; i < fields.length; i++) {
        String fieldName = fields[i].getName();
        if (!TextUtils.isEmpty(fieldName) && fieldName.startsWith("TAG")) {
            String fieldValue = fields[i].get(cls).toString();
            String attribute = oldExif.getAttribute(fieldValue);
            if (attribute != null) {
                newExif.setAttribute(fieldValue, attribute);
            }
        }
    }
    //將內(nèi)存中的修改寫入磁盤(IO操作)
    newExif.saveAttributes();
 }

以上方案弊端也很明顯,就是需要對(duì)文件進(jìn)行多次IO操作。觀察上面方法中的兩個(gè)參數(shù)都是文件路徑,比如我們通過拍照進(jìn)行圖片壓縮上傳,那么拍完照通過 onPictureTaken(byte[] data, Camera camera) 回調(diào)方法拿到圖片的 byte[] data 數(shù)據(jù)后處理是這樣的:

  • 將data緩存到磁盤,路徑為oldFilePath;(IO)
  • 將data轉(zhuǎn)換成 bitmap 進(jìn)行壓縮、旋轉(zhuǎn)、剪切等操作;
  • 將處理后的 bitmap 緩存到磁盤,路徑為newFilePath;(IO)
  • 調(diào)用上面的 saveExif(oldFilePath, newFilePath) 方法; (IO)

能否只在內(nèi)存中操作?發(fā)現(xiàn)有 ExifInterface (String filename) 和 ExifInterface (InputStream inputStream) 兩種構(gòu)造方法, 進(jìn)行如下改造:

public static void saveExif(byte[] srcData, String destFilePath) throws Exception {
    ExifInterface oldExif = new ExifInterface(new ByteArrayInputStream(srcData));
    ExifInterface newExif = new ExifInterface(destFilePath);
    Class<ExifInterface> cls = ExifInterface.class;
    Field[] fields = cls.getFields();
    for (int i = 0; i < fields.length; i++) {
        String fieldName = fields[i].getName();
        if (!TextUtils.isEmpty(fieldName) && fieldName.startsWith("TAG")) {
            String fieldValue = fields[i].get(cls).toString();
            String attribute = oldExif.getAttribute(fieldValue);
            if (attribute != null) {
                newExif.setAttribute(fieldValue, attribute);
            }
        }
    }
    //將內(nèi)存中的修改寫入磁盤(IO操作)
    newExif.saveAttributes();
 }

使用自定義方案

不管是圖片還是其他文件,本質(zhì)都是格式化的數(shù)據(jù),都有專用的數(shù)據(jù)結(jié)構(gòu)。研究下JPG的數(shù)據(jù)結(jié)構(gòu),找到 EXIF 數(shù)據(jù)塊的起始索引,然后從源文件byte[]中復(fù)制插入到目標(biāo)文件byte[]對(duì)應(yīng)位置中就實(shí)現(xiàn)了。

圖片圖片

JPEG文件的內(nèi)容都開始于一個(gè)二進(jìn)制的值 '0xFFD8', 并結(jié)束于二進(jìn)制值'0xFFD9'. 在JPEG的數(shù)據(jù)中有好幾種類似于二進(jìn)制 0xFFXX 的數(shù)據(jù)都統(tǒng)稱作 "標(biāo)記", 代表了一段JPEG的信息數(shù)據(jù)。

0xFFD8 的意思是 SOI圖像起始(Start of image) ,是Jpeg文件的魔數(shù)(Magic Number)。每種格式的文件都有固定的Magic Number,比如.class 字節(jié)碼文件的Magic Number是 “0xCAFEBABE”。0xFFD9 則表示 EOI圖像結(jié)束 (End of image)。

0xFF+標(biāo)記號(hào)(1個(gè)字節(jié))+數(shù)據(jù)大小描述符(2個(gè)字節(jié))+數(shù)據(jù)內(nèi)容(n個(gè)字節(jié))

對(duì)于EXIF數(shù)據(jù),使用的是APP1標(biāo)記,前兩個(gè)字節(jié)固定為 0xFFE1,后面緊跟著兩個(gè)字節(jié)記錄的是EXIF數(shù)據(jù)內(nèi)容的 length + 2,假設(shè)這兩個(gè)字節(jié)的值是 24,那么EXIF數(shù)據(jù)內(nèi)容的長(zhǎng)度就是22字節(jié)。所以只要找到EXIF在數(shù)組中的起始索引,摳出來插入到新數(shù)組中去就完成了。

圖片圖片

public static byte[] cloneExif(byte[] srcData, byte[] destData) {
    if (srcData == null || srcData.length == 0 || destData == null || destData.length == 0) return null;

    ImageHeaderParser srcImageHeaderParser = new ImageHeaderParser(srcData);
    byte[] srcExifBlock = srcImageHeaderParser.getExifBlock();
    if (srcExifBlock == null || srcExifBlock.length <= 4) return null;

    LOG.d(TAG, "pictureData src: %1$s KB; dest: %2$s KB", srcData.length / 1024, destData.length / 1024);
    LOG.d(TAG, "srcExif: %s B", srcExifBlock.length);
    ImageHeaderParser destImageHeaderParser = new ImageHeaderParser(destData);
    byte[] destExifBlock = destImageHeaderParser.getExifBlock();
    if (destExifBlock != null && destExifBlock.length > 0) {
        LOG.d(TAG, "destExif: %s B", destExifBlock.length);
        //目標(biāo)圖片中已有exif信息, 需要先刪除
        int exifStartIndex = destImageHeaderParser.getExifStartIndex();
        //構(gòu)建新數(shù)組
        byte[] newDestData = new byte[srcExifBlock.length + destData.length - destExifBlock.length];
        //copy 1st block
        System.arraycopy(destData, 0, newDestData, 0, exifStartIndex);
        //copy 2rd block (exif)
        System.arraycopy(srcExifBlock, 0, newDestData, exifStartIndex, srcExifBlock.length);
        //copy 3th block
        int srcPos = exifStartIndex + destExifBlock.length;
        int destPos = exifStartIndex + srcExifBlock.length;
        System.arraycopy(destData, srcPos, newDestData, destPos, destData.length - srcPos);
        LOG.d(TAG, "output image Data with exif: %s KB", newDestData.length / 1024);
        return newDestData;
    } else {
        LOG.d(TAG, "destExif: %s B", 0);
        //目標(biāo)圖片中沒有exif信息
        byte[] newDestData = new byte[srcExifBlock.length + destData.length];
        //copy 1st block (前兩個(gè)字節(jié))
        System.arraycopy(destData, 0, newDestData, 0, 2);
        //copy 2rd block (exif)
        System.arraycopy(srcExifBlock, 0, newDestData, 2, srcExifBlock.length);
        //copy 3th block
        int srcPos = 2;
        int destPos = 2 + srcExifBlock.length;
        System.arraycopy(destData, srcPos, newDestData, destPos, destData.length - srcPos);
        LOG.d(TAG, "output image Data with exif: %s KB", newDestData.length / 1024);
        return newDestData;
    }

}

將原圖的數(shù)據(jù)流和壓縮處理后的數(shù)據(jù)流傳入,調(diào)用cloneExif方法,返回附加了EXIF信息的數(shù)據(jù)流,將返回的數(shù)據(jù)流存儲(chǔ)即得到一張帶有原EXIF信息的壓縮圖片。

「注意」上述方法只針對(duì)JPEG格式圖片,其他格式文件數(shù)據(jù)結(jié)構(gòu)不同,方法可能無(wú)效。

責(zé)任編輯:武曉燕 來源: 沐雨花飛蝶
相關(guān)推薦

2024-04-19 08:31:40

Android屬性讀取

2009-08-20 12:35:41

C#讀取圖片的EXIF

2020-05-07 09:45:16

前端JS圖片壓縮

2009-08-24 17:02:18

C#旋轉(zhuǎn)圖片EXIF

2020-10-20 11:12:11

Nodejs

2013-07-29 10:02:42

2018-10-29 09:24:41

Web圖片壓縮網(wǎng)頁(yè)加速

2023-01-15 20:28:32

前端圖片壓縮

2023-11-04 12:43:44

前端圖片參數(shù)

2022-08-08 08:29:55

圖片壓縮前端互聯(lián)網(wǎng)

2013-06-27 11:16:27

Android異步加載

2010-10-12 13:57:43

GoogleWebP

2022-07-17 11:22:35

前端開發(fā)圖片裁切壓縮上傳

2016-03-29 10:18:48

Android圖片代碼

2023-08-21 12:13:53

2011-04-11 14:14:29

checkboxlistviewAndroid

2020-08-21 09:58:16

谷歌Android工具

2022-06-14 07:29:51

squoosh壓縮工具開源

2011-05-30 13:23:11

Android 動(dòng)畫

2013-05-15 10:27:05

R語(yǔ)言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)