Pro Yearly is on sale from $80 to $50! »

Camera 2 APIはじめの一歩

D0a4d1da4644054751e3fa7fd023ad8d?s=47 mhidaka
February 19, 2016

Camera 2 APIはじめの一歩

DroidKaigi 2016 Day2 RoomC Camera2 API セッションの資料です。Camera2 APIのオーバービューではパイプラインやリクエスト、セッションといった概念から設計、コードを図をつかいながら解説します。

D0a4d1da4644054751e3fa7fd023ad8d?s=128

mhidaka

February 19, 2016
Tweet

Transcript

  1. DroidKaigi 2016 カメラアプリはじめの一歩 @mhidaka #DroidKaigi #DroidKaigiC 2/19/2016 1

  2. Camera2 API Overview 2/19/2016 2

  3. Camera2 API What’s new? Android 5.0 から追加された新しいパッケージ • android.hardware.camera2 Camera

    API を置き換える高性能なカメラ制御が特徴 • リクエストやセッションといった新しい概念 2/19/2016 3
  4. カメラアプリはじめの一歩 セッションの対象者 • Camera2 APIに触れたことがないひと • Camera1 APIとの違いを知りたいひと セッションのゴール •

    Camera2 APIの概念を理解する • サンプルコードを独力で読み、試行錯誤をしながら変更できる 2/19/2016 4
  5. 自己紹介 @mhidaka/日高 正博 エンジニア(組込) Android開発は 7年目 TechBooster 本を書いたり、技術同人誌を発行したり techbooster.org 2/19/2016

    5
  6. Target Platform Versions Camera 2 API Covered : 35% https://developer.android.com/intl/ja/about/dashboards/index.html

    2/19/2016 6
  7. Camera Use case Androidにおけるカメラは次の組み合わせ • Point & Shoot • DSLR

    • Effects • Innovations 2/19/2016 7
  8. FAQ Camera1 APIに価値はなくなったのか? • No, 利用価値はまだまだ高い • 枯れたAPI&たくさんのバッドノウハウ • This

    class was deprecated. Camera2 APIは時期尚早か? • No, Camera1 APIではできないことが可能に • モダンなAPI, 高い拡張性 2/19/2016 8
  9. If You Build Camera App アプリの一部、Optionとして開発する場合 • 工数はあまり消費したくない • 主機能は別にある

    • Camera2 APIは不要 • Intentによるカメラアプリ連携 • Google Play Services 7.8 – Mobile Vision API (QRコード) • それでも足りなければCamera1 APIを。 https://github.com/googlesamples/android-vision 2/19/2016 9
  10. If You Build Camera App アプリの主機能として開発する場合 • 一体化したユーザー体験を作りたい • 将来的な拡張を視野に入れたい

    • Camera2 APIを利用できる • Innovationsな分野であれば適用可能 • 1年後にはAndroid 5.x以上が70%に。 • つまり今日の知識は今後数年生き続ける 2/19/2016 10
  11. Camera2 API Workflow 2/19/2016 11

  12. Requestによる非同期モデル モードレス/パイプライン処理 Request Request Queue Camera Device Image Buffer Surface

    パイプライン 2/19/2016 12
  13. Requestによる非同期モデル ストリームを隠蔽したパイプライン処理により複数のリクエスト 実行が可能に Request Request Queue Camera Device Image Buffer

    Surface Request Request Request Result Result Result Surface Surface Surface パイプライン 2/19/2016 13
  14. RequestとSession Camera Deviceに対してキャプチャリクエストとセッションを発 行。Requestが操作、Sessionが操作の反映を担当 Request Session Camera Device リクエスト:操作 カメラパラメータの設定

    セッション:実行単位 複数のリクエストを受けることで 同時処理が可能 カメラデバイス:処理 結果はSurfaceへ 2/19/2016 14
  15. RequestとSession カメラプレビューの場合 • ※プレビュー中の静止画撮影は一旦プレビューをとめて実行する (カメラデバイスは1動作しかできない) Request Session Camera Device リクエスト:操作

    カメラプレビューを指定 セッション:実行単位 常時プレビューを行うため リクエストをRepeat実行 カメラデバイス:処理 結果はTextureViewへ 2/19/2016 15
  16. Camera2 Advantage 出力のSurface、ImageBufferの設定次第で自由な表現 • プレビュー表示:TextureView • 録画 :MediaRecorder • エンコードのみ:MediaCodec

    • YUV処理 :RenderScriptAllocation 2/19/2016 16
  17. 基本的な処理シーケンス 1. カメラデバイスを準備する 2. 準備の完了通知を受け取る 3. キャプチャリクエストを準備する 4. キャプチャセッションを準備する 5.

    セッションの状態通知を受け取る 6. キャプチャリクエストを発行する 7. カメラデバイスの利用を終了する 2/19/2016 17
  18. How to Use 2/19/2016 18

  19. Camera2 Sample いま公開したよ!! https://github.com/mhidaka/Camera2App/ 2/19/2016 19

  20. Camera 2 API Classes • CameraManager カメラのシステムサービス • CameraDevice カメラ制御

    • CaptureRequest 画像の取得リクエスト • CameraCaptureSession 画像を取得・処理するセッション 2/19/2016 20
  21. 基本的な処理シーケンス 1. カメラデバイスを準備する CameraManager#openCamera 2. 準備の完了通知を受け取る CameraDevice.StateCallback 3. キャプチャリクエストを準備する CaptureRequest.Builder

    4. キャプチャセッションを準備する CameraDevicecreateCaptureSession 5. セッションの状態通知を受け取る CameraCaptureSession.StateCallback 6. キャプチャリクエストを発行する CameraCaptureSession#capture 7. カメラデバイスの利用を終了する CameraDevice#close 2/19/2016 21
  22. 1.カメラデバイスを準備する 動作:TextureViewにプレビューを表示する CameraManager CameraManagerの主なメソッド 説明 open カメラの利用開始 getCameraIdList カメラのリストを取得(id) getCameraCharacteristics

    向きなどのパラメータ Close カメラの利用終了 2/19/2016 22
  23. private void openCamera(int width, int height) { if (checkSelfPermission(Manifest.permission.CAMERA) !=

    PackageManager.PERMISSION_GRANTED) { requestCameraPermission(); return; } String cameraId = setUpCameraOutputs(width, height); CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (!mCamera.isLocked()) { throw new RuntimeException( "Time out waiting to lock camera opening."); } manager.openCamera(cameraId, mCamera.stateCallback, mThread.getHandler()); } …error handling… } 2/19/2016 23
  24. Viewに依存する処理 • 表示サイズに依存してカメラ出力のアスペクト比を計算 • 表示サイズは画面サイズに依存 • カメラの出力サイズがデバイスごと固有 • 画面回転、再描画時でリサイズが必要 •

    TextureViewへの描画はImageBuffer経由で実施 • Camera2ではストリームとして画像を扱う(ファイルではない) • YUV表色系にするとやたら高速(BurstCaptureで顕著) 2/19/2016 24
  25. 2.カメラデバイスの状態変化を受け取る 非同期でカメラデバイスへの接続を検出する CameraDevise.StateCallbackインターフェイス StateCallbackインターフェイス 説明 onOpend カメラデバイスと接続が完了 onDisconnected カメラデバイスから切断された onError

    回復不能な状況 2/19/2016 25
  26. public class BasicCamera { private Semaphore mCameraOpenCloseLock = new Semaphore(1);

    private CameraDevice mCameraDevice; ...省略... public final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) { // カメラが利用可能状態になった mCameraOpenCloseLock.release(); mCameraDevice = cameraDevice; createCameraPreviewSession(); } 2/19/2016 26
  27. @Override public void onDisconnected(CameraDevice cameraDevice) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice =

    null; } @Override public void onError(CameraDevice cameraDevice, int error) { mCameraOpenCloseLock.release(); cameraDevice.close(); mCameraDevice = null; Log.e(TAG, "Camera StateCallback onError: Please Reboot Android OS"); } }; 2/19/2016 27
  28. 3,4,5リクエストとセッションを使う Request Session Camera Device CameraSessionに関する主なメソッド 説明 CameraDevice.createCaptureSession CaptureSessionの生成 CameraCaptureSession.setRepeatingRequest

    リクエストの繰り返し実行(プレビューなど) CameraCaptureSession.capture リクエストの実行 CameraRequestに関する主なメソッド 説明 CameraDevice.createCaptureRequest CaptureRequestBuilderの取得 CaptureRequest.Build.set AFなど撮影内容の設定 CaptureRequest.Build.build CaptureRequestの生成 2/19/2016 28
  29. mCameraDevice.createCaptureSession(Arrays.asList(surface, imageRenderSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession)

    { // プレビュー準備が完了したのでカメラのAF,AE制御を指定する mCaptureSession = cameraCaptureSession; try { // プレビューがぼやけては困るのでオートフォーカスを利用する mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 露光、フラッシュは自動モードを使用する mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); 2/19/2016 29
  30. // カメラプレビューを開始する(ここでは開始要求のみ) mPreviewRequest = mPreviewRequestBuilder.build(); mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mInterface.getBackgroundHandler()); } catch

    (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Log.e(TAG, "CameraCaptureSession onConfigureFailed"); } }, null ); 2/19/2016 30
  31. なぜこんなに非同期処理が多いのか • <del>つらすぎる</del> • ハードウェアデバイスという特性 • 入力に対して応答までの時差がかならずある • CameraManager#openCameraは500ms単位での処理(高コスト) •

    パイプラインの特性 • 複数リクエストをパイプラインで処理するには独立性を上げる • ResultはリクエストのQueuing時にはわからないので(仕方ない) 2/19/2016 31
  32. 6.キャプチャリクエストを発行する 静止画を撮影する CameraCaptureSession.StateCallback セッションの状態通知 CameraCaptureSession.CaptureCallback セッションでの処理通知 Camera Device Image Buffer

    File Save 2/19/2016 32
  33. CameraCaptureSession.CaptureCallback CaptureCallback 説明 onCaptureProgressed セッションのリクエスト処理結果 onCaptureCompleted キャプチャ(撮影)の完了 onCaptureProgressedメソッドは、リクエストの内容に応じて、 複数回呼び出されることがある点に注意 (CaptureRequest.Build.setをパラメータごと複数回指定できる

    から) 2/19/2016 33
  34. private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void

    onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult) { // キャプチャの進捗状況(随時呼び出される) process(partialResult); } @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // キャプチャの完了(プレビューの場合、プレビュー状態が継続) process(result); } } 2/19/2016 34
  35. カメラで撮影するということ 日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 を「撮影する」の一言で表現できる日本語がやばい。 2/19/2016 35

  36. プログラム的表現 2/19/2016 36

  37. 日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 2/19/2016 37

  38. private void process(CaptureResult result) { switch (mState) { case STATE_PREVIEW:

    { // プレビュー中は何もしない break; } case STATE_WAITING_LOCK: { // 焦点が合った時に静止画を撮影する(AF) Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState == null) { captureStillPicture(); } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) { // CONTROL_AE_STATE がnullのデバイスがある Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); if (aeState == null || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { mState = STATE_PICTURE_TAKEN; captureStillPicture(); } ...省略... 2/19/2016 38
  39. private void captureStillPicture() { try { // 静止画の撮影を開始する final CaptureRequest.Builder

    captureBuilder = mCameraDevice. createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(mInterface.getImageRenderSurface()); // 静止画の撮影モードを指定(AF,AE) captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 現在のカメラの向きを指定する(0~270度) int rotation = mInterface.getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); 2/19/2016 39
  40. カメラの撮影条件 カメラには、どうしても「オートフォーカスがあわない」など苦 手な対象物がある。ピントのずれた画像がとれたときは悲しいが、 撮影できないという状況が一番体験を損なう そのため、どんな状況でも撮影できる(とりあえず撮れる)こと がカメラとして使うには重要となる。 2/19/2016 40

  41. 日本語的表現 「プレビュー中に写真撮影ボタンを押したので、オートフォーカ スを合わせる。フォーカスが合ったときに写真の明るさ(露光) がちょうどよいところで静止画を取得して撮影が完了したら画面 をプレビュー中にもどす、そのときにフォーカスロックを解除す ることを忘れない」 2/19/2016 41

  42. private void captureStillPicture() { try { ...前のリストから継続... CameraCaptureSession.CaptureCallback CaptureCallback =

    new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { // 静止画撮影が完了した時に呼ばれるコールバック Log.e(TAG, "onCaptureCompleted Picture Saved"); // プレビュー用の設定に戻す unlockFocus(); } }; mCaptureSession.stopRepeating(); // プレビューを一旦停止する // 静止画を撮影する(captureBuilder) mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null); } 2/19/2016 42
  43. private void unlockFocus() { try { // AFのロックを解除する(トリガーをキャンセルする) mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);

    mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // AFトリガーのキャンセルを実行する mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mInterface.getBackgroundHandler()); // プレビューを継続するためsetRepeatingRequestメソッドを実行する mState = STATE_PREVIEW; mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mInterface.getBackgroundHandler()); } catch (CameraAccessException e) { e.printStackTrace(); } } 2/19/2016 43
  44. Camera2 API、複雑すぎませんか 間違いなく難しい。が、自由度は著しく向上している。カメラのス テートマシンはアプリの機能要件に依存が強く、性質が特異。きちん と使いこなすにはカメラの知識が必要 アプリで必要な満たすために、カメラ制御(非同期処理)とステート マシンを密結合するケースが多くなり、複雑度が加速度的に増す。 これは設計で回避できる問題でもある(解説でつかったサンプルでは ステートマシンとCamera2 APIの実装を別クラスに分けている。

    GoogleのCamera2Basicは密結合している) 2/19/2016 44
  45. Abyss of Camera2 2/19/2016 45

  46. https://source.android.com/devices/camera/index.html アプリケーション層 ハードウェア層 レイヤ同士はモジュール ごとにインターフェイス を使い、疎結合 2/19/2016 46

  47. Camera HAL3 Overview https://source.android.com/devices/camera/camera3.html METADATAを制御できる ことがCamera2の意義 METADATA:色空間や輝度などカメラのパラメータ 2/19/2016 47

  48. HAL subsystem ハードウェアを抽象化 ・レンズ、センサー フラッシュ ・動作モード ・YUV変換 https://source.android.com/devices/camera/camera3_requests_hal.html 2/19/2016 48

  49. Camera HAL3 Overview 内部ではRequestをみて 撮影フレームごとに 設定値を変更、処理に備える Captureのタイミングで キューイング ハードウェア処理 Requestをパイプライン

    (Aの準備をしつつBを実行) 処理できる Requestごとの設定 Surfaceへ書き出し 2/19/2016 49
  50. Tips • 3A Modes • AF,AE,AWの設定のこと • カメラデバイス起動時はオートフォーカス、自動露光、ホワイトバラ ンスいずれも”STATE_INACTIVE” •

    CTS requirements • Android 5.0 and later devices must pass both Camera API1 CTS and Camera API2 CTS. And as always, devices are required to pass the CTS Verifier camera tests. 2/19/2016 50
  51. まとめ 2/19/2016 51

  52. まとめ • Camera2 APIは今後の主流 • 多くの開発者はカメラの中身を作りたいわけではなく、カメラをつ かったあたらしい体験をつくりたい • カンタンを追求していくとCamera1 APIの形に落ち着く

    • 手間のかかる部分をサンプルとしてまとめて作って公開、多分誰かがよい抽 象化実装を作ると流行りだす • Camera2 API はCamera1 APIのrebase、rebuild • 近年のAndroid API(MediaCodec、BLE、NFCなど)は内部構造に深くアク セスできるようになっており、その流れの中では自然 • カメラという言葉には人を引きつける沼っぽさがある 2/19/2016 52
  53. @mhidaka rabbitlog@gmail.com Question ? / Thank You ! 2/19/2016 53