一 問題
聽到小夥伴說有關審覈圖片的接口經常超時的時候,正好看了下這塊代碼。主要是從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 |
|
|||||||||||||||||||||||||||
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) 幾何模式.
色彩過濾和CFA值之間的關係.
|