Android の Camera2 API を使って カメラのプレビュー画面を表示する
の続きです。
バーストモードをサポートしているか
下記のように判定する
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 として指定する。
それぞれのキャプチャー要求に対して、
CaptureCallback の onCaptureCompleted と
OnImageAvailableListener の onImageAvailable
が呼び出される。
1枚ごとに、キャプチャー要求のパラメータを変更することもできる。
その場合は、マニュアルモードにする。
ImageReader
通常の撮影では、JPEG 形式の ImageReader を使用する。
PEG 形式では、画像データを加工しているので、処理に 200 ms ほどかかる。
高速なバーストモードを実現するときは、YUV_420_888 形式のImageReaderを使用する。
処理時間は、画像の形式とサイズで異なる。
大まかな値は、getOutputStallDuration で取得できる。
reference : getOutputStallDuration
参考 stackoverflow : 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 では、アプリにて回転を補正する。
参考 stackoverflow : 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