關於圖片的Exif信息

一 問題

聽到小夥伴說有關審覈圖片的接口經常超時的時候,正好看了下這塊代碼。主要是從cdn取幾張圖片,校驗創建時間。

這塊邏輯是用下載圖片讀流的方式處理。受制於網絡及圖片文件,自然性能快不了。

二 優化

  本文不展開圖片的反作弊,只簡單介紹下圖片的創建時間識別。

我們只是爲了獲取圖片的拍攝時間做初步判斷,而不是圖片內容本身。所以有沒有辦法只獲取圖片信息而不去下載圖片就好了。

答案是有的。

先說下exif.  EXIF(Exchangeable Image File)是 “可交換圖像文件” 的縮寫,當中包含了專門爲數碼相機的照片而定製的元數據,可以記錄數碼照片的拍攝參數、縮略圖及其他屬性信息,簡單來說,Exif 信息是鑲嵌在 JPEG/TIFF 圖像文件格式內的一組拍攝參數.正常的手機拍攝的圖片會包含這些信息。

看個樣例:

{
    "ApertureValue": {"value": "193685/85136"},
    "BrightnessValue": {"value": "100718/10007"},
    "ColorSpace": {"value": "1"},
    "DateTime": {"value": "2019:08:16 15:54:15"},
    "DateTimeDigitized": {"value": "2019:08:16 15:54:15"},
    "DateTimeOriginal": {"value": "2019:08:16 15:54:15"},
    "ExifTag": {"value": "192"},
    "ExposureBiasValue": {"value": "0/1"},
    "ExposureMode": {"value": "0"},
    "ExposureProgram": {"value": "2"},
    "ExposureTime": {"value": "1/1721"},
    "FNumber": {"value": "11/5"},
    "FileSize": {"value": "241531"},
    "Flash": {"value": "24"},
    "FocalLength": {"value": "83/20"},
    "FocalLengthIn35mmFilm": {"value": "29"},
    "Format": {"value": "jpg"},
    "GPSLatitude": {"value": "31deg 55' 3.870\" "},
    "GPSLongitude": {"value": "117deg 18' 4.110\" "},
    "GPSTag": {"value": "699"},
    "ISOSpeedRatings": {"value": "32"},
    "ImageHeight": {"value": "1001"},
    "ImageWidth": {"value": "750"},
    "LensMake": {"value": "Apple"},
    "LensModel": {"value": "iPhone 6 back camera 4.15mm f/2.2"},
    "LensSpecification": {"value": "83/20 83/20 11/5 11/5"},
    "Make": {"value": "Apple"},
    "MeteringMode": {"value": "5"},
    "Model": {"value": "iPhone 6"},
    "Orientation": {"value": "1"},
    "PixelXDimension": {"value": "750"},
    "PixelYDimension": {"value": "1001"},
    "ResolutionUnit": {"value": "2"},
    "SceneType": {"value": "1"},
    "SensingMethod": {"value": "2"},
    "ShutterSpeedValue": {"value": "100881/9385"},
    "Software": {"value": "12.3"},
    "SubSecTimeDigitized": {"value": "039"},
    "SubSecTimeOriginal": {"value": "039"},
    "SubjectArea": {"value": "1631 1223 1795 1077"},
    "WhiteBalance": {"value": "0"},
    "XResolution": {"value": "144/1"},
    "YResolution": {"value": "144/1"}}

參數很多,很有用的(詳細可以看下面的)。

這裏我們關注時間:DateTimeOriginal 這是原始時間,不能被修改的。還有個是DateTime。圖像最後一次被修改時的日期/時間. 

判斷這個可以做初步的校驗,其他的圖片相似度反作弊還是有專業的做吧。

關於接口優化,因爲圖片已經在cdn上,每家不一樣獲取exif不一樣。

阿里雲是:http://image-demo.img.aliyuncs.com/f.jpg@exif

七牛雲:http://7xt44n.com2.z0.glb.qiniucdn.com/exif.png?exif

並非每一張圖片都包含 exif 信息,需要自己結合業務判斷。

另外,有的根據Orientation 去調整圖片的角度顯示正常的,還有根據gps信息去判斷位置的。這個比較敏感,上面的gps數據是假的。

