CameraX 1.1 有哪些新的特性發布?

語言: CN / TW / HK

CameraX 是一個 Jetpack 支持庫,旨在幫助您簡化相機應用的開發工作。它提供一致且易用的 API 接口,適用於大多數 Android 設備,並可向後兼容至 Android 5.0 (API 級別 21)。我們將在本文中介紹 CameraX 1.1 的多項功能,比如視頻功能。

如果您更喜歡通過視頻瞭解此內容,請 點擊此處 查看。

CameraX 概覽

CameraX 是一個為了簡化編寫相機應用而設計的支持庫,它所提供的高級 API 可以讓開發者專注於和用户交互而非相機的內部實現。我們一直在探索並修復其背後複雜的兼容性問題,讓每個新版本都得以在更多的設備上穩定運行。

何時使用 CameraX 或 Camera2,這取決於您期望更快的開發速度或是想要更高的自定義程度。

  • CameraX 可以很方便地實現普通照片視頻的拍攝功能,而 Camera2 則可以對拍攝流程進行特殊控制,例如實現多重曝光或全手動捕獲;
  • CameraX 旨在消除不同設備間的差異並在不同設備上進行了測試,而 Camera2 則需要應用來管理不同設備間的差異並測試其行為;
  • CameraX 提升了代碼開發速度,讓您更專注於用户界面和體驗流程,而 Camera2 則用於更深入地開發以創造基於相機的定製功能;
  • CameraX 發佈新版本頻繁,而 Camera2 則隨着 Android 的版本而更新;
  • CameraX 可以在您不熟悉相機的情況下也能夠進行開發,而 Camera2 則需要您對相機的專業知識有更深層次的瞭解。

CameraX 基於主要的使用場景來構建,比如實時預覽相機、檢索緩衝區以進行分析和拍攝照片,在 CameraX 1.1 版本中還加入了視頻拍攝功能。我們來看一個簡單的 CameraX 示例:

```Kotlin fun bindPreview(cameraProvider : ProcessCameraProvider) { // 使用 CameraX 創建 Preview 用例 var preview : Preview = Preview.Builder().build()

// 創建 cameraSelector,它會在設備上搜索所需的相機
var cameraSelector : CameraSelector = CameraSelector.Builder()
    // 在本例中,我們選擇搜索後置相機
    .requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

// 從 CameraX 的 CameraView 包中獲取 previewView 的句柄
// 利用此方法可以輕鬆的將相機內容添加到視圖上
preview.setSurfaceProvider(previewView.getSurfaceProvider())

// 將 preview 與其生命週期綁定
var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner,
                            cameraSelector, preview)

} ```

△ CameraX 代碼示例

CameraX 是生命週期感知型組件,這意味着它將自動處理應用的生命週期事件來實現開始、停止、暫停和恢復。現在,應用啟動時屏幕上便會顯示實時預覽。

我們已於 2021 年 5 月發佈了 1.0 穩定版本,目前正在開發 1.1 Alpha 版本並且很快將會進入 Beta 階段。並且我們一如既往地不斷為新增設備推出兼容性修復程序,例如 1.0.1 和 1.0.2。

在 CameraX 1.1 版本中我們新增了開發者呼聲很高的功能,具體而言,在本文中我們將重點介紹:

  • 視頻拍攝
  • YUV 到 RGB 的轉換
  • Beta 版 Extensions API
  • 一些需要了解的其它功能

視頻拍攝

在 CameraX 1.1 版本中我們加入了視頻拍攝功能,視頻拍攝 API (尚處於 Alpha 階段,細節可能會發生變化,但整體結構基本會保持不變) 提供了錄製到文件等基本功能、可自動適配每台設備的 Quality Setting API,以及 Lifecycle Management API。接下來我們先來了解如何設定視頻拍攝功能,代碼示例如下:

```Kotlin // 創建 Recorder val recorder = Recorder.Builder() // 我們可以在此處使用 setQualitySelector 設置視頻質量 .setQualitySelector(...) .build()

// 使用新創建的 Recorder 創建 VideoCapture val videoCapture = VideoCapture.withOutput(recorder)

// 將其與生命週期綁定 cameraProvider.bindToLifecycle( this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)

// 設定 VideoRecordEvent 監聽器 val videoRecordEventListener = Consumer{ when (it) { is VideoRecordEvent.Start -> {} is VideoRecordEvent.Finalize -> {} is VideoRecordEvent.Status -> { // status 事件將會在錄製時持續更新 val stats: RecordingStats = it.recordingStats // RecordingStats 中包含錄製文件的尺寸和時長 } is VideoRecordEvent.Pause -> {} is VideoRecordEvent.Resume -> {}

// 指定輸出 val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI) .setContentValues(contentValues) .build()

// 準備錄製 val activeRecording = videoCapture.output.prepareRecording(this, mediaStoreOutput) // 關聯事件監聽器 .withEventListener(ContextCompat.getMainExecutor(this), videoRecordEventListener) // 啟用音頻 (前提是此應用已獲得音頻權限) .withAudioEnabled() // 開始錄製 .start() ```

