在移動端發展的過程中,相機設備對於推動移動設備創新起到了舉足輕重的作用,而相機曝光則是能夠拍攝出非凡品質照片的關鍵要素。在本文中,我將爲大家詳解移動端開發者在處理相機曝光時所遇到的挑戰。之後我會爲大家介紹新的 CameraX 曝光補償 (Exposure Compensation) API 如何幫助開發者應對這些挑戰,使快速拍攝高品質照片變得更加簡單。最後,我將爲大家展示如何在您的應用中使用該 API。
背景
在攝影中,曝光是決定最終相機所呈現圖像的最重要因素之一,因此大多數攝影師都極力將曝光調至最佳。自動曝光 (AE) 模式在大多數常見拍攝場景下的效果都不錯,比如自動快速拍照。然而在某些特定條件下,AE 模式會爲了照片的整體質量而做出一些妥協,而這些妥協有時並不是您想要的。其中一個例子就是逆光,例如,在室內朝向窗戶或者其它光源的位置拍攝,或者在室外拍攝,拍攝對象的後方剛好是太陽。在這些拍攝場景中,AE 模式會因爲明亮的背景 (照片中高亮的區域) 而對照片進行適當曝光,但是處於黑暗前景中的主體就會變得過於昏暗或者成爲剪影效果。圖 1 展示的是在相同條件 (室內逆光) 下使用不同曝光進行拍攝的三張照片。
AE 對於照片的整體質量控制得很好,但是更高的曝光確實能夠捕獲到 Android 公仔的更多畫面細節。
傳統操作中,攝影師通過調整如下三個參數來達到滿意的曝光:
- 光圈: 鏡片的打開程度,它控制相機的進光量
- 快門速度: 照片曝光的持續時間
- ISO (國際標準化組織): 相機感光元件對捕獲光線的敏感程度
每個參數對於圖像都有特殊的作用: 光圈可以調整景深效果,快門速度可以虛化運動的過程或者定格運動的瞬間,而更高的 ISO 會增加圖像噪點。其中用到相當精準的數學計算,但是這些參數設置的不同組合可以獲得相同的整體曝光效果,以及在這個過程中獲得高水準的完美曝光數值。爲了輔助補償曝光,CameraX 實現了曝光補償 API。
實現曝光補償 API
曝光補償 API 根據生成的自動曝光值,通過曝光補償指數調整曝光補償度。曝光補償指數可以爲正值 (用於提高照片亮度) 或者負值 (降低照片亮度)。它將整體曝光範圍映射到補償指數的取值區間,並將曝光總量映射到 EV 步長。相機設備內部會根據曝光指數改變曝光,減少控制其它參數的操作並且達到同一效果。
以圖 1 爲例,相機的默認配置無法爲我們希望對焦的主體提供足夠的亮度,所以我們可以使用正值指數的曝光補償配置來獲得兩倍或者四倍的曝光,從而突出主體。通過改變曝光補償指數,相機內部會控制快門速度、光圈和 ISO 以獲得合適的曝光。
要獲得補償指數,CameraX 實現了以下內容:
CameraControl::setExposureCompensationIndex,用於設置新的曝光補償指數。
ExposureState 用於曝光補償性能和當前配置,包括:
支持調整補償
所支持的補償範圍
所支持的補償步長
當前補償指數值
我們來看看在 CameraX 應用中如何使用 API 來創建曝光合適的照片。
使用曝光補償 API
要在應用中使用曝光補償 API,您需要完成如下工作:
- 查詢曝光補償範圍
- 設置新的曝光補償指數
- 當 CameraX 確認後繼續後續操作
查詢曝光補償指數的範圍
曝光補償值的範圍依賴 相機設備的配置和硬件級別;應用可以通過 ExposureState 接口查詢所支持的取值範圍。
val camera = cameraProvider.bindToLifecycle( … )
val range = camera.cameraInfo.exposureState.exposureCompensationRange
設置新的曝光補償指數
如果照片顯得較暗,在相機原本的修正曝光基礎上設置一個正值的曝光補償指數來提高亮度;同理,如果照片顯得過亮,則設置一個負值。這可以通過 setExposureCompensationIndex() 來實現:
interface CameraControl {
fun setExposureCompensationIndex(value: Int): ListenableFuture<Int!>
}
默認情況下,曝光補償指數的值從 "0" 開始,新的指數值必須在相機設備所支持的範圍內變化。否則 CameraX 會拋出 IllegalArgument
錯誤。請注意如果相機設備不支持曝光補償調節,CameraX 所返回支持的取值範圍是 [0,0]。
舉個例子,下述代碼中當用戶點擊了 UI 中的按鈕會提高曝光補償指數直到曝光指數達到所支持的最大值爲止:
var exposureIndex = 0
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
getCameraSelector(),
preview,
imageCapture,
)
evButton.setOnClickListener {
val range = camera.cameraInfo.exposureState.exposureCompensationRange
if (range.contains(exposureIndex + 1)) {
camera.cameraControl.setExposureCompensationIndex(++exposureIndex)
val ev = camera.cameraInfo.exposureState.exposureCompensationStep.toFloat() * exposureIndex
Log.i("CameraXLog", "EV: $ev")
}
}
setExposureCompensationIndex()
是 async
函數,每個相機只能響應一個未處理的請求。如果應用在之前的調用未被響應之前再次調用 setExposureCompensationIndex()
,新的調用請求就會替代之前的請求,而之前的請求會被取消並拋出 OperationCanceledException
。通常,新的指數值會被迅速啓用,輸出流的曝光會隨着 綁定用例 變化。如果應用需要知道特定請求的準確執行時間和結果,可以在函數返回的 ListenableFuture<Int> 之上註冊監聽器。
關聯補償指數和曝光值 (EV)
曝光值 (EV) 是一個用於表示曝光補償的光照度差異的度量單位。+1 EV 表示兩倍光照,+2 EV 是四倍光照,依此類推。應用可以將相同的曝光值應用到各種相機,甚至不同的設備,來獲得相似的曝光,但是應用不應該針對相同的目標效果直接使用相同的指數值,因爲每個指數值是相機的專屬值。
補償 API 中,曝光值是通過如下公式進行計算的:
曝光值 (EV) = exposure_compensation_index * compensation_step
上述公式中提到的 compensation_step 是曝光值可被修改的最小單位。請注意 compensation_step 也依賴相機設備。應用可以通過 ExposureState
查詢相機的 compensation_step,並且可以按照下面所示計算曝光值:
val camera = cameraProvider.bindToLifecycle( … )
val exposureValue = camera.cameraInfo.exposureState.exposureCompensationStep.toFloat() * exposureIndex
例如: exposure_compensation_index 等於 6,compensation_step 等於 ⅓,那麼曝光補償計算爲 +2 EV,也就是兩倍的默認曝光值。
compensation_step 的 step_size 取值通常爲 ⅓ 或者 ½,較少情況下,有些設備可能會支持 1 或者甚至 ¼。所能支持的最大曝光值一般是 2 EV 或者 3 EV。
CameraX 曝光補償 API 的可用性
曝光補償 API 是在 android.camera:camera-core:1.0.0-beta09
中首次以實驗方式發佈。
該 API 在版本 android.camera:camera-core:1.1.0-alpha06
中完成了實驗階段,成爲正式版本 CameraX API 的一部分。
要獲得最新的發佈版本,請查閱 官方 CameraX 版本說明。
總結
在 GitHub 上可以找到 CameraX 曝光補償的示例代碼。結合 CameraX API 的其它部分,曝光補償 API 可以幫助開發者實現富有創意的 Android 相機應用,並在移動應用的海洋中脫穎而出。如果您有興趣瞭解更多關於 CameraX 的內容,請參閱 官方文檔,如果希望能夠持續獲得 CameraX 的最新進展,可以加入 CameraX 討論組。歡迎大家提出寶貴意見,可以在 CameraX 討論組 中和我們交流,或者創建 CameraX issues 提交您遇到的問題。
相關閱讀
歡迎您 點擊這裏 向我們提交反饋,或分享您喜歡的內容、發現的問題。您的反饋對我們非常重要,感謝您的支持!