這篇文章整理的很好。http://blog.sina.com.cn/s/blog_651251e60102uz3d.html

JPEG格式和標記

每一個JPEG文件的內容都開始於一個二進制的值 '0xFFD8', 並結束與二進制值'0xFFD9'. 在JPEG的數據 中有好幾種類似於二進制 0xFFXX 的數據, 它們都統稱作 "標記", 並且它們代表了一段JPEG的 信息數據. 0xFFD8 的意思是 SOI圖像起始(Start of image), 0xFFD9 則表示 EOI圖像結束 (End of image). 這兩個特殊的標記的後面都不跟隨數據, 而其他的標記在後面則會附帶數據. 標記的基本 格式如下.

0xFF+標記號(1個字節)+數據大小描述符(2個字節)+數據內容(n個字節)

數據大小描述符(2個字節) 是 "Motorola" 的字節順序, 數據的低位被存放在高地址,也就是 BigEndian. 請注意上面中的 "數據內容" 中包含他前面的數據大小描述符, 如果下面的是一個標記的話;

FF C1 00 0C

它就表示這個標記(0xFFC1) 的數據佔 0x000C(等於12)個字節. 但是這個數據大小'12' 包含了 "數據大小" 描述符, 也就是在0x000C後面它只允許帶有10 個字節大小的數據.

在JPEG 格式中, 最開始先是用一些標記來描述數據, 然後是放置 SOS數據流的起始(Start of stream) 標記. 在SOS標記的後面纔是, 存放JPEG圖像的數據流並終結於EOI標記.
 

SOI 標記 標記 XX 的大小=SSSS 標記 YY 的大小=TTTT SOS 標記 的大小=UUUU 圖像數據流 EOI 標記
FFD8 FFXX SSSS DDDD...... FFYY TTTT DDDD...... FFDA UUUU DDDD.... I I I I.... FFD9

Exif使用的標記

0xFFE0~0xFFEF之間的標記被叫做 "應用標記", 它們在JPEG圖像解碼中不是必須存在的. 它們被使用於用戶的應用程序之中. 例如, 老款的olympus/canon/casio/agfa 數字相機使用 JFIF(JPEG文件交換格式/JPEG File Interchange Format)來存儲圖像. JFIF 使用 APP0(0xFFE0) 標記來插入數字相機的配置信息數據和縮略圖.

Exif也使用應用標記來插入數據, 但是Exif 使用 APP1(0xFFE1)標記來避免與JFIF格式的 衝突. 且每一個 Exif 文件格式都開始於它, 如;
 

SOI 標記 APP1 標記 APP1 數據 Other 標記
FFD8 FFE1 SSSS 457869660000 TTTT...... FFXX SSSS DDDD......


該圖像文件從SOI(0xFFD8) 標記開始, 因此它是一個 JPEG 文件. 後面馬上跟着 APP1 標記. 而它的所有 Exif數據都被存儲在 APP1 數據域中. 上面的 "SSSS" 這部分表示 APP1 數據域 (Exif data area)的大小. 請注意這裏的大小 "SSSS" 包含描述符本身的大小.

在 "SSSS"後面, 是 APP1 的數據. 其中第一個部分是一個特殊的數據,它用來標識是否是 Exif, 其值是ASCII 字符 "Exif" 和 兩個0x00字節 的組合字符串.

在 APP1 標記域的後面是, 跟隨着其他的 JPEG 標記.

IFD0 (主圖像)使用的標籤

