熱門:瘦小腿瘦小腿瘦小腿

  1. 首頁
  2. 科技日報
  3. 網路

分享一個困惑了我很久的知識點 | Exif

  • 小白兔

  • 2018-10-22 08:56:46

本文作者

作者:少年阿濤

連結:

https://juejin.im/post/5bc3fbcc5188255c672ed754

本文由作者授權釋出。

之前我在做相機的時候,知道部分裝置拍攝完成後預覽圖片會需要旋轉,並且該旋轉資訊儲存在 EXIF 中,網路上也有一些較為成熟的旋轉處理程式碼,很多時候貼過來用就行了,一直不清楚其原理,直到這篇文章,才讓我徹底弄清楚該如何通用的的處理旋轉!

1

問題

不知道大家有沒有遇到過這樣一個問題,安卓手機拍照預覽圖片是正常的,但是讀取拍照返回的圖片,卻發現圖片方向是錯的。恰好我就遇到這樣一個問題,圖片如下:

發現沒,圖片逆時針旋轉了九十度。這其實是一個叫Exif的東東在搞鬼。下面我們就來看看看看這個Exif究竟是何方神聖。

2

背景

首先,先要了解Exif是個什麼東東,搬出百度百科

可交換影象檔案格式(英語:Exchangeable image file format,官方簡稱Exif),是專門為數碼相機的照片設定的,可以記錄數碼照片的屬性資訊和拍攝資料。

說到底Exif就是一種格式,用來儲存圖片的一些資訊,這些資訊和我們日常比較相關的有拍攝裝置,拍攝地點,圖片尺寸等,不過今天的主角是另外一個——那就是圖片方向(orientation)。這個圖片方向不是指我們平時使用圖片編輯器旋轉的方向,而是拍照時手機的方向。

總共有八個方向:

下圖是JPEG ORIENTATION對應圖片方向的糾正演演算法,這裡它通過三位二進位制數代表八種方向,然後再通過每一位二進位制數對應不同的操作來對圖片進行糾正,如下:

https://magnushoff.com/jpeg-orientation.html

最高位二進位制數代表對角線翻轉的操作,第二位二進位制數代表旋轉180度的操作,最低位代表水平翻轉的操作。 例如001,就是水平翻轉,所以可以看到001的圖形和原圖形關於水平軸對稱。通過把八個方向的圖形用3個二進位制數即三種操作組合,就可以很方便的對圖形做轉換,編碼虛擬碼如下:

if( value& 100b != 0) image.flip- diagonally

if(value& 010b != 0) image.rotate-180

if(value& 001b != 0) image.flip-horizontally

那有人就會困惑了,自己怎麼平時沒有看到這種圖片呢,這是因為我們使用的圖片檢視器或者是瀏覽器對orientation做了相容,會對展示的圖片做轉換。

如下是windows資料夾的展示:

下面則是Android Studio的圖片展示

所以可以看到,windows是預設對圖片orientation做了處理,而Android的ImageView則沒有處理所以看到的是圖片本來的方向。

這是八個F的圖片連結

https://magnushoff.com/assets/test-exiforientation.zip

3

應用

在Android裡面,三星手機的拍照是個奇葩的存在,我上面用的手機就是三星手機,三星手機的exif是旋轉90度,別家手機則是0度,所以三星手機的照片需要做處理,這裡是一張三星手機照片的exif資訊:

三星手機的方向是Rotate 90CW,意思就是需要順時針方向(ClockWise)旋轉90度。 腦殼轉的快的同學可以對照上面的F圖,相信很快看出是101這張圖。

那我們取出圖片的orientation值進行驗證:

try{

valexifInterface = ExifInterface(resources.openRawResource(id))

valorientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)

Log.e( "orientation", orientation.toString())

} catch(e: IOException) {

e.printStackTrace()

}

列印結果是6,和上面的101對不上,其實在Android的orientation是需要做減1處理的,也就是說6其實對應的是101這種狀態。

另外,需要注意的是,如果列印結果是0,那麼說明圖片沒有orientation這個資訊。

那接下來我們進行編碼,這是第一張方式:

valoptions = BitmapFactory.Options()

varbitmap = BitmapFactory.decodeResource(resources, R.mipmap.error_orientation, options)

valmatrix = Matrix()

matrix.postRotate(getOrientation(R.mipmap.error_orientation).toFloat())

bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)

imageview.setImageBitmap(bitmap)

privatefungetOrientation(id:Int): Int{

vardegree = 0

try{

valexifInterface = ExifInterface(resources.openRawResource(id))

valorientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)

when(orientation) {

ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90

ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180

ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270

}

} catch(e: IOException) {

e.printStackTrace()

}

returndegree

}

一般來說,我們只需要處理這三種角度,上面三個角度對應的orientation是6 3 8,也就是101,010,111這三種狀態。為什麼一般只需要處理這三種狀態呢,自己腦補一下拿相機的角度,不外乎就四種情況,除了正常的情況下,不就只需要處理三種情況嗎?

嘿嘿,我真是個小機靈鬼。

當然,如果要嚴謹一點,還是需要按照JPEG那種操作方式來,如下:

valoptions = BitmapFactory.Options()

varbitmap = BitmapFactory.decodeResource(resources, R.mipmap.f7t, options)

valmatrix = genOrientationMatrix(R.mipmap.f7t)

bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)

imageview.setImageBitmap(bitmap)

privatefungenOrientationMatrix(id:Int): Matrix {

valmatrix = Matrix()

try{

valexifInterface = ExifInterface(resources.openRawResource(id))

varorientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)

if(orientation > 0) {

orientation--

if(orientation and 0b100 != 0) { //對角線翻轉

matrix.postScale( -1.0f, 1.0f)

matrix.postRotate( -90f)

}

if(orientation and 0b010 != 0) { //旋轉180度

matrix.postRotate( 180f)

}

if(orientation and 0b001 != 0) { //水平翻轉

matrix.postScale( -1.0f, 1.0f)

}

}

returnmatrix

} catch(e: IOException) {

e.printStackTrace()

}

returnmatrix

}

其實就是將JPEG對於orientation的轉換利用程式碼進行實現,對矩陣進行相應的變換。

4

總結

Exif是一種儲存了相片一些資訊的格式,平常我們在進行Android開發的時候,一般需要考慮方向的問題,但是在日常生活,這個也是暴露我們隱私的入口,所以手機在拍照的時候,最好將儲存位置這些選項關閉,避免洩漏自己的隱私。

這篇文章讓我們弄清楚了,當我們需要預覽一張照片時,不太清楚其需要選擇多少角度時,有了通用的做法:

讀取orientation 值,然後減1,轉化為3位的2進位制的值,例如101,根據二進位制的值進行轉化即可,每一位為1,則需要做固定的轉化,程式碼以及轉化規則都在上文。

推薦您的文章

其他文章