サービスに関する最新情報

ローライト ブーストでリアルタイムのカメラフィードを明るくする

所要時間: 7 分
Donovan McMurray さんのプロフィールを表示
Donovan McMurray デベロッパー リレーションズ エンジニア

先日、Instagram が夜間モードを使用して、暗い場所でも鮮明な写真を撮影できるようにした方法についてご紹介しました。この機能は、複数の露出を組み合わせて高品質の静止画を作成する時間がある静止画に最適です。しかし、写真と写真の間に起こる瞬間はどうでしょうか。ユーザーは、シャッター ボタンを押す瞬間だけでなく、カメラを操作する必要があります。また、プレビューを使用してシーンを構成したり、QR コードをスキャンしたりします。

今回は、リアルタイムのカメラ ストリームを明るくするために設計された強力な機能であるローライト ブースト(LLB)について詳しく説明します。夜景モードでは撮影中にカメラを静止させる必要がありますが、ローライト ブーストはライブ プレビューや動画撮影に瞬時に適用されます。LLB は、利用可能な光量に基づいて必要な明るさを自動的に調整するため、あらゆる環境に最適化されます。

最近のアップデートにより、Instagram ユーザーは LLB で完璧なショットを撮影できるようになりました。また、既存の夜間モードの実装により、1 年以上前からユーザーに好評だった、暗い場所でも高画質な写真を撮影できるようになりました。

リアルタイムの明るさが重要な理由

夜間モードは最終的な画質の向上を目的としていますが、ローライト ブーストは暗い環境での使いやすさとインタラクティブ性を目的としています。もう 1 つの重要な要素は、LLB と夜間モードは連携して機能するものの、それぞれ独立して使用できるということです。一部のユースケースでは、夜間モードの写真が必要ない場合でも、LLB 自体に価値があることがわかります。LLB がユーザー エクスペリエンスを向上させる仕組みは次のとおりです。

  • フレーミングと撮影の改善: 薄暗いシーンでは、標準のカメラ プレビューが真っ暗になることがあります。LLB はビューファインダーを明るくするため、ユーザーはシャッター ボタンを押す前に、フレームに収めているものを実際に確認できます。この場合、夜間モードを使用して低照度でも高品質の写真に仕上げるか、LLB を使用して「見たまま」の写真に仕上げるかを選択できます。
  • 信頼性の高いスキャン: QR コードはどこにでもありますが、暗いレストランや屋内駐車場でスキャンするのは、多くの場合、面倒な作業です。カメラフィードが大幅に明るくなることで、スキャン アルゴリズムは非常に暗い環境でも QR コードを確実に検出してデコードできます。
  • インタラクションの強化: ライブ動画インタラクションを伴うアプリ(AI アシスタントやビデオ通話など)では、LLB によって認識可能な情報量が増加し、コンピュータ ビジョン モデルが処理するのに十分なデータが確保されます。

Instagram の違い

LLB_IG_demo_white_background.gif

Android 版 Instagram アプリのエンジニアリング チームは、ユーザーに最先端のカメラ エクスペリエンスを提供するために、常に尽力しています。上の例では、Google Pixel 10 Pro で LLB がどれほど効果を発揮しているかがわかります。

lowlight.png

この違いがユーザー エクスペリエンスにどのような影響を与えるかは、容易に想像できます。撮影しているものがユーザーに表示されない場合、撮影を中断する可能性が高くなります。

lowlight1.png

実装の選択

ローライト ブーストを実装して、幅広いデバイスで最適なエクスペリエンスを提供するには、次の 2 つの方法があります。

  1. ローライト ブースト AE モード: ハードウェア レイヤの自動露出モードです。画像信号処理装置(ISP)パイプラインを直接微調整するため、最高の品質とパフォーマンスを実現します。最初に必ずこの点を確認してください。
  2. Google ローライト ブースト: デバイスが AE モードをサポートしていない場合は、Google Play 開発者サービスが提供するこのソフトウェア ベースのソリューションにフォールバックできます。カメラ ストリームに後処理を適用して明るくします。オール ソフトウェア ソリューションとして、より多くのデバイスで利用できるため、この実装により、LLB を使用してより多くのデバイスにリーチできます。

ローライト ブースト AE モード(ハードウェア)

メカニズム:
このモードは、Android 15 以降を搭載したデバイスでサポートされています。OEM が HAL でサポートを実装している必要があります(現在、Google Pixel 10 デバイスで利用可能)。カメラの画像信号処理装置(ISP)と直接統合されています。CaptureRequest.CONTROL_AE_MODE を CameraMetadata.CONTROL_AE_MODE_ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY に設定すると、カメラシステムが制御を行います。