標籤號 標籤名 格式 組件數 描述
0x010e ImageDescription ascii string   用來描述圖像. 雙字節的字符碼不能使用, 如 中文/韓文/日文.
0x010f Make ascii string   表示數字相機的製造商. 在 Exif 標準中, 這個標籤是可選的, 但是在DCF中它是必需的.
0x0110 Model ascii string   表示數字相機的模塊代碼. 在 Exif 標準中, 這個標籤是可選的, 但在DCF中它也是必需的.
0x0112 Orientation unsigned short 1
Value 0th Row 0th Column
1 top left side
2 top right side
3 bottom right side
4 bottom left side
5 left side top
6 right side top
7 right side bottom
8 left side bottom
當拍照時, 相機相對於場景的方向. 在右邊表示的是'0th row' 以及 '0th column' 在視覺位置上的關係.
0x011a XResolution unsigned rational 1 圖像的 顯示/打印 分辨率. 缺省值是 1/72英寸, 但是它沒有意義因爲個人PC在 顯示/打印 圖像的時候不使用這個值.
0x011b YResolution unsigned rational 1
0x0128 ResolutionUnit unsigned short 1 XResolution(0x011a)/YResolution(0x011b)的單位. '1' 表示沒有單位, '2' 意味着英寸, '3' 表示釐米. 缺省值是 '2'(英寸).
0x0131 Software ascii string   顯示固件的版本號(數字相機的內部控制軟件).
0x0132 DateTime ascii string 20 圖像最後一次被修改時的日期/時間. 日期的格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共 20個字節. 如果沒有設置時鐘或者數字相機沒有時鐘, 則這個域是用空格來填充. 通常, 它和DateTimeOriginal(0x9003)具有相同的值
0x013e WhitePoint unsigned rational 2 定義圖像白點(white point/白點:在彩色分色、照相或攝影時作爲色彩平衡測量用途的參考點) 的色度(chromaticity). 如果圖像是用CIE標準照度 D65(著名的是 '光線/daylight'的國際標準), 這個值是 '3127/10000,3290/10000'.
0x013f PrimaryChromaticities unsigned rational 6 定義圖像的原始色度. 如果圖像使用 CCIR 推薦 709原始色度, 則這個值是 '640/1000,330/1000,300/1000,600/1000,150/1000,0/1000'.
0x0211 YCbCrCoefficients unsigned rational 3 當圖像的格式是 YCbCr(JPEG的格式), 這個值表示轉換成 RGB格式的一個常量. 通常, 這個值是'0.299/0.587/0.114'.
0x0213 YCbCrPositioning unsigned short 1 當圖像的格式是 YCbCr 並且使用 '子採樣/Subsampling'(色度數據的剪切值, 所有的數字相機都使用), 定義了subsampling 像素陣列的色度採樣點. '1'表示像素陣列的中心, '2' 表示基準點.
0x0214 ReferenceBlackWhite unsigned rational 6 表示黑點(black point)/白點 的參考值. 在YCbCr 格式中,前兩個值是 Y的黑點/白點, 下兩個值是 Cb, 最後兩個值是 Cr. 而在 RGB 格式中, 前兩個表示R的黑點/白點, 下兩個是 G, 最後兩個是 B.
0x8298 Copyright ascii string   表示版權信息
0x8769 ExifOffset unsigned long 1 Exif 子IFD的偏移量


Exif 子IFD使用的標籤

