Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Camera 2 APIはじめの一歩

mhidaka
February 19, 2016

Camera 2 APIはじめの一歩

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

mhidaka

February 19, 2016
Tweet

More Decks by mhidaka

Other Decks in Technology

Transcript

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

    View full-size slide

  2. Camera2 API Overview
    2/19/2016 2

    View full-size slide

  3. Camera2 API What’s new?
    Android 5.0 から追加された新しいパッケージ
    • android.hardware.camera2
    Camera API を置き換える高性能なカメラ制御が特徴
    • リクエストやセッションといった新しい概念
    2/19/2016 3

    View full-size slide

  4. カメラアプリはじめの一歩
    セッションの対象者
    • Camera2 APIに触れたことがないひと
    • Camera1 APIとの違いを知りたいひと
    セッションのゴール
    • Camera2 APIの概念を理解する
    • サンプルコードを独力で読み、試行錯誤をしながら変更できる
    2/19/2016 4

    View full-size slide

  5. 自己紹介
    @mhidaka/日高 正博
    エンジニア(組込)
    Android開発は 7年目
    TechBooster
    本を書いたり、技術同人誌を発行したり
    techbooster.org
    2/19/2016 5

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. FAQ
    Camera1 APIに価値はなくなったのか?
    • No, 利用価値はまだまだ高い
    • 枯れたAPI&たくさんのバッドノウハウ
    • This class was deprecated.
    Camera2 APIは時期尚早か?
    • No, Camera1 APIではできないことが可能に
    • モダンなAPI, 高い拡張性
    2/19/2016 8

    View full-size slide

  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

    View full-size slide

  10. If You Build Camera App
    アプリの主機能として開発する場合
    • 一体化したユーザー体験を作りたい
    • 将来的な拡張を視野に入れたい
    • Camera2 APIを利用できる
    • Innovationsな分野であれば適用可能
    • 1年後にはAndroid 5.x以上が70%に。
    • つまり今日の知識は今後数年生き続ける
    2/19/2016 10

    View full-size slide

  11. Camera2 API Workflow
    2/19/2016 11

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  14. RequestとSession
    Camera Deviceに対してキャプチャリクエストとセッションを発
    行。Requestが操作、Sessionが操作の反映を担当
    Request Session
    Camera
    Device
    リクエスト:操作
    カメラパラメータの設定
    セッション:実行単位
    複数のリクエストを受けることで
    同時処理が可能
    カメラデバイス:処理
    結果はSurfaceへ
    2/19/2016 14

    View full-size slide

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

    View full-size slide

  16. Camera2 Advantage
    出力のSurface、ImageBufferの設定次第で自由な表現
    • プレビュー表示:TextureView
    • 録画 :MediaRecorder
    • エンコードのみ:MediaCodec
    • YUV処理 :RenderScriptAllocation
    2/19/2016 16

    View full-size slide

  17. 基本的な処理シーケンス
    1. カメラデバイスを準備する
    2. 準備の完了通知を受け取る
    3. キャプチャリクエストを準備する
    4. キャプチャセッションを準備する
    5. セッションの状態通知を受け取る
    6. キャプチャリクエストを発行する
    7. カメラデバイスの利用を終了する
    2/19/2016 17

    View full-size slide

  18. How to Use
    2/19/2016 18

    View full-size slide

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

    View full-size slide

  20. Camera 2 API Classes
    • CameraManager カメラのシステムサービス
    • CameraDevice カメラ制御
    • CaptureRequest 画像の取得リクエスト
    • CameraCaptureSession 画像を取得・処理するセッション
    2/19/2016 20

    View full-size slide

  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

    View full-size slide

  22. 1.カメラデバイスを準備する
    動作:TextureViewにプレビューを表示する
    CameraManager
    CameraManagerの主なメソッド 説明
    open カメラの利用開始
    getCameraIdList カメラのリストを取得(id)
    getCameraCharacteristics 向きなどのパラメータ
    Close カメラの利用終了
    2/19/2016 22

    View full-size slide

  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

    View full-size slide

  24. Viewに依存する処理
    • 表示サイズに依存してカメラ出力のアスペクト比を計算
    • 表示サイズは画面サイズに依存
    • カメラの出力サイズがデバイスごと固有
    • 画面回転、再描画時でリサイズが必要
    • TextureViewへの描画はImageBuffer経由で実施
    • Camera2ではストリームとして画像を扱う(ファイルではない)
    • YUV表色系にするとやたら高速(BurstCaptureで顕著)
    2/19/2016 24

    View full-size slide

  25. 2.カメラデバイスの状態変化を受け取る
    非同期でカメラデバイスへの接続を検出する
    CameraDevise.StateCallbackインターフェイス
    StateCallbackインターフェイス 説明
    onOpend カメラデバイスと接続が完了
    onDisconnected カメラデバイスから切断された
    onError 回復不能な状況
    2/19/2016 25

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  31. なぜこんなに非同期処理が多いのか
    • つらすぎる
    • ハードウェアデバイスという特性
    • 入力に対して応答までの時差がかならずある
    • CameraManager#openCameraは500ms単位での処理(高コスト)
    • パイプラインの特性
    • 複数リクエストをパイプラインで処理するには独立性を上げる
    • ResultはリクエストのQueuing時にはわからないので(仕方ない)
    2/19/2016 31

    View full-size slide

  32. 6.キャプチャリクエストを発行する
    静止画を撮影する
    CameraCaptureSession.StateCallback
    セッションの状態通知
    CameraCaptureSession.CaptureCallback
    セッションでの処理通知
    Camera
    Device
    Image
    Buffer
    File
    Save
    2/19/2016 32

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  40. カメラの撮影条件
    カメラには、どうしても「オートフォーカスがあわない」など苦
    手な対象物がある。ピントのずれた画像がとれたときは悲しいが、
    撮影できないという状況が一番体験を損なう
    そのため、どんな状況でも撮影できる(とりあえず撮れる)こと
    がカメラとして使うには重要となる。
    2/19/2016 40

    View full-size slide

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

    View full-size slide

  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

    View full-size slide

  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

    View full-size slide

  44. Camera2 API、複雑すぎませんか
    間違いなく難しい。が、自由度は著しく向上している。カメラのス
    テートマシンはアプリの機能要件に依存が強く、性質が特異。きちん
    と使いこなすにはカメラの知識が必要
    アプリで必要な満たすために、カメラ制御(非同期処理)とステート
    マシンを密結合するケースが多くなり、複雑度が加速度的に増す。
    これは設計で回避できる問題でもある(解説でつかったサンプルでは
    ステートマシンとCamera2 APIの実装を別クラスに分けている。
    GoogleのCamera2Basicは密結合している)
    2/19/2016 44

    View full-size slide

  45. Abyss of Camera2
    2/19/2016 45

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  49. Camera HAL3 Overview
    内部ではRequestをみて
    撮影フレームごとに
    設定値を変更、処理に備える
    Captureのタイミングで
    キューイング
    ハードウェア処理
    Requestをパイプライン
    (Aの準備をしつつBを実行)
    処理できる
    Requestごとの設定
    Surfaceへ書き出し
    2/19/2016 49

    View full-size slide

  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

    View full-size slide

  51. まとめ
    2/19/2016 51

    View full-size slide

  52. まとめ
    • Camera2 APIは今後の主流
    • 多くの開発者はカメラの中身を作りたいわけではなく、カメラをつ
    かったあたらしい体験をつくりたい
    • カンタンを追求していくとCamera1 APIの形に落ち着く
    • 手間のかかる部分をサンプルとしてまとめて作って公開、多分誰かがよい抽
    象化実装を作ると流行りだす
    • Camera2 API はCamera1 APIのrebase、rebuild
    • 近年のAndroid API(MediaCodec、BLE、NFCなど)は内部構造に深くアク
    セスできるようになっており、その流れの中では自然
    • カメラという言葉には人を引きつける沼っぽさがある
    2/19/2016 52

    View full-size slide

  53. @mhidaka
    [email protected]
    Question ? / Thank You !
    2/19/2016 53

    View full-size slide