△ 視頻拍攝示例

videoCapture 會在應用啟動時就緒,應用可以使用 videoRecordEventListener 響應開始、結束、暫停和恢復等拍攝事件,其中 Status 事件會提供包括文件大小和持續時間的 RecordingStats。視頻拍攝可以輸出到 File、FileDescriptorMediaStore,在本例中我們選擇 MediaStore。如果選擇啟用音頻,則需要此應用已經獲得音頻權限。調用 start() 開始錄製為我們提供了 activeRecording 句柄,它可以用來暫停、恢復或停止錄製。您可以在 1.1 版本中試用這些 API。

YUV 至 RGB 的轉換

另一個呼聲很高的功能是 YUV 到 RGB 的轉換,我們來了解一下此功能。

△ YUV 格式 (圖左) 轉換至 RGB 格式 (圖右)

相機通常以 YUV420 格式生成數據,其中包括明亮度 (Luminance, Y)、色度 (Chroma, U, V) 和一些填充字節以將各行與有效的內存步幅對齊。但是這種格式的圖像處理起來可能很麻煩,而現在 CameraX 可以將 ImageAnalysis 的輸出轉換為大家更熟悉的 RGBA 以方便處理。接下來我們看一個示例:

Kotlin val imageAnalysis = ImageAnalysis.Builder() .setTargetResolution(Size(1280, 720)) .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build()

△ 從 ImageAnalysis 獲取 RGB 輸出

在示例代碼中,我們創建了 ImageAnalysis 實例,為圖像緩衝區指定了所需的分辨率和背壓策略,並調用新的 setOutputImageFormat 方法以請求 RGBA 8888 格式的輸出。現在,ImageAnalysis輸出的幀為 RGBA 8888 數據而不再是 YUV 格式。

CameraX 中 YUV 到 RGB 的轉換基於 libyuv。此外,在 CameraX 1.1 版本中數據本身可以轉換到目標分辨率。在中端設備上對圖像大小為 640x480 至 1080p 的數據進行轉換大約需要 5~10 毫秒,具體性能因設備而異。此外 APK 會略微增加 50KB 左右。

修復單像素漂移

YUV 轉換還修復了部分設備上存在的單像素漂移問題。在這些設備上,YUV 輸出經過桶形移位一個像素,導致最右邊的一列數據出現在圖像的左邊緣。在已知會發生這種情況的設備上,進行 YUV 到 RGB 的轉換及輸出 YUV 或 RGB 都會被修復,並且 CameraX 將會持續對更多有需要的設備進行修復。

△ 修復單像素漂移

如需瞭解更多,請參閲我們之前的推文《為 CameraX ImageAnalysis 進行 YUV 到 RGB 的轉換》。

CameraX Extensions API

相機特效

在 CameraX 1.1 中的 CameraX Extensions API 可以更為充分地發揮設備強大的功能。

CameraX Extensions 包括一些最常見的內置相機特效:

  • BOKEH (焦外虛化) : 在人像模式下拍攝照片時,讓前景人物更清晰。
  • HDR (高動態範圍) : 拍照時使用不同的自動曝光 (AE) 配置,以獲得最佳效果。
  • NIGHT (夜間) : 在低照度環境下 (通常是在夜間) 捕獲最佳靜態圖像。
  • FACE RETOUCH (臉部照片修復) : 拍攝靜態圖像時,修飾臉部膚色、輪廓等。
  • AUTO (自動) : 根據周圍的景色自動調整最終圖像。

我們來看看如何使用 CameraX Extensions API:

``` // 獲取後置相機列表 val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

// 檢查所有的後置相機中是否有支持焦外虛化 if (extensionsManager.isExtensionAvailable( cameraProvider, cameraSelector, ExtensionMode.BOKEH )) { // 創建擴展 cameraSelector,我們提供了相機並指定焦外虛化模式 // 它將開始在後台搜索支持焦外虛化的後置相機 val bokehCameraSelector = extensionsManager.getExtensionCameraSelector( cameraProvider, cameraSelector, ExtensionMode.BOKEH )

// 創建 imageCapture 和 preview
val imageCapture = ImageCapture.Builder().builder()
val preview = Preview.Builder().build()

// 使用 bokehCameraSelector 將它們綁定到生命週期
cameraProvider.bindToLifecycle(lifecycleOwner,
    bokehCameraSelector,
    imageCapture,
    preview
)

} ```