標籤號 標籤名 格式 組件數 描述
0x829a ExposureTime unsigned rational 1 曝光時間 (快門速度的倒數). 單位是秒.
0x829d FNumber unsigned rational 1 拍照時的光圈F-number(F-stop).
0x8822 ExposureProgram unsigned short 1 拍照時相機使用的曝光程序. '1' 表示手動曝光, '2' 表示正常程序曝光, '3' 表示光圈優先曝光, '4' 表示快門優先曝光, '5' 表示創意程序(慢速程序), '6' 表示動作程序(高速程序), '7'表示 肖像模式, '8' 表示風景模式.
0x8827 ISOSpeedRatings unsigned short 2 CCD 的感光度, 等效於 Ag-Hr 膠片的速率.
0x9000 ExifVersion undefined 4 Exif 的版本號. 用4個ASCII字符來存儲. 如果圖片是基於Exif V2.1的, 這個值是 "0210". 因爲它不是一個用NULL(0x00)來終結的字符串,所以這裏的類型是 'undefined'.
0x9003 DateTimeOriginal ascii string 20 照片在被拍下來的日期/時間. 使用用戶的軟件是不能被修改這個值的. 日期的格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共佔用20個字節. 如果數字相機沒有設置時鐘或者 數字相機沒有時鐘, 這個域使用空格來填充. 在Exif標準中, 這個標籤是可選的, 但是在 DCF中是必需的.
0x9004 DateTimeDigitized ascii string 20 照片被數字化時的日期/時間. 通常, 它與DateTimeOriginal(0x9003)具有相同的值. 數據格式是 "YYYY:MM:DD HH:MM:SS"+0x00, 一共佔用20個字節. 如果數字相機沒有設置時鐘或者 數字相機沒有時鐘, 這個域使用空格來填充. 在Exif標準中, 這個標籤是可選的, 但是在 DCF中是必需的.
0x9101 ComponentsConfiguration undefined   表示的是像素數據的順序. 大多數情況下RGB格式使用 '0x04,0x05,0x06,0x00' 而YCbCr 格式使用 '0x01,0x02,0x03,0x00'. 0x00:並不存在, 其他的對應關係爲 0x01:Y, 0x02:Cb, 0x03:Cr, 0x04:Red, 0x05:Green, 0x06:Bllue.
0x9102 CompressedBitsPerPixel unsigned rational 1 JPEG (粗略的估計)的平均壓縮率.
0x9201 ShutterSpeedValue signed rational 1 用APEX表示出的快門速度. 爲了轉換成原始的 'Shutter Speed'; 則先要計算2的ShutterSpeedValue次冪, 然後求倒數. 例如, 如果 ShutterSpeedValue 是 '4', 快門速度則是1/(24)=1/16秒.
0x9202 ApertureValue unsigned rational 1 拍照時鏡頭的光圈. 單位是 APEX. 爲了轉換成普通的 F-number(F-stop), 則要先計算出根號2 2 (=1.4142)的ApertureValue次冪. 例如, 如果ApertureValue 是 '5', F-number 就等於1.41425 = F5.6.
0x9203 BrightnessValue signed rational 1 被拍攝對象的明度, 單位是 APEX. 爲了從BrigtnessValue(Bv)計算出曝光量(Ev), 你必須加上 SensitivityValue(Sv).
Ev=Bv+Sv   Sv=log2(ISOSpeedRating/3.125)
ISO100:Sv=5, ISO200:Sv=6, ISO400:Sv=7, ISO125:Sv=5.32.
0x9204 ExposureBiasValue signed rational 1 照片拍攝時的曝光補償. 單位是APEX(EV).
0x9205 MaxApertureValue unsigned rational 1 鏡頭的最大光圈值. 你可以通過計算根號2的MaxApertureValue次冪來轉換成普通的光圈 F-number (跟ApertureValue:0x9202的處理過程一樣).
0x9206 SubjectDistance signed rational 1 到焦點的距離, 單位是米.
0x9207 MeteringMode unsigned short 1 曝光的測光方法. '0' 表示未知, '1' 爲平均測光, '2' 爲中央重點測光, '3' 是點測光, '4' 是多點測光, '5' 是多區域測光, '6' 部分測光, '255' 則是其他.
0x9208 LightSource unsigned short 1 光源, 實際上是表示白平衡設置. '0' 意味着未知, '1'是日光, '2'是熒光燈, '3' 白熾燈(鎢絲), '10' 閃光燈, '17' 標準光A, '18' 標準光B, '19' 標準光C, '20' D55, '21' D65, '22' D75, '255' 爲其他.
0x9209 Flash unsigned short 1 '0' 表示閃光燈沒有閃光, '1' 表示閃光燈閃光, '5' 表示閃光但沒有檢測反射光, '7' 表示閃光且檢測了反射光.
0x920a FocalLength unsigned rational 1 拍攝照片時的鏡頭的焦距長度. 單位是毫米.
0x927c MakerNote undefined   製造商的內部數據. 一些製造商如 Olympus/Nikon/Sanyo 等在這個區域中使用IFD 格式的數據.
0x9286 UserComment undefined   存儲用戶的註釋. 這個標籤允許使用兩字節的德字符或者 unicode. 前8 個字節描述的是字符集. 'JIS' 是日文 (著名的有 Kanji).
'0x41,0x53,0x43,0x49,0x49,0x00,0x00,0x00':ASCII
'0x4a,0x49,0x53,0x00,0x00,0x00,0x00,0x00':JIS
'0x55,0x4e,0x49,0x43,0x4f,0x44,0x45,0x00':Unicode
'0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00':Undefined
0x9290 SubsecTime ascii string   一些數字相機每秒能拍攝 2~30 張照片, 但是DateTime/DateTimeOriginal/DateTimeDigitized 標籤只能記錄到秒單位的時間. SubsecTime 標籤就是用來記錄秒後面的數據(微秒).
例如, DateTimeOriginal = "1996:09:01 09:15:30", SubSecTimeOriginal = "130", 合併起來的原始的拍攝 時間就是 "1996:09:01 09:15:30.130"
0x9291 SubsecTimeOriginal ascii string  
0x9292 SubsecTimeDigitized ascii string  
0xa000 FlashPixVersion undefined 4 存儲FlashPix 的版本信息. 如果圖像數據是基於 FlashPix formar Ver.1.0, 則這個值爲 "0100". 因爲它不是一個用NULL(0x00)來終結的字符串,所以這裏的類型是 'undefined'.
0xa001 ColorSpace unsigned short 1 定義色彩空間. DCF 圖像必須使用 sRGB 色彩空間因此這個值總是 '1'. 如果這個照片使用了 其他的色彩空間, 這個值是 '65535':未校準(Uncalibrated).
0xa002 ExifImageWidth unsigned short/long 1 主圖像的尺寸大小.
0xa003 ExifImageHeight unsigned short/long 1
0xa004 RelatedSoundFile ascii string   如果數字相機能夠紀錄圖像的音頻數據, 則表示音頻數據的名字.
0xa005 ExifInteroperabilityOffset unsigned long 1 表示這是一個擴展"ExifR98", 細節未知. 這個值經常是IFD格式的數據. 當前這兒有兩個 目錄項, 第一個是 Tag0x0001, 值是"R98", 下一個是 Tag0x0002, 它的值爲 "0100".
0xa20e FocalPlaneXResolution unsigned rational 1 表示CCD的像素密度. 如果你的相機是百萬像素的並且是用低分辨率(如VGA模式) 來拍攝照片, 這個值可以通過照片的分辨率來重新採樣. 在這種情況下, FocalPlaneResolution 就不是CCD的實際的分辨率.
0xa20f FocalPlaneYResolution unsigned rational 1
0xa210 FocalPlaneResolutionUnit unsigned short 1 FocalPlaneXResoluton/FocalPlaneYResolution的單位. '1' 表示沒有單位, '2'是英寸inch, '3' 表示釐米.

