Android の Camera2 API を使って バーストモードで撮影する

[Android の Camera2 API を使って カメラのプレビュー画面を表示する](https://qiita.com/ohwada/items/7bb65a566114066c8ced)
の続きです。

### バーストモードをサポートしているか

下記のように判定する

```
     int[] modes = characteristics.get(
                                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    contains(modes, CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
```


### バーストモードで撮影する

Camera2 APIには、バーストモードのための APIとして captureBurst が用意されている。

capture を繰り返し呼び出すことでも、同様な処理が行える。

captureBurst と capture の繰り返しの主な違いは、
captureBurst は他の要求と混在しないことを保証されていることです。

通常の capture では、キャプチャー要求のパラメータを
CaptureRequest で指定する。

captureBurst では、複数のキャプチャー要求をまとめて
List<CaptureRequest> として指定する。

それぞれのキャプチャー要求に対して、
CaptureCallback の onCaptureCompleted と
OnImageAvailableListener　の　onImageAvailable
が呼び出される。

１枚ごとに、キャプチャー要求のパラメータを変更することもできる。
その場合は、マニュアルモードにする。

[reference : captureBurst](https://developer.android.com/reference/android/hardware/camera2/CameraCaptureSession.html#captureBurst(java.util.List%3Candroid.hardware.camera2.CaptureRequest%3E,%20android.hardware.camera2.CameraCaptureSession.CaptureCallback,%20android.os.Handler))


### ImageReader

通常の撮影では、JPEG 形式の ImageReader　を使用する。

PEG 形式では、画像データを加工しているので、処理に 200 ms ほどかかる。

高速なバーストモードを実現するときは、YUV_420_888 形式のImageReaderを使用する。

処理時間は、画像の形式とサイズで異なる。
大まかな値は、getOutputStallDuration　で取得できる。


[reference : getOutputStallDuration](https://developer.android.com/reference/android/hardware/camera2/params/StreamConfigurationMap.html#getOutputStallDuration(int,%20android.util.Size))

参考 [stackoverflow : camera2-capture-burst-is-too-slow](
https://stackoverflow.com/questions/29265126/android-camera2-capture-burst-is-too-slow)

Nexsus5 では、下記の値となる。

|形式|サイズ|StallDuration (ms)|
|:------------:|:------------:|:------------:|
|JPEG|3264x2448|243|
|YUV_420|3264x2448|0|
|RAW_SENSOR|3280x2464|200|

### ファイルに保存する

通常の撮影と同様に、
onImageAvailable が呼び出されたときにファイルに保存する。

YUV_420_888 形式の ImageReader から JPEG 形式に変換して保存するには、
下記のようにする。


```
// YUV_420_888 形式のImageから NV21 形式のバイト列に変換する
    ByteBuffer buffer0 = image_yuv.getPlanes()[0].getBuffer();
    ByteBuffer buffer2 =  image_yuv.getPlanes()[2].getBuffer();
    int buffer0_size = buffer0.remaining();
    int buffer2_size = buffer2.remaining();
   byte[] bytes_nv21 = new byte[buffer0_size + buffer2_size];
    buffer0.get(bytes_nv21, 0, buffer0_size);
    buffer2.get(bytes_nv21, buffer0_size, buffer2_size);

// NV21 形式のバイト列から YuvImage に変換する
    int width = image_yuv.getWidth();
    int height = image_yuv.getHeight();
    YuvImage yuvImsge = new YuvImage(bytes_nv21, ImageFormat.NV21, width, height, null);

// YuvImage からに JPEG 形式のバイト列に変換する
    int width = yuvImsge.getWidth();
    int height = yuvImsge.getHeight();
    Rect rect = new Rect(0, 0, width, height);
    ByteArrayOutputStream output_stream = new ByteArrayOutputStream();
    yuvImsge.compressToJpeg(rect, 100, output_stream);
     byte[] bytes_jpeg = output_stream.toByteArray();

// JPEG 形式のバイト列をファイルに保存する
    File file;
    FileOutputStream out = new FileOutputStream(file);
    out.write(bytes_jpeg);
```

### 画像の回転を補正する

上記のようにファイルに保存すると、画像は90度 回転している

JPEG 形式の ImageReade では、
キャプチャー要求のパラメータに JPEG_ORIENTATION を指定すると、カメラのほうで、回転を補正する。
YUV_420_888 形式の ImageReade では、アプリにて回転を補正する。

[reference : JPEG_ORIENTATION](https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.html#JPEG_ORIENTATION)

参考 [stackoverflow : rotate-an-yuv-byte-array-on-android](https://stackoverflow.com/questions/14167976/rotate-an-yuv-byte-array-on-android)


```
// JPEG 形式のバイト列から Bitmap に変換する
    int length = bytes_jpeg.length
    Bitmap bitmap_orig = BitmapFactory.decodeByteArray(bytes_jpeg, 0, length);

// Bitmap を回転する
    int jpegOrientation
    Matrix matrix = new Matrix();
    matrix.postRotate(jpegOrientation);  
    int width = bitmap_orig.getWidth();
    int height = bitmap_orig.getHeight();
    Bitmap bitmap_rotate = Bitmap.createBitmap(bitmap_orig, 0, 0, width, height, matrix, true);
```
### バーストモードのファイル保存

ファイル保存は、処理に 5000 ms ほどの時間がかかる。

上記のように、onImageAvailable
が呼び出されるたびに　ファイル保存すると、バーストの間隔が長くなる。

高速なバーストモードを実現するときは、画像情報を一時保存しておき、
バーストが完了した時点でまとめてファイル保存する。

条件を変えて、バースト間隔の時間を実測した。
Nexsus5 では、下記の値となった。

|項番|形式|ファイル保存のタイミング|バースト間隔 (ms)|
|:------------:||:------------:|:------------:||:------------:|
|1|JPEG|onImageAvailable毎|270|
|2|RAW|onImageAvailable毎|580|
|3|YUV|onImageAvailable毎|100|
|4|YUV||完了した時点でまとめて|30|

サンプルコードをgithub に公開した。
https://github.com/ohwada/Android_Samples/tree/master/Camera218