Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

Camera X 1.3.0-alpha06で追加されたResolutionSelector APIを使ってみた

こんにちは。Androidエンジニアの宮本です。 マネーフォワード クラウド確定申告アプリの開発を担当しています。

本記事ではCamera Xライブラリの最新機能であるResolutionSelector APIを使った、既存APIの置き換えや高解像度撮影への対応方法について紹介します。

もしCamera Xライブラリについてまだご存知でない方は、私がDroidKaigi 2022で発表した「Camera Xライブラリの魅力と最新機能を紐解く」のセッションをぜひご覧ください。

はじめに

2023年4月19日にCamera Xライブラリのv1.3.0-alpha06がリリースされました。

このリリースでは新しくResolutionSelector APIというものが追加されており、カメラの解像度・アスペクト比の設定方法に関して変更が入っています。

また、以前のバージョンまででは実現できなかった一部端末での高解像度画像撮影が実現できるオプションが追加されています。

ResolutionSelector APIを使った解像度の設定

これまでカメラのプレビューや画像撮影の際にアプリケーション側で任意の解像度を設定するには、以下のように各UseCaseに対してsetTargetResolution()を使用していました。

val imageCapture = ImageCapture.Builder()
    .setTargetResolution(Size(1920, 1080))
    .build()

v1.3.0-alpha06ではこのAPIはdeprecatedになっています。

各UseCaseのBuilderにsetResolutionSelector()が追加されているので、この引数にResolutionSelectorを設定することで代替することができます。

初めにResolutionSelector APIを使った解像度の設定について見てみましょう。以下のようにsetResolutionStrategy()を使ってResolutionStrategyを設定します。

val imageCapture = ImageCapture.Builder()
    .setResolutionSelector(
        ResolutionSelector.Builder()
            .setResolutionStrategy(ResolutionStrategy(Size(1080, 1920), ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER))
            .build()
    )
    .build()

ResolutionStrategyboundSizefallbackRuleの2つのパラメータを持ちます。

boundSizeには使用したい解像度をSize型で指定します。

fallbackRuleboundSizeで指定した解像度がデバイスでサポートされていない場合に、代替の解像度を設定するかどうかを決定できます。

fallbackRuleに指定できる定数はいくつかあり、例えばFALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWERは指定した解像度が利用できない場合にそれに最も近く、かつ高い解像度が設定しようとします。もしその解像度が存在しないなどの理由で利用できない場合は他の最も低い解像度を選択しようとします。

setResolutionStrategy()を設定しない場合は、デフォルト値のResolutionStrategy.HIGHEST_AVAILABLE_STRATEGYが設定され、デバイスが使用可能な最大解像度が設定されます。 また、fallbackRuleのデフォルト値のFALLBACK_RULE_NONEでは代替の解像度を設定することなく、UseCaseがbindされたときにIllegalArgumentExceptionがthrowされます。

基本的にはFALLBACK_RULE_NONE以外の定数を指定してエラーが起きないようにするのが良いでしょう。

ResolutionSelector APIを使ったアスペクト比の設定

次にResolutionSelector APIを使ったアスペクト比の設定について見てみましょう。 これまでは以下のようにsetTargetAspectRatio()を使用していましたが、こちらもdeprecatedになっています。

val preiview = Preview.Builder()
    .setTargetAspectRatio(AspectRatio.RATIO_16_9)
    .build()

v1.3.0-alpha06からは以下のようにsetAspectRatioStrategy()を使ってAspectRatioStrategyを設定します。

val preiview = Preview.Builder()
    .setResolutionSelector(
        ResolutionSelector.Builder()
            .setAspectRatioStrategy(AspectRatioStrategy(AspectRatio.RATIO_16_9, AspectRatioStrategy.FALLBACK_RULE_AUTO))
            .build()
    )
    .build()

AspectRatioStrategypreferredAspectRatiofallbackRuleの2つのパラメータを持ちます。