注 意:一些Fujifilm的數碼相機(如.FX2700,FX2900,Finepix4700Z/40i 等) 使用的值是 '3' 所以它的單位一定是 '釐米' , 但是它們的分辨率單位就變成'8.3mm?'(1/3in.?). 這是Fuji 的 BUG? 從Finepix4900Z 開始這個值就使用 '2' 了但仍然跟實際的值不吻合.
0xa215 ExposureIndex unsigned rational 1 跟ISOSpeedRatings(0x8827)一樣但是數據類型是 unsigned rational. 只有Kodak的數字相機使用 這個標籤來替代 ISOSpeedRating, 我不知道這是爲什麼(歷史原因?).
0xa217 SensingMethod unsigned short 1 表示圖像傳感器單元的類型. '2' 意味着這是一個芯片顏色區域傳感器, 幾乎所有的數字相機都 使用這個類型.
0xa300 FileSource undefined 1 顯示圖像來源. 值 '0x03' 表示圖像源是數字定格相機.
0xa301 SceneType undefined 1 表示拍攝場景的類型. 值 '0x01' 表示圖像是通過相機直接拍攝出來的.
0xa302 CFAPattern undefined   表示色彩過濾陣列(CFA) 幾何模式.
長度 類型 意義
2 short Horizontal repeat pixel unit = n
2 short Vertical repeat pixel unit = m
1 byte CFA value[0,0]

:

:

:

1 byte CFA value[n-1,0]
1 byte CFA value[0,1]

:

:

:

1 byte CFA value[n-1,m-1]

色彩過濾和CFA值之間的關係.
Filter Color Red Green Blue Cyan Magenta Yellow White
CFA value 0 1 2 3 4 5 6
 
R G
G B
例如, 普通的 RGB 過濾器使用左表的副本, 這個值是 '0x0002,0x0002,0x00,0x01,0x01,0x02'.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章