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

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

### RAW モード

一般的なデジカメやスマホは、撮影した画像をJPEG形式で保存する。
JPEG形式は、非可逆の圧縮方式なので、カメラ本来の画質より劣化する。
デジタル一眼などの専用機種では、
カメラ本来の画質で保存するRAW モードをサポートしている。

参考 : [RAWデータとは](https://silkypix.isl.co.jp/community/what-is-raw-data/)

### DNG フォーマット

RAW モードの画像保存は、カメラメーカーが独自のフォーマットを使用している。
カメラメーカーが提供する専用ソフトでないと、閲覧や編集することができない。
DNG 形式は、Adobeが推奨しているRAW モードの画像ファイルのフォーマットです。

参考 : [PhotoshopとDNGの将来](https://dc.watch.impress.co.jp/cda/other/2004/12/08/604.html)

### Android端末の対応

Android OS では、API 21 から RAW モードで撮影保存するAPIを提供している。
ただし、機種依存なのですべての端末で利用出来るわけではない。

画像保存は、DNG フォーマットを採用している。

一方で、DNG フォーマットを閲覧編集するAPIは、提供されていない。
そのため、標準の画像閲覧アプリでは、閲覧編集もできない。

Adobeから 写真加工・編集アプリ Lightroom Mobile の Android 版が提供されているので、それを利用する。

[Google Play : Adobe Lightroom](https://play.google.com/store/apps/details?id=com.adobe.lrmobile)

### 端末が RAW モードの撮影をサポートしているか

下記のように判定する。

```
contains(characteristics.get(
                                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES),
                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)
```

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

### Raw モードで撮影する

基本的な処理は、JPEG 形式の写真を撮影するときと同じです。
下記を参考のこと。

[Camera2 API を使って 写真を撮る](https://qiita.com/ohwada/items/9943deb61b8883b12aac)


### ImageReader

JPEG 形式のときは、カメラからの画像をキャプチャーするために ImageReader を生成した。

同様に、Raw モード用の ImageReader を生成する



```
        CameraCharacteristics  characteristics;
        ImageReader.OnImageAvailableListener rawImageAvailableListener;

        StreamConfigurationMap map = characteristics.get(
                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

// Raw モードの画像サイズを取得する
        Size largestRaw = Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.RAW_SENSOR)),
                        new CompareSizesByArea());

// ImageReader を生成する
        ImageReader imageReaderRaw =
                                ImageReader.newInstance( 
                                        largestRaw.getWidth(),
                                        largestRaw.getHeight(), ImageFormat.RAW_SENSOR, 
                                        4);
// リスナーを設定する
        imageReaderRaw.setOnImageAvailableListener(
                            rawImageAvailableListener, backgroundHandler);
```

[reference : ImageReader](https://developer.android.com/reference/android/media/ImageReader)

### DNG形式でファイルに保存する

JPEG形式のときは、ImageReader にキャプチャーされた Image から Bitmap を取得し保存した。

Raw モードでは、DngCreator を使って下記のように保存する。

```
    CameraCharacteristics characteristics;
    CaptureResult captureResult;
    Image image;
    File file;

    DngCreator dngCreator  = new DngCreator(characteristics, captureResult);

    try {
            FileOutputStream output = new FileOutputStream(file);
            dngCreator.writeImage(output, image);
    } catch (IOException e) {
            e.printStackTrace();
    }
```
[reference : DngCreator](https://developer.android.com/reference/android/hardware/camera2/DngCreator)

DngCreator の機能は、reference だけだとわかりにくい。
ソースコードも読んでおくといい。

[ソースコード : DngCreator.java](https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/hardware/camera2/DngCreator.java)

### Media Content Provider に登録する

Lightroom Mobile などの閲覧編集アプリから参照できるように、
画像ファイルを Media Content Provider に登録する。

 ```
        File file;
        MediaScannerConnection.MediaScannerConnectionClient mediaScannerConnectionClient;

        String[] paths = new String[]{ file.getPath() };
        MediaScannerConnection.scanFile(context, paths, null, mediaScannerConnectionClient );
```

[reference : scanFile](https://developer.android.com/reference/android/media/MediaScannerConnection.html#scanFile(android.content.Context,%20java.lang.String[],%20java.lang.String[],%20android.media.MediaScannerConnection.OnScanCompletedListener))

### JPEG画像との併用

公式サンプルでは、DNG 形式と同時に　JPEG 形式でも撮影保存している。

DNG 形式 と JPEG 形式 は 3264x2448 と画像のサイズは同じでも、
ファイルサイズは DNG 形式 はJPEG 形式に比べ 5〜6倍になる

> DNG : 3264x2448 15.44 MB
> JPEG : 3264x2448 2.8MB

DNG 形式 という高画質の画像があるので、
JPEG 形式はサムネイル的な小さい画像のサイズでもいい。
そこで、JPEG 形式でファイル保存するときに、画像サイズを小さくする。


```
    Bitmap  srcBitmap;
    int desiredSize;

    int bitmap_width = srcBitmap.getWidth();
    int bitmap_height = srcBitmap.getHeight();
    int max = Math.max(bitmap_width, bitmap_height);
    double scale = (double)desiredSize/ (double)max;
    int scaled_width =  (int) ( (double)bitmap_width * scale );
    int scaled_height =  (int) ( (double)bitmap_height * scale );
    Bitmap resized_bitmap = Bitmap.createScaledBitmap(srcBitmap,
        scaled_width, scaled_height, true);
```

[reference : createScaledBitmap](https://developer.android.com/reference/android/graphics/Bitmap.html#createScaledBitmap(android.graphics.Bitmap,%20int,%20int,%20boolean))

これにより、ファイルサイズは 12分の１に減少する。

> JPEG 原寸: 3264x2448 　2.8MB
> JPEG サムネイル: 480x640　243KB　

### Camera2Basic と Camera2Raw の違い

Rawモードの撮影は、上記のようにCamera2Basicをベースに、RAW用のImage Readerを追加すれば、いいのですが。
それ以外にも違いがあります。

Camera2Basic　を読み解くだけで、お腹いっぱいです。
 Camera2Raw　と読み比べしたら、パンクしそう。

### 　自動ホワイトバランス

Camera2Raw は、自動ホワイトバランスを設定している。
これは画質に大きく左右するので、納得です。
一方で、Camera2Basicでは、なぜ設定していないのか、疑問になる。
推測するに、その頃は対応している実機がなかったのかな。

### 　RefCountedAutoCloseable

imgereaderのリソース管理のための AutoCloseable のラッパークラス。
imgereaderをそのまま使うのではなく、このクラスでラップしている。
連続して撮影するときに、imgereader　を複数作成するので、
その管理のためと思われる。
今回は、ひとまずなしで。

### PendingUserCaptures

PICTUREボタンをクリックした数

連続して撮影するときに、すぐに実行せず、カメラデバイスの状態に応じて、順次処理するための変数。
今回は、ひとまずなしで。


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