Newer
Older
SmTIAS-Capture / android / app / src / main / kotlin / com / example / smtias_capture / CaptureMetadata.kt
package com.example.smtias_capture

import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CaptureResult
import android.hardware.camera2.TotalCaptureResult
import android.media.Image
import android.os.Build
import java.time.Instant

/**
 * 定量モード撮影のメタデータ Map(meta.json の元データ)を構築するユーティリティ.
 *
 * 撮影設定([exposureTimeNs] 等は呼び出し元が適用した値)・実適用値(CaptureResult)・
 * センサ特性(CameraCharacteristics)・画素統計・Hot pixel map をまとめる.
 */
internal object CaptureMetadata {

    /** メタデータ Map を構築する. */
    fun build(
        cameraId: String,
        characteristics: CameraCharacteristics,
        captureResult: TotalCaptureResult,
        image: Image,
        exposureTimeNs: Long,
        sensitivity: Int,
        frameDurationNs: Long,
        imageStatistics: Map<String, Any?>? = null,
        hotPixelMap: Map<String, Any?>? = null,
    ): Map<String, Any?> {
        val settingsMap = mapOf<String, Any?>(
            "control_mode" to "OFF",
            "ae_mode" to "OFF",
            "exposure_time_ns" to exposureTimeNs,
            "sensor_sensitivity_iso" to sensitivity,
            "frame_duration_ns" to frameDurationNs,
            "awb_mode" to "OFF",
            "color_correction_gains" to listOf(1.0, 1.0, 1.0, 1.0),
            "color_correction_transform" to "identity",
            "noise_reduction_mode" to "OFF",
            "edge_mode" to "OFF",
            "tonemap_mode" to "CONTRAST_CURVE_LINEAR",
            "shading_mode" to "HIGH_QUALITY",
            "statistics_lens_shading_map_mode" to "ON",
            "statistics_hot_pixel_map_mode" to "ON",
            "af_mode" to "OFF",
            "black_level_lock" to true,
        )

        val actualMap = mapOf<String, Any?>(
            "exposure_time_ns" to captureResult.get(CaptureResult.SENSOR_EXPOSURE_TIME),
            "sensor_sensitivity" to captureResult.get(CaptureResult.SENSOR_SENSITIVITY),
            "frame_duration_ns" to captureResult.get(CaptureResult.SENSOR_FRAME_DURATION),
            "sensor_timestamp" to captureResult.get(CaptureResult.SENSOR_TIMESTAMP),
            "dynamic_black_level" to
                captureResult.get(CaptureResult.SENSOR_DYNAMIC_BLACK_LEVEL)?.toList(),
            "dynamic_white_level" to
                captureResult.get(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL),
            "neutral_color_point" to
                captureResult.get(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT)?.map {
                    "${it.numerator}/${it.denominator}"
                },
            "color_gains" to captureResult.get(CaptureResult.COLOR_CORRECTION_GAINS)?.let {
                listOf(
                    it.red.toDouble(),
                    it.greenEven.toDouble(),
                    it.greenOdd.toDouble(),
                    it.blue.toDouble()
                )
            },
            "color_transform" to
                captureResult.get(CaptureResult.COLOR_CORRECTION_TRANSFORM)?.let { transform ->
                    (0 until 3).map { row ->
                        (0 until 3).map { col ->
                            val r = transform.getElement(col, row)
                            "${r.numerator}/${r.denominator}"
                        }
                    }
                },
        )

        val sensorCharacteristicsMap = mapOf<String, Any?>(
            "color_filter_arrangement" to
                characteristics.get(
                    CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
                ),
            "black_level_pattern" to
                characteristics.get(
                    CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN
                )?.toString(),
            "white_level" to characteristics.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL),
            "physical_size" to
                characteristics.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE)?.let {
                    listOf(it.width, it.height)
                },
            "pixel_array_size" to
                characteristics.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE)?.let {
                    listOf(it.width, it.height)
                },
            "active_array_size" to
                characteristics.get(
                    CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE
                )?.toString(),
        )

        return mapOf(
            "captureTimestampUtc" to Instant.now().toString(),
            "deviceModel" to Build.MODEL,
            "androidVersion" to Build.VERSION.RELEASE,
            "cameraId" to cameraId,
            "hardwareLevel" to
                characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL),
            "captureMode" to "quantitative",
            "imageFormat" to "RAW_SENSOR",
            "imageSize" to listOf(image.width, image.height),
            "settings" to settingsMap,
            "actual" to actualMap,
            "sensorCharacteristics" to sensorCharacteristicsMap,
            "image_statistics" to imageStatistics,
            "hot_pixel_map" to hotPixelMap,
        )
    }
}