動作:
HAL / ISP がシーンを分析し、センサーと処理のパラメータを調整して画像を明るくします。多くの場合、露光時間を長くします。これにより、デジタル センサーのゲイン(ISO)を上げるのではなく、露出時間を長くすることで、センサーがより多くの光情報をキャプチャできるため、信号対雑音比(SNR)が大幅に向上したフレームを生成できます。

メリット:
専用のハードウェア パスを使用するため、画質と電力効率が向上する可能性があります。

トレードオフ:
センサーが光を取り込むのに時間がかかるため、非常に暗い状況ではフレームレートが低下する可能性があります。光量が非常に少ない状況では、フレームレートが 10 FPS まで低下することがあります。

Google ローライト ブースト(Google Play 開発者サービスを介したソフトウェア)

メカニズム:
このソリューションは、Google Play 開発者サービスを介してオプション モジュールとして配布され、カメラ ストリームに後処理を適用します。HDRNet と呼ばれる高度なリアルタイム画像拡張技術を使用しています。

Google HDRNet:
このディープ ラーニング モデルは、低解像度で画像を分析して、コンパクトなパラメータ セット(バイラテラル グリッド)を予測します。このグリッドは、GPU でのフル解像度画像の効率的な空間変化の補正をガイドします。このモデルは、暗い場所での画質を明るく改善し、顔の視認性を高めるようにトレーニングされています。

プロセス オーケストレーション:
HDRNet モデルとその付随ロジックは、ローライト ブースト プロセッサによってオーケストレートされます。以下が該当します。

  1. シーン分析:
    カメラのメタデータ(センサー感度、露出時間など)と画像コンテンツを使用して、実際のシーンの明るさを推定するカスタム計算ツール。この分析により、ブーストレベルが決定されます。
  2. HDRNet 処理:
    HDRNet モデルを適用してフレームを明るくします。使用されるモデルは、低照度シーン向けにチューニングされ、リアルタイムのパフォーマンス向けに最適化されています。
  3. ブレンド:
    元のフレームと HDRNet で処理されたフレームがブレンドされます。適用されるブレンドの量は、シーンの明るさ計算ツールによって動的に制御され、ブースト状態と非ブースト状態の間のスムーズな移行が保証されます。
low-light-boost-processor-diagram.png

メリット:
特定の HAL サポートを必要とせず、より幅広いデバイス(現在は Samsung S22 Ultra、S23 Ultra、S24 Ultra、S25 Ultra、Google Pixel 6 ~ Google Pixel 9)で動作します。後処理エフェクトであるため、カメラのフレームレートを維持します。

トレードオフ:
後処理方法として、品質はセンサーから提供されるフレームに存在する情報によって制限されます。センサーレベルで極端に暗い場合に失われたディテールを復元することはできません。

ハードウェアとソフトウェアの両方のパスを提供する Low Light Boost は、Android エコシステム全体で低照度カメラのパフォーマンスを向上させるスケーラブルなソリューションです。デベロッパーは、利用可能な場合は AE モードを優先し、Google ローライト ブーストを堅牢なフォールバックとして使用する必要があります。

アプリにローライト ブーストを実装する

次に、両方の LLB サービスを実装する方法を見てみましょう。アプリで CameraX と Camera2 のどちらを使用している場合でも、以下の手順を実装できます。最適な結果を得るには、ステップ 1 とステップ 2 の両方を実装することをおすすめします。

ステップ 1: ローライト ブースト AE モード

Android 15 以降を搭載した一部のデバイスで利用できる LLB AE モードは、特定の自動露出(AE)モードとして機能します。

1. 在庫状況を確認する

まず、カメラデバイスが LLB AE モードをサポートしているかどうかを確認します。

val cameraInfo = cameraProvider.getCameraInfo(cameraSelector)
val isLlbSupported = cameraInfo.isLowLightBoostSupported

2. モードを有効にする

サポートされている場合は、CameraX の CameraControl オブジェクトを使用して LLB AE モードを有効にできます。

// After setting up your camera, use the CameraInfo object to enable LLB AE Mode.
camera = cameraProvider.bindToLifecycle(...)

if (isLlbSupported) {
  try {
    // The .await() extension suspends the coroutine until the
    // ListenableFuture completes. If the operation fails, it throws
    // an exception which we catch below.
    camera?.cameraControl.enableLowLightBoostAsync(true).await()
  } catch (e: IllegalStateException) {
    Log.e(TAG, "Failed to enable low light boost: not available on this device or with the current camera configuration", e)
  } catch (e: CameraControl.OperationCanceledException) {
    Log.e(TAG, "Failed to enable low light boost: camera is closed or value has changed", e)
  }
}

3. 状態をモニタリングする