△ 以 BOKEH 效果捕捉並預覽圖像

在上面的例子中,imageCapture 輸出的圖像將會具有焦外虛化效果,如果設備支持的話,preview 也將預覽焦外虛化效果。

如需詳細瞭解,請參閲我們之前的推文《使用 CameraX Extensions API 將特效應用到照片上》。

曝光補償

CarmeraX 1.1 中還添加了曝光補償 API,此功能可以幫助用户更好地捕捉過度曝光或者曝光不足的區域。

如圖所示我們所處的場景窗外很明亮而室內很昏暗,此時則可以調整曝光補償來更好地捕捉明亮的室外或昏暗的室內場景。我們來看一個例子:

```Kotlin // 創建變量來跟蹤 exposureIndex 值 var exposureIndex = 0 // 使用 cameraSelector 將 imageCapture 和 preview 綁定到生命週期 val camera = cameraProvider.bindToLifecycle( lifecycleOwner, getCameraSelector(), preview, imageCapture )

// 為視圖中的按鈕添加點擊事件 evButton.setOnclickListener {

// 檢查有效的範圍以防止可能的異常
val range = camera.cameraInfo.exposureState.exposureCompensationRange

if (range.contains(exposureIndex + 1)) {
    // 調用 camera.cameraControl 的 setExposureCompenstation() 方法來設置曝光補償
    camera.cameraControl.setExposureCompenstation(++exposureIndex)
    // 使用 exposureCompensationStep 來實現從 index 到 EV 到轉換
    val ev = camera.cameraInfo.exposureState.exposureCompensationStep.toFloat() * exposureIndex
    Log.i("CameraXLog", "EV: $ev")
}

} ```

△ 通過按鈕調整曝光

其中 exposureIndex 是一個與設備無關的數字,它將以硬件允許的最小步長遞增或遞減曝光值,因此可以在不同的設備上以類似的方式運作。如果您想向用户展示 EV 值,可以獲取 exposureCompensationStep 來實現轉換。

如需瞭解 CameraX 中曝光補償 API 的應用背景和調用方法,請參閲我們之前的推文《CameraX 曝光補償 API 入門指南》。

平滑縮放

在 CameraX 1.1 中,我們還增加了平滑縮放功能。有一些設備有包括廣角和長焦在內的多個鏡頭,CameraX 可以檢測這些設備是否支持 SMOOTH_ZOOM 框架,在受支持的設備上使用 CameraX 的縮放控件時,會自動使用所有的相機來實現更大的縮放範圍。如果您已經在使用這個縮放控件,那當您使用 1.1 版本進行編譯時,您的應用應該就可以訪問這些設備上的所有相機。

CameraX 1.1 的更多功能

接下來介紹我們在 1.1 中添加的更多功能。

CameraState API 現在可以提供諸如另一個應用正在使用相機或者正處於勿擾模式等更多有關相機狀態的信息,使得應用能夠圍繞不同的相機時間來設計更好的界面和用户體驗流程。Image Analysis 現在可以提供超過 1080p 的圖像。Logging API 可以更詳細的調試日誌並改善了錯誤報吿。Coordinate Transformation API 可以將不同用例間的座標關聯起來,如果您在 imageAnalysis緩衝區中定位了興趣點,便可以方便地在圖像捕捉的輸出或預覽中輕鬆找到它。您可以使用 CameraFilter API 來指定詳細的規則以選擇合適的相機。如果應用只需要前置或者後置相機,可以使用 AvailableCamerasLimiter 來加快啟動時間。CameraControllerInfo 中可提供相機功能的更多詳細信息。

設備兼容性

CameraX 會持續關注設備兼容性,以便應用在眾多設備上都能夠良好運行。我們修復了很多諸如圖像拉伸、縮放不正確、圖像顛倒及關閉相機時意外輸出了綠色圖形等問題。每個 CameraX 的發佈版本或補丁版本中都會添加此類修復,最新的穩定版為 1.0.2。

您可以在 版本記錄 中看到每個版本中的詳細變更,還可以在 問題跟蹤器 中看已經修復的問題。

更多信息

希望對 CameraX 1.1 版本的簡要介紹對大家有所幫助,非常期待看到大家使用 CameraX 構建的功能!

歡迎您 點擊這裏 向我們提交反饋,或分享您喜歡的內容、發現的問題。您的反饋對我們非常重要,感謝您的支持!