引言
最近做一套基于Camera2 API的相機應(yīng)用剪廉,發(fā)現(xiàn)有關(guān)Camera2 手動點擊區(qū)域?qū)沟奈恼律僦稚偻扪瑹o奈痛苦研究了一天手動區(qū)域?qū)梗K于達到理想效果斗蒋。
展示一下效果:
有關(guān)Camera2 API的基本使用請自行Google捌斧,本文基于研究Camera2點擊對焦實現(xiàn)基礎(chǔ)之上,如有不足泉沾,歡迎指出捞蚂。
手動對焦基本原理
我們都應(yīng)該知道,手機改變焦距是通過分析攝像頭拍攝二維圖爆哑,并經(jīng)過算法根據(jù)對焦區(qū)域來測量光度情況進行對焦洞难。因此,Camera2中也提供了該對焦方式的API,我們只需提供基于Camera2所需的對焦區(qū)域(Rect)即可队贱。
Camera2通過不同的接口色冀,給應(yīng)用層提供了這方面的信息:
SENSOR_INFO_PIXEL_ARRAY_SIZE表示的是攝像頭成像區(qū)域所使用的內(nèi)存大小。
SENSOR_INFO_ACTIVE_ARRAY_SIZE表示真正接收光線的區(qū)域柱嫌,因此成像的區(qū)域是該參數(shù)指定的區(qū)域锋恬,當然該矩形區(qū)域的坐標系基于SENSOR_INFO_PIXEL_ARRAY_SIZE。
SCALER_CROP_REGION表示最終的輸出內(nèi)容是基于SENSOR_INFO_ACTIVE_ARRAY_SIZE裁剪的部分编丘,而該值指定裁剪的區(qū)域与学。
上圖灰色區(qū)域可以理解為通過 CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE拿到的區(qū)域,這個區(qū)域就是我們攝像頭拍攝照片的最大分辨率(比如我的三星s8嘉抓,最大分辨率為4032*3024)索守。
藍色區(qū)域是通過CaptureRequest.SCALER_CROP_REGION 拿到的部分,該部分經(jīng)測試與*SENSOR_INFO_PIXEL_ARRAY_SIZE *拿到的區(qū)域大小是一樣的抑片。
橘黃色區(qū)域和綠色區(qū)域分別代表我們的應(yīng)用界面裁剪區(qū)域卵佛。橘黃色一般為豎屏,綠色一般為橫屏敞斋。這個兩個部分的大小其實就是我們在設(shè)定相機預(yù)覽界面時的大小截汪,這個可以根據(jù)需要來設(shè)定。但這個區(qū)域顯示在手機屏幕的時候是等比縮放的植捎。所以我們在處理手指的點擊區(qū)域時衙解,我們需要根據(jù)預(yù)覽界面相對于相機取景最大分辨率進行調(diào)整觸摸坐標位置。
開始對焦
原理介紹完了焰枢,開始切入正題
首先我們需要知道蚓峦,手動對焦是通過CaptureRequest.Builder的CONTROL_AF_REGIONS(控制對焦區(qū)域)和CONTROL_AE_REGIONS(控制曝光區(qū)域)來控制的。上代碼(Kotlin):
mPreViewBuilder.apply{
set(CaptureRequest.CONTROL_AF_REGIONS, arrayOf(MeteringRectangle(rect, 1000)))
set(CaptureRequest.CONTROL_AE_REGIONS, arrayOf(MeteringRectangle(rect, 1000)))
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)
set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START)
}
可以見得手動對焦關(guān)鍵在于如何獲取上述代碼中的rect變量医咨。也就是相對于整個相機最大分辨率枫匾,手指的點擊區(qū)域架诞。繼續(xù)上代碼(在這里我們暫只考慮豎屏情況拟淮,橫屏情況參考注釋):
/**
* 獲取點擊區(qū)域
* @param x:手指觸摸點x坐標
* @param y: 手指觸摸點y坐標
*/
private fun getFocusRect( x: Int, y: Int): Rect {
val screenW = FontDisplayUtil.getScreenWidth()//獲取屏幕長度
val screenH = FontDisplayUtil.getScreenHeight()//獲取屏幕寬度
//因為獲取的SCALER_CROP_REGION是寬大于高的,也就是默認橫屏模式谴忧,豎屏模式需要對調(diào)width和height
var realPreviewWidth = mPreviewSize.height
var realPreviewHeight = mPreviewSize.width
//根據(jù)預(yù)覽像素與拍照最大像素的比例很泊,調(diào)整手指點擊的對焦區(qū)域的位置
val focusX = realPreviewWidth.toFloat() / screenW * x
val focusY = realPreviewHeight.toFloat() / screenH * y
LogUtil.i("focusX=$focusX,focusY=$focusY")
//獲取SCALER_CROP_REGION,也就是拍照最大像素的Rect
val totalPicSize = mPreViewBuilder!!.get(CaptureRequest.SCALER_CROP_REGION)
LogUtil.i("camera pic area size=$totalShowSize")
//計算出攝像頭剪裁區(qū)域偏移量
val cutDx = (totalPicSize.height() - mPreviewSize.height) / 2
LogUtil.i("cutDx=$cutDx")
//我們默認使用10dp的大小沾谓,也就是默認的對焦區(qū)域長寬是10dp委造,這個數(shù)值可以根據(jù)需要調(diào)節(jié)
val width = FontDisplayUtil.dip2px(10f)
val height = FontDisplayUtil.dip2px(10f)
//返回最終對焦區(qū)域Rect
return Rect(focusY.toInt(), focusX.toInt() + cutDx, (focusY + height).toInt(), (focusX + cutDx + width).toInt())
}
其中cutDx變量是屏幕比例相對于攝像頭剪裁區(qū)域的偏移量。因為在我的項目中屏幕是固定豎屏的均驶,也就是說我的屏幕高度與SCALER_CROP_REGION獲取的最大高度時相等的昏兆,所以只需要考慮寬度的調(diào)整「狙ǎ可以參考下圖整理一下思路...
至此爬虱,點擊對焦的大部分工作均已完成隶债。不要忘了在設(shè)置完mPreViewBuilder的手動對焦模式之后,使用setRepeatingRequest方法來實現(xiàn)畫面的連貫加載跑筝。
本文參考:http://www.reibang.com/p/76225ac72b56
如有不足死讹,歡迎指出,謝謝曲梗。