モードをリクエストしたからといって、現在「ブースト中」であるとは限りません。システムは、シーンが実際に暗い場合にのみブーストを有効にします。Observer を設定して UI を更新する(月のアイコンを表示するなど)か、拡張関数 asFlow() を使用して Flow に変換できます。

if (isLlbSupported) {
  camera?.cameraInfo.lowLightBoostState.asFlow().collectLatest { state ->
    // Update UI accordingly
    updateMoonIcon(state == LowLightBoostState.ACTIVE)
  }
}

ローライト ブースト AE モードに関するガイドの全文はこちらをご覧ください。

Step 2: Google ローライト ブースト

ハードウェア AE モードをサポートしていないデバイスでは、Google ローライト ブーストが強力なフォールバックとして機能します。LowLightBoostSession を使用して、ストリームをインターセプトして明るくします。

1. 依存関係を追加する

この機能は Google Play 開発者サービスを介して提供されます。

implementation("com.google.android.gms:play-services-camera-low-light-boost:16.0.1-beta06")
// Add coroutines-play-services to simplify Task APIs
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.10.2")

2. クライアントの初期化

カメラを起動する前に、LowLightBoostClient を使用して、モジュールがインストールされていることと、デバイスがサポートされていることを確認します。

val llbClient = LowLightBoost.getClient(context)

// Check support and install if necessary
val isSupported = llbClient.isCameraSupported(cameraId).await()
val isInstalled = llbClient.isModuleInstalled().await()

if (isSupported && !isInstalled) {
    // Trigger installation
    llbClient.installModule(installCallback).await()
}

3. LLB セッションを作成する

Google LLB は各フレームを処理するため、ディスプレイ Surface を LowLightBoostSession に渡す必要があります。これにより、明るさ調整が適用された Surface が返されます。Camera2 アプリの場合、結果の Surface を CaptureRequest.Builder.addTarget() で追加できます。CameraX の場合、この処理パイプラインは CameraEffect クラスに最適です。このコードに示すように、SurfaceProcessor でエフェクトを適用し、SurfaceProvider でプレビューに返すことができます。

// With a SurfaceOutput from SurfaceProcessor.onSurfaceOutput() and a
// SurfaceRequest from Preview.SurfaceProvider.onSurfaceRequested(),
// create a LLB Session.
suspend fun createLlbSession(surfaceRequest: SurfaceRequest, outputSurfaceForLlb: Surface) {
  // 1. Create the LLB Session configuration
  val options = LowLightBoostOptions(
    outputSurfaceForLlb,
    cameraId,
    surfaceRequest.resolution.width,
    surfaceRequest.resolution.height,
    true // Start enabled
  )

  // 2. Create the session.
  val llbSession = llbClient.createSession(options, callback).await()

  // 3. Get the surface to use.
  val llbInputSurface = llbSession.getCameraSurface()

  // 4. Provide the surface to the CameraX Preview UseCase.
  surfaceRequest.provideSurface(llbInputSurface, executor, resultListener)

  // 5. Set the scene detector callback to monitor how much boost is being applied.
  val onSceneBrightnessChanged = object : SceneDetectorCallback {
    override fun onSceneBrightnessChanged(
      session: LowLightBoostSession,
      boostStrength: Float
    ) {
      // Monitor the boostStrength from 0 (no boosting) to 1 (maximum boosting)
    }
  }
  llbSession.setSceneDetectorCallback(onSceneBrightnessChanged, null)
}

4. メタデータを渡す

このアルゴリズムが機能するには、カメラの自動露出状態を分析する必要があります。キャプチャ結果を LLB セッションに返す必要があります。CameraX では、Camera2Interop.Extender.setSessionCaptureCallback() を使用して Preview.Builder を拡張することで、これを実現できます。

Camera2Interop.Extender(previewBuilder).setSessionCaptureCallback(
  object : CameraCaptureSession.CaptureCallback() {
    override fun onCaptureCompleted(
      session: CameraCaptureSession,
      request: CaptureRequest,
      result: TotalCaptureResult
    ) {
      super.onCaptureCompleted(session, request, result)
      llbSession?.processCaptureResult(result)
    }
  }
)

クライアントとセッションの実装手順の詳細については、Google ローライト ブースト ガイドをご覧ください。

次のステップ

この 2 つのオプションを実装することで、照明条件に関係なく、ユーザーがはっきりと確認し、確実にスキャンし、効果的に操作できるようにします。

これらの機能が本番環境に対応した完全なコードベース内でどのように動作するかを確認するには、GitHub の Jetpack Camera アプリをご覧ください。LLB AE モードと Google LLB の両方を実装しており、独自の統合のリファレンスとして利用できます。

作成者:
続きを読む