来る5月7日~9日(現地時間)、Google I/O 2019が開催される。 この期間中に、Googleの開発チームが"CameraX"なる新たなJetpackライブラリの紹介を行うとのことで全俺が騒然!

Learn how to get started with the new CameraX support library in Jetpack, which provides the best support across a wide selection of Android Camera hardware with minimal effort to integrate machine learning.

機械学習を利用するにあたって最小限の労力で、様々な種類のカメラと連携できるなにかしらのライブラリ、とのことであるが、その実力はいかほどか、我々取材班は android.googlesource.com へと飛んだ…

注意点

CameraXは未リリースである。正式リリース時にAPIが変わっている可能性が高いです。 よって本記事のコードをアテにして見積もりをするとか、ましてや実装をするということをしてはいけない。

この記事を書いている人は1年ほどカメラや動画を扱う案件に従事していたが、当時もそれほどマルチメディア関係はよくわかっていなかったし、現在は全く関係のない案件に関わっているため、ますますよくわかってない状態です。 間違いなどあれば適宜訂正したいと思いますが、一方で記載内容に関して責任は取りません。また今後のアップデートに追随することもないと思います(もっとわかりやすく書くことが出来る若者にゆだねたい)。

— 宣伝 —

現在私が所属している 株式会社オトバンク ではモバイルアプリエンジニアを募集しております。 具体的には、iOSやってるんだけどそろそろReact Nativeとかもやってフロントエンド領域をつよくしたいな~みたいな方であるとか、React Nativeやってるけどネイティブ領域にも比重寄せたいな~と考えている方とかどうでしょうか。

制度としてはいろいろあるが一番僕が気に入っているものとしてフレックス制をとっており、昼まで寝てから働きたいというような働き方も実際できます。

採用フォームはこちら

採用とかはともかくとしても、ちょっと話を聞いてみたい、まずはお友達から/// という方は @pside へDMをお願いします m(_ _)m

(ちゃんとしたrecruitingページ用意したいねえ)

以上、宣伝終わり。

検証環境

  • Android Studio 3.4
  • Emulator API 27
  • CameraX 1.0.0-alpha02 (とあるが絶賛開発中のためあてにしないほうがいいかも)

事前準備

現時点ではGoogle Mavenにライブラリがあがっていないので、自分でビルドをする。 以下を参考にやっていく。

allprojects {
    repositories {
        google()
        jcenter()
        maven {
            url "/path/to/checkout/out/support/build/support_repo/"
        }
    }
}
  • appレベルのbuild.gradleには以下のように書く。なおsyncしたタイミングでバージョンが変わるとかパッケージIDが変わるとかありそうなので参考程度に。
implementation 'androidx.camera:camera-camera2:1.0.0-alpha02'

検証雑感

検証コードはこちらに用意いたしました:

このタイミングでは内部実装を見ていないのでアレでしたが、Camera2 APIのみを使用しており、所謂Camera1は使っていません。 しかし、実装を見る限りではカメラAPIの実装とCameraX自体のAPI実装が注意深く分けられている(或いは分けることを意識している)ように見受けられました。

camera-camera2camera-core に依存しており、そのcore側にあるUseCaseクラスを継承しているのがこれら4つのクラスです。

  • ImageCapture
    • 写真を撮影するために使います
  • VideoCapture
    • 動画を撮影するために使います
  • Preview
    • プレビュー表示のために使います
  • ImageAnalysis
    • Firebase ML Kitなどの機械学習や連続像の処理をさせるために使います

ところでUseCaseという名前は、アーキテクチャのそれと同一名称のため、もうちょっと名前どうにかならんかなぁ、という気持ちがあります。

検証コードを見ていきます。

すべてのUseCaseは CameraX.bindToLifecycle() に登録することで使用できるようになります。 書き忘れしないようにしたいところです。 また、(現時点では)すべてのUseCaseのコンストラクタで{UseCaseName}Configという設定を与えますが、{UseCaseName}Config.Builder().build() といった具合にBuilderパターンで設定していきます。それぞれのUseCaseで与えることが出来るパラメータが異なっているので、利用する際は確認しておきたいところです。

ImageCaptureは写真撮影に特化したUseCaseです。 例えばActivityのメンバーなどでImageCaptureを保持しておき、CameraX.bindToLifecycle()に登録した状態で、imageCapture.takePicture() を実行するとファイルが作成される(か ImageProxy を得られる)という具合です。

VideoCaptureは動画撮影に特化したUseCaseです。 こちらは絶賛開発中のようで@RestrictedToが付けられていたため深追いしておりませんが、startRecording/stopRecordingを行うことで動画撮影ができます。

実は内部ではMediaRecorderを使わず、MediaCodecMediaMuxerを用いた実装となっています。 MediaRecorderはカメラを使う上で音声と映像を良い感じにやってくれるものの、録画開始のタイムラグがあったり(録画開始から数百ミリ秒遅れるため、録画時間に厳格な案件では要件を満たせない)と微妙に品質が良くない印象がありました。 MediaCodecMediaMuxerを用いた実装は、開発難度が高いものの、タイムラグはほぼ発生しないと考えられます。 このような実装を我々が享受できるのは非常に有難いことです。これだけでもリリースが待ち遠しいです。

Previewはプレビュー用のViewを実装するために使用するUseCaseです。 このように実装することで簡単に使うことができます:

