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. Camera2 API What’s new? Android 5.0 から追加された新しいパッケージ • android.hardware.camera2 Camera

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

    Camera2 APIの概念を理解する • サンプルコードを独力で読み、試行錯誤をしながら変更できる 2/19/2016 4
  3. FAQ Camera1 APIに価値はなくなったのか? • No, 利用価値はまだまだ高い • 枯れたAPI&たくさんのバッドノウハウ • This

    class was deprecated. Camera2 APIは時期尚早か? • No, Camera1 APIではできないことが可能に • モダンなAPI, 高い拡張性 2/19/2016 8
  4. 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
  5. If You Build Camera App アプリの主機能として開発する場合 • 一体化したユーザー体験を作りたい • 将来的な拡張を視野に入れたい

    • Camera2 APIを利用できる • Innovationsな分野であれば適用可能 • 1年後にはAndroid 5.x以上が70%に。 • つまり今日の知識は今後数年生き続ける 2/19/2016 10
  6. RequestとSession Camera Deviceに対してキャプチャリクエストとセッションを発 行。Requestが操作、Sessionが操作の反映を担当 Request Session Camera Device リクエスト:操作 カメラパラメータの設定

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

    カメラプレビューを指定 セッション:実行単位 常時プレビューを行うため リクエストをRepeat実行 カメラデバイス:処理 結果はTextureViewへ 2/19/2016 15
  8. 基本的な処理シーケンス 1. カメラデバイスを準備する 2. 準備の完了通知を受け取る 3. キャプチャリクエストを準備する 4. キャプチャセッションを準備する 5.

    セッションの状態通知を受け取る 6. キャプチャリクエストを発行する 7. カメラデバイスの利用を終了する 2/19/2016 17
  9. Camera 2 API Classes • CameraManager カメラのシステムサービス • CameraDevice カメラ制御

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

    4. キャプチャセッションを準備する CameraDevicecreateCaptureSession 5. セッションの状態通知を受け取る CameraCaptureSession.StateCallback 6. キャプチャリクエストを発行する CameraCaptureSession#capture 7. カメラデバイスの利用を終了する CameraDevice#close 2/19/2016 21
  11. 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
  12. Viewに依存する処理 • 表示サイズに依存してカメラ出力のアスペクト比を計算 • 表示サイズは画面サイズに依存 • カメラの出力サイズがデバイスごと固有 • 画面回転、再描画時でリサイズが必要 •

    TextureViewへの描画はImageBuffer経由で実施 • Camera2ではストリームとして画像を扱う(ファイルではない) • YUV表色系にするとやたら高速(BurstCaptureで顕著) 2/19/2016 24
  13. 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
  14. @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
  15. 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
  16. 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
  17. // カメラプレビューを開始する(ここでは開始要求のみ) 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
  18. なぜこんなに非同期処理が多いのか • <del>つらすぎる</del> • ハードウェアデバイスという特性 • 入力に対して応答までの時差がかならずある • CameraManager#openCameraは500ms単位での処理(高コスト) •

    パイプラインの特性 • 複数リクエストをパイプラインで処理するには独立性を上げる • ResultはリクエストのQueuing時にはわからないので(仕方ない) 2/19/2016 31
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. まとめ • Camera2 APIは今後の主流 • 多くの開発者はカメラの中身を作りたいわけではなく、カメラをつ かったあたらしい体験をつくりたい • カンタンを追求していくとCamera1 APIの形に落ち着く

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