preferredAspectRatioには使用するアスペクト比をAspectRatio.RATIO_4_3AspectRatio.RATIO_16_9で指定します。

fallbackRuleにはpreferredAspectRatioで指定したアスペクト比の解像度が一つもデバイスでサポートされていない場合に、代替のアスペクト比を設定するかどうかを決定できます。

FALLBACK_RULE_AUTOを指定すると別の最適なアスペクト比が設定されます。 FALLBACK_RULE_NONEを指定すると代替のアスペクト比が設定されることなく、UseCaseがbindされたときにIllegalArgumentExceptionがthrowされます。

setAspectRatioStrategy()を指定しない場合、デフォルトでAspectRatio.RATIO_4_3FALLBACK_RULE_AUTOが適用されるRATIO_4_3_FALLBACK_AUTO_STRATEGYが設定されます。

基本的にはRATIO_4_3_FALLBACK_AUTO_STRATEGYもしくはRATIO_16_9_FALLBACK_AUTO_STRATEGYを指定してエラーが起きないようにするのが良いでしょう。

デバイスが使用できる解像度の候補を制限する

setResolutionFilter()を使用することで、デバイスがサポートしている解像度を取得して、使用できる解像度の候補を制限することができます。

以下は1920×1080以上の解像度のみ利用できるように制限する例です。

val imageCapture = ImageCapture.Builder()
    .setResolutionSelector(
        ResolutionSelector.Builder()
            .setResolutionFilter { supportedSizes, rotationDegrees ->
                supportedSizes.filter { it.width >= 1080 && it.height >= 1920}
            }
            .build()
    )
    .build()

一部デバイスで実現できなかった高解像度画像撮影が可能になった

これまでCamera Xライブラリを使ったカメラアプリにおいて、一部のデバイスで撮影した画像の解像度が、プリインストールされているカメラアプリやCamera 2 APIで撮影された画像よりも低い事象が起きていました。

以下はとあるデバイスにてStreamConfigurationMap.getOutputSizes()StreamConfigurationmap.getHighResolutionOutputSizes()で出力した、サポートされている解像度のListです。

getHighResolutionOutputSizes : [4208x2368, 4208x2048, 4160x3120, 3120x3120]
getOutputSizes : [1920x1080, 1480x720, 1280x720, 1080x1080, 960x720, 720x720, 720x480, 640x480, 352x288, 320x240, 176x144]

このように特定の高解像度がgetHighResolutionOutputSize()でのみ取得できます。

これまでこれらの高解像度はCamera Xでは使用することができず、issue trackerにも起票されていましたが、setAllowedResolutionMode()が追加されたことで使用できるようになりました。

※ v1.3.0-alpha07でsetHighResolutionEnabledFlag()からsetAllowedResolutionMode()に名前が変更されています。

val imageCapture = ImageCapture.Builder()
    .setResolutionSelector(
        ResolutionSelector.Builder()
            .setAllowedResolutionMode(ALLOWED_RESOLUTIONS_SLOW)
            .build()
    )
    .build()

このようにALLOWED_RESOLUTIONS_SLOWを指定することでCamera Xが利用可能な解像度の候補にgetHighResolutionOutputSizes()で取得できる解像度が含まれるようになります。 デフォルトではALLOWED_RESOLUTIONS_NORMALが設定されています。

このフラグを有効にすることで撮影に時間がかかる場合があるため、ProgressIndicatorなどを表示して処理中であることをユーザーに伝えるなどすると良いでしょう。

おわりに

Camera X v1.3.0-alpha06で追加されたResolutionSelector APIについて調べたことをまとめました。

個人的にはDroidKaigi 2022で発表した時点ではできなかった高解像度撮影のサポートがようやく実現されて嬉しく感じています。

Camera XライブラリはCamera 2 APIと比較してとても使いやすいものになっているので可能なら乗り換えることをおすすめします。

GihHubでJetpack ComposeでCamera Xライブラリを使用したサンプルプロジェクトを公開していますので、参考になれば幸いです。

参考文献