首先,先要了解Exif是個什麼東東,搬出百度百科html
可交換圖像文件格式(英語:Exchangeable image file format,官方簡稱Exif),是專門爲數碼相機的照片設定的,能夠記錄數碼照片的屬性信息和拍攝數據。
說到底Exif就是一種格式,用來存儲圖片的一些信息,這些信息和咱們平常比較相關的有拍攝設備,拍攝地點,圖片尺寸等,不過今天的主角是另一個——那就是圖片方向(orientation)。這個圖片方向不是指咱們平時使用圖片編輯器旋轉的方向,而是拍照時手機的方向。總共有八個方向:git
下圖是JPEG ORIENTATION對應圖片方向的糾正算法,這裏它經過三位二進制數表明八種方向,而後再經過每一位二進制數對應不一樣的操做來對圖片進行糾正,以下:github
最高位二進制數表明對角線翻轉的操做,第二位二進制數表明旋轉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
以下是windows文件夾的展現:瀏覽器
下面則是Android Studio的圖片展現性能優化
因此能夠看到,windows是默認對圖片orientation作了處理,而Android的ImageView則沒有處理因此看到的是圖片原本的方向。
這是八個F的圖片連接。編輯器
在Android裏面,三星手機的拍照是個奇葩的存在,三星手機的exif是旋轉90度,別家手機則是0度,因此三星手機的照片須要作處理,這裏是一張三星手機照片的exif信息:post
三星手機的方向是Rotate 90CW,意思就是須要順時針方向(ClockWise)旋轉90度。
腦袋轉的快的同窗能夠對照上面的F圖,相信很快看出是101這張圖。
那咱們取出圖片的orientation值進行驗證:性能
try { val exifInterface = ExifInterface(resources.openRawResource(id)) val orientation = 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這個信息。
那接下來咱們進行編碼,這是第一張方式:
val options = BitmapFactory.Options() var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.error_orientation, options) val matrix = Matrix() matrix.postRotate(getOrientation(R.mipmap.error_orientation).toFloat()) bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) imageview.setImageBitmap(bitmap) private fun getOrientation(id:Int): Int { var degree = 0 try { val exifInterface = ExifInterface(resources.openRawResource(id)) val orientation = 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() } return degree }
通常來講,咱們只須要處理這三種角度,上面三個角度對應的orientation是6 3 8,也就是101,010,111這三種狀態。爲何通常只須要處理這三種狀態呢,本身腦補一下拿相機的角度,不外乎就四種狀況,除了正常的狀況下,不就只須要處理三種狀況嗎?嘿嘿,我真是個小機靈鬼。
固然,若是要嚴謹一點,仍是須要按照JPEG那種操做方式來,以下:
val options = BitmapFactory.Options() var bitmap = BitmapFactory.decodeResource(resources, R.mipmap.f7t, options) val matrix = genOrientationMatrix(R.mipmap.f7t) bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) imageview.setImageBitmap(bitmap) private fun genOrientationMatrix(id:Int): Matrix { val matrix = Matrix() try { val exifInterface = ExifInterface(resources.openRawResource(id)) var orientation = 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) } } return matrix } catch (e: IOException) { e.printStackTrace() } return matrix }
其實就是將JPEG對於orientation的轉換利用代碼進行實現,對矩陣進行相應的變換。
Exif是一種存儲了相片一些信息的格式,日常咱們在進行Android開發的時候,通常須要考慮方向的問題,可是在平常生活,這個也是暴露咱們隱私的入口,因此手機在拍照的時候,最好將保存位置這些選項關閉,避免泄漏本身的隱私。