Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d70132c3 authored by Ruben Brunk's avatar Ruben Brunk
Browse files

camera2: Fix number of DNG NoiseProfile coeffs.

Bug: 16369384
Change-Id: I3ceca5ed3489120664990c0d057e6537b357c788
parent 1109c7d4
Loading
Loading
Loading
Loading
+153 −41
Original line number Diff line number Diff line
@@ -653,6 +653,119 @@ static status_t moveEntries(TiffWriter* writer, uint32_t ifdFrom, uint32_t ifdTo
    return OK;
}

/**
 * Write CFA pattern for given CFA enum into cfaOut.  cfaOut must have length >= 4.
 * Returns OK on success, or a negative error code if the CFA enum was invalid.
 */
static status_t convertCFA(uint8_t cfaEnum, /*out*/uint8_t* cfaOut) {
    camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
            static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
            cfaEnum);
    switch(cfa) {
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
            cfaOut[0] = 0;
            cfaOut[1] = 1;
            cfaOut[2] = 1;
            cfaOut[3] = 2;
            break;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
            cfaOut[0] = 1;
            cfaOut[1] = 0;
            cfaOut[2] = 2;
            cfaOut[3] = 1;
            break;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
            cfaOut[0] = 1;
            cfaOut[1] = 2;
            cfaOut[2] = 0;
            cfaOut[3] = 1;
            break;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
            cfaOut[0] = 2;
            cfaOut[1] = 1;
            cfaOut[2] = 1;
            cfaOut[3] = 0;
            break;
        }
        default: {
            return BAD_VALUE;
        }
    }
    return OK;
}

/**
 * Convert the CFA layout enum to an OpcodeListBuilder::CfaLayout enum, defaults to
 * RGGB for an unknown enum.
 */
static OpcodeListBuilder::CfaLayout convertCFAEnumToOpcodeLayout(uint8_t cfaEnum) {
    camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
            static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
            cfaEnum);
    switch(cfa) {
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
            return OpcodeListBuilder::CFA_RGGB;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
            return OpcodeListBuilder::CFA_GRBG;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
            return OpcodeListBuilder::CFA_GBRG;
        }
        case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
            return OpcodeListBuilder::CFA_BGGR;
        }
        default: {
            return OpcodeListBuilder::CFA_RGGB;
        }
    }
}

/**
 * For each color plane, find the corresponding noise profile coefficients given in the
 * per-channel noise profile.  If multiple channels in the CFA correspond to a color in the color
 * plane, this method takes the pair of noise profile coefficients with the higher S coefficient.
 *
 * perChannelNoiseProfile - numChannels * 2 noise profile coefficients.
 * cfa - numChannels color channels corresponding to each of the per-channel noise profile
 *       coefficients.
 * numChannels - the number of noise profile coefficient pairs and color channels given in
 *       the perChannelNoiseProfile and cfa arguments, respectively.
 * planeColors - the color planes in the noise profile output.
 * numPlanes - the number of planes in planeColors and pairs of coefficients in noiseProfile.
 * noiseProfile - 2 * numPlanes doubles containing numPlanes pairs of noise profile coefficients.
 *
 * returns OK, or a negative error code on failure.
 */
static status_t generateNoiseProfile(const double* perChannelNoiseProfile, uint8_t* cfa,
        size_t numChannels, const uint8_t* planeColors, size_t numPlanes,
        /*out*/double* noiseProfile) {

    for (size_t p = 0; p < numPlanes; ++p) {
        size_t S = p * 2;
        size_t O = p * 2 + 1;

        noiseProfile[S] = 0;
        noiseProfile[O] = 0;
        bool uninitialized = true;
        for (size_t c = 0; c < numChannels; ++c) {
            if (cfa[c] == planeColors[p] && perChannelNoiseProfile[c * 2] > noiseProfile[S]) {
                noiseProfile[S] = perChannelNoiseProfile[c * 2];
                noiseProfile[O] = perChannelNoiseProfile[c * 2 + 1];
                uninitialized = false;
            }
        }
        if (uninitialized) {
            ALOGE("%s: No valid NoiseProfile coefficients for color plane %u", __FUNCTION__, p);
            return BAD_VALUE;
        }
    }
    return OK;
}