private val previewView by lazy {
    findViewById<TextureView>(R.id.surfaceView)
}
...
preview.setOnPreviewOutputUpdateListener { output ->
    previewView.surfaceTexture = output.surfaceTexture
}

追記(2019/05/07): プレビューのアスペクト比(4:3とか16:9とか1:1とか)は、Previewのコールバックを直に使う場合に限っては我々自信で対応する必要があります。 つまり、上記のコードのように単に実装するだけだと引き延ばされた像が投影されます。 正しく表示させるなら、TextureViewのサイズを変えるとか、Rendererで調整する必要がありそうです。 後述するCameraViewでは、これらのサポートが入ると思います。 追記終わり。

しかしPreviewクラスはこれだけに留まらず、ズームやフォーカス、フラッシュライトを用いたトーチ機能などのOn/Offが設定できます。 また、現在は簡単に利用できる状態ではないものの、CameraViewが準備されているようです。 ExoPlayerのPlayerViewのような立ち位置のViewコンポーネントがあれば非常に楽にカメラ機能を組み込めるようになるかも知れません。

追記(2019/05/07): stsnさんがCameraViewについて調べてくれました

追記終わり。

ImageAnalysisはFirebase ML Kitなどの機械学習や連続像の処理をさせるために使います。 専門外のため詳細は別の方の解説を待ちたいですが、カメラが取り込んだ像が次々とコールされてゆきます。

imageAnalysis.setAnalyzer { image, rotationDegrees ->
    // Log.d("Main", "Analyzer: $image, $rotationDegrees")
    // some ml task here...
}

なおここでLogを仕込むとめっちゃやかましくなりエミュレーターは重くなるなど良くないことが起こる(つまりめっちゃたくさんコールバックされている)ので気をつけて。。。

現在、UseCaseは4つ実装されていますが、現時点でbindToLifecycleで受けつけることが出来るUseCaseは3つまでとなっています。理由は特に追っていませんがCamera2 APIの制約でそういうのがあったかも知れません。

いやはや、CameraライブラリがContentProviderで初期化をする時代なのか・・・と恐れ入ったわけですが、Camera2Initializerが内部でCameraX.init()を実行し、Factoryなどをstaticに登録することで、実際に必要になったタイミングでもCamera2の初期化がうまく動作する仕組みになっているようです。

また、先ほども述べましたがCameraXはOSのカメラAPIの実装を注意深く分離するよう実装されているため、Camera APIが今後バージョンが上がってCamera3などが出てきても、CameraXで吸収できるようになっています。 或いは、とんでもなくヤバい端末メーカーが独自のCameraAwesomeみたいなAPIを実装しても、CameraXで吸収できるはずです。

まだすぐ使えるような状態に整ってはいませんが、HDR撮影を行うためのExtensionなどが準備されているようです。 ところが想定されるプラットフォームは以下の通り定義されているようですが、主実装がまだないようで(実装するためのstubはある)、誰が実装するんだろう・・・?(つまりGoogleがそういうことを管理していくのか、メーカーにある程度好き勝手やらせるのか、ということ)。

Huawei (HDR, Portrait): Mate 20 series, P30 series, Honor Magic 2, Honor View 20
Samsung (HDR, Night, Beauty): Galaxy S10 series

ひとまず調査はここまで。 機械学習を触りやすくするためのAPIとしては、かなり使いやすくなっている気がします。 特にCamera2のコールバック地獄をうまく隠蔽できているのは素晴らしいと思います。

また、Instagramのようなフィルター加工をさせるような領域はTextureViewを操作したりする為、 そのあたりのViewについては現時点で未実装なためなんとも言えないところですが、 嫌なら自分でforkしてカスタマイズすればいいわけで、よいバランス感覚だと思います。 (例えば google/cameraviewnatario1/CameraView はViewからメソッドが生えている形式なので、一蓮托生であった…)

まとめ

ざっとまとめます。

  • CameraXは「機械学習の勉強をするはずがCamera2の勉強をしているでござるw」な状況をうまく回避できている
  • 今後の実装次第ではあるが純粋なAPIの置き換えとしてよくできているが課題もある
    • 課題1: いつリリースされるか今日(2019/05/06)の時点では不明(I/Oでセッションあるからそのタイミングで出るとは思うけど)
    • 課題2: 国内の意味不明なカメラ実装についてケアしてくれたりはしなさそう(カメラ動かして温度が50度に達したら崩壊する某夢の国端末とか)
  • 新規案件では、今後安定版がでたタイミングで機械学習を使うとか、ちょっと複雑な案件であるとか、そういうのであればぜひ使いたい
    • Camera2をいいかんじにラップしているAPIと捉えるだけでもだいぶ気持ちが良い
    • しかし、今、現時点においては、例えば natario1/CameraView のほうがずっとよく出来ていると思います
  • じゃあ既存の実装を置き換えるべきかどうか?は、「置き換えを検討するほどヤバいなら置き換えたほうが良いし、そうでなく安定してるなら置いとけばいい」
    • 話題のためだけに既存の実装をCameraXに置き換えるのは、よほど既存実装がしょぼいなら許容できるが、そうでないならもっと重要なことに時間を割くべきだと思う。例えばダンスをするとかさ

というわけでおしまい。

本記事を書くに当たって情報提供をしてくれたstsnさんに感謝。