// ----------------------------------------------------------------------------
extern "C" {

@@ -745,6 +858,8 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
    uint32_t imageHeight = 0;

    OpcodeListBuilder::CfaLayout opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
    uint8_t cfaPlaneColor[3] = {0, 1, 2};
    uint8_t cfaEnum = -1;

    // TODO: Greensplit.
    // TODO: Add remaining non-essential tags
@@ -841,49 +956,23 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
        camera_metadata_entry entry =
                        characteristics.find(ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
        BAIL_IF_EMPTY(entry, env, TAG_CFAPATTERN, writer);
        camera_metadata_enum_android_sensor_info_color_filter_arrangement_t cfa =
                static_cast<camera_metadata_enum_android_sensor_info_color_filter_arrangement_t>(
                entry.data.u8[0]);
        switch(cfa) {
            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_RGGB: {
                uint8_t cfa[4] = {0, 1, 1, 2};
                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
                                                env, TAG_CFAPATTERN, writer);
                opcodeCfaLayout = OpcodeListBuilder::CFA_RGGB;
                break;
            }
            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GRBG: {
                uint8_t cfa[4] = {1, 0, 2, 1};
                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
                                                env, TAG_CFAPATTERN, writer);
                opcodeCfaLayout = OpcodeListBuilder::CFA_GRBG;
                break;
            }
            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_GBRG: {
                uint8_t cfa[4] = {1, 2, 0, 1};
                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
                                                env, TAG_CFAPATTERN, writer);
                opcodeCfaLayout = OpcodeListBuilder::CFA_GBRG;
                break;
            }
            case ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR: {
                uint8_t cfa[4] = {2, 1, 1, 0};
                BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, 4, cfa, TIFF_IFD_0),
                                env, TAG_CFAPATTERN, writer);
                opcodeCfaLayout = OpcodeListBuilder::CFA_BGGR;
                break;
            }
            default: {

        const int cfaLength = 4;
        cfaEnum = entry.data.u8[0];
        uint8_t cfa[cfaLength];
        if ((err = convertCFA(cfaEnum, /*out*/cfa)) != OK) {
            jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
                        "Invalid metadata for tag %d", TAG_CFAPATTERN);
                return;
            }
        }

        BAIL_IF_INVALID(writer->addEntry(TAG_CFAPATTERN, cfaLength, cfa, TIFF_IFD_0), env,
                TAG_CFAPATTERN, writer);

        opcodeCfaLayout = convertCFAEnumToOpcodeLayout(cfaEnum);
    }

    {
        // Set CFA plane color
        uint8_t cfaPlaneColor[3] = {0, 1, 2};
        BAIL_IF_INVALID(writer->addEntry(TAG_CFAPLANECOLOR, 3, cfaPlaneColor, TIFF_IFD_0),
                env, TAG_CFAPLANECOLOR, writer);
    }
@@ -1298,10 +1387,33 @@ static void DngCreator_init(JNIEnv* env, jobject thiz, jobject characteristicsPt
        camera_metadata_entry entry =
            results.find(ANDROID_SENSOR_NOISE_PROFILE);

        const status_t numPlaneColors = 3;
        const status_t numCfaChannels = 4;

        uint8_t cfaOut[numCfaChannels];
        if ((err = convertCFA(cfaEnum, /*out*/cfaOut)) != OK) {
            jniThrowException(env, "java/lang/IllegalArgumentException",
                    "Invalid CFA from camera characteristics");
            return;
        }

        double noiseProfile[numPlaneColors * 2];

        if (entry.count > 0) {
            BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, entry.count,
                    entry.data.d, TIFF_IFD_0), env,
                    TAG_NOISEPROFILE, writer);
            if (entry.count != numCfaChannels * 2) {
                ALOGW("%s: Invalid entry count %u for noise profile returned in characteristics,"
                        " no noise profile tag written...", __FUNCTION__, entry.count);
            } else {
                if ((err = generateNoiseProfile(entry.data.d, cfaOut, numCfaChannels,
                        cfaPlaneColor, numPlaneColors, /*out*/ noiseProfile)) == OK) {

                    BAIL_IF_INVALID(writer->addEntry(TAG_NOISEPROFILE, numPlaneColors * 2,
                            noiseProfile, TIFF_IFD_0), env, TAG_NOISEPROFILE, writer);
                } else {
                    ALOGW("%s: Error converting coefficients for noise profile, no noise profile"
                            " tag written...", __FUNCTION__);
                }
            }
        } else {
            ALOGW("%s: No noise profile found in result metadata.  Image quality may be reduced.",
                    __FUNCTION__);