From patchwork Tue Dec 21 05:28:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 15203 X-Patchwork-Delegate: paul.elder@ideasonboard.com Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id E2C9DBF415 for ; Tue, 21 Dec 2021 05:29:28 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 943F96090C; Tue, 21 Dec 2021 06:29:28 +0100 (CET) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fQC0z+i5"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id A9A8A608E9 for ; Tue, 21 Dec 2021 06:29:24 +0100 (CET) Received: from pyrite.mediacom.info (unknown [IPv6:2604:2d80:ad90:fb00:96fd:8874:873:6c16]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 819FF881; Tue, 21 Dec 2021 06:29:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1640064564; bh=omYf7ekd3m0/xp2eFT75886be4ezB/yS1o4HRPei9Y0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fQC0z+i5XR4CNa4NTnAomIe4DD6u1h+H/NRfYrEc3RB7S/gvnEjt3t95eDAzXNy0G YepIUNJ0dptfonn5bPyVnoviJEPvK3zgBXnIJCmOsJyTTmRm8TqPVBNXNosd1dzAiJ H+XPMVYNvDCwuz50EMNQdYMXYm6uGwYDh1ODMc8M= From: Paul Elder To: libcamera-devel@lists.libcamera.org Date: Mon, 20 Dec 2021 23:28:54 -0600 Message-Id: <20211221052854.2690659-3-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.27.0 In-Reply-To: <20211221052854.2690659-1-paul.elder@ideasonboard.com> References: <20211221052854.2690659-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH 2/2] android: Plumb tonemap curves X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Plumb everything related to tonemaps: - checking for manual post processing capability - static metadata: set available modes - static metadata: enable keys (if the libcamera camera supports tonemapping) - fill out template fields - converting between the android tonemap controls and libcamera tonemap controls (for request and result) Notably, for sending the tonemap in the request, we build the tonemap incrementally. That is, if the red, green, and blue parts of the curve are passed in separate requests, we "pool" them together into a single curve and pass it to the camera. As libcamera will have its own request control caching, we only send the tonemap curve to libcamera when at least one of the three curves is provided. Signed-off-by: Paul Elder --- src/android/camera_capabilities.cpp | 113 +++++++++++++++++++ src/android/camera_device.cpp | 162 ++++++++++++++++++++++++++++ src/android/camera_device.h | 3 + 3 files changed, 278 insertions(+) diff --git a/src/android/camera_capabilities.cpp b/src/android/camera_capabilities.cpp index cb5ea5e9..2ce465aa 100644 --- a/src/android/camera_capabilities.cpp +++ b/src/android/camera_capabilities.cpp @@ -281,6 +281,8 @@ bool CameraCapabilities::validateManualSensorCapability() bool CameraCapabilities::validateManualPostProcessingCapability() { + camera_metadata_ro_entry_t entry; + const char *noMode = "Manual post processing capability unavailable: "; if (!staticMetadata_->entryContains(ANDROID_CONTROL_AWB_AVAILABLE_MODES, @@ -307,6 +309,28 @@ bool CameraCapabilities::validateManualPostProcessingCapability() return false; } + bool found = staticMetadata_->getEntry(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, &entry); + if (!found) { + LOG(HAL, Info) << noMode << "missing tonemapping"; + return false; + } + + std::set tonemapModes; + for (unsigned int i = 0; i < entry.count; i++) + tonemapModes.insert(entry.data.u8[i]); + + if ((!tonemapModes.count(ANDROID_TONEMAP_MODE_CONTRAST_CURVE) && + !(tonemapModes.count(ANDROID_TONEMAP_MODE_GAMMA_VALUE) && + tonemapModes.count(ANDROID_TONEMAP_MODE_PRESET_CURVE))) || + !tonemapModes.count(ANDROID_TONEMAP_MODE_FAST) || + !tonemapModes.count(ANDROID_TONEMAP_MODE_HIGH_QUALITY)) { + LOG(HAL, Info) + << noMode + << "tonemap modes must contain at least {contrast, fast, hq} " + << "or {gamma, preset, fast, hq}"; + return false; + } + /* * \todo return true here after we satisfy all the requirements: * https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING @@ -1165,6 +1189,77 @@ int CameraCapabilities::initializeStaticMetadata() availableResultKeys_.insert(ANDROID_EDGE_MODE); } + std::vector availableTonemapModes; + bool tonemapGammaSupported = false; + bool tonemapPresetSupported = false; + const auto tonemapModeInfo = controlsInfo.find(&controls::TonemapMode); + if (tonemapModeInfo != controlsInfo.end()) { + for (const auto &value : tonemapModeInfo->second.values()) { + uint8_t mode; + switch (value.get()) { + case controls::TonemapModeContrastCurve: + mode = ANDROID_TONEMAP_MODE_CONTRAST_CURVE; + break; + case controls::TonemapModeFast: + mode = ANDROID_TONEMAP_MODE_FAST; + break; + case controls::TonemapModeHighQuality: + mode = ANDROID_TONEMAP_MODE_HIGH_QUALITY; + break; + case controls::TonemapModeGammaValue: + mode = ANDROID_TONEMAP_MODE_GAMMA_VALUE; + tonemapGammaSupported = true; + break; + case controls::TonemapModePresetCurve: + mode = ANDROID_TONEMAP_MODE_PRESET_CURVE; + tonemapPresetSupported = true; + break; + default: + LOG(HAL, Error) << "Unknown tonemap mode"; + continue; + } + availableTonemapModes.push_back(mode); + } + } + + /* \todo Figure out how to report TonemapCurve ControlInfo */ + const auto tonemapSizeInfo = controlsInfo.find(&controls::TonemapCurveSize); + if (!availableTonemapModes.empty() && + (tonemapSizeInfo != controlsInfo.end())) { + /* Available tonemap modes */ + staticMetadata_->addEntry(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, + availableTonemapModes); + availableCharacteristicsKeys_.insert(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES); + + /* Tonemap size */ + int32_t tonemapMaxCurvePoints = tonemapSizeInfo->second.max().get(); + staticMetadata_->addEntry(ANDROID_TONEMAP_MAX_CURVE_POINTS, + tonemapMaxCurvePoints); + availableCharacteristicsKeys_.insert(ANDROID_TONEMAP_MAX_CURVE_POINTS); + + /* Tonemap mode */ + availableRequestKeys_.insert(ANDROID_TONEMAP_MODE); + availableResultKeys_.insert(ANDROID_TONEMAP_MODE); + + /* Tonemap curve */ + availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_RED); + availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_GREEN); + availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_BLUE); + availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_RED); + availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_GREEN); + availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_BLUE); + + if (tonemapGammaSupported) { + availableRequestKeys_.insert(ANDROID_TONEMAP_GAMMA); + availableResultKeys_.insert(ANDROID_TONEMAP_GAMMA); + } + + if (tonemapPresetSupported) { + availableRequestKeys_.insert(ANDROID_TONEMAP_PRESET_CURVE); + availableResultKeys_.insert(ANDROID_TONEMAP_PRESET_CURVE); + } + } + /* JPEG static metadata. */ /* @@ -1894,6 +1989,12 @@ std::unique_ptr CameraCapabilities::requestTemplatePreview() con requestTemplate->addEntry(ANDROID_SHADING_MODE, shadingMode); } + if (staticMetadata_->entryContains(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, + ANDROID_TONEMAP_MODE_FAST)) { + uint8_t tonemapMode = ANDROID_TONEMAP_MODE_FAST; + requestTemplate->addEntry(ANDROID_TONEMAP_MODE, tonemapMode); + } + return requestTemplate; } @@ -1922,6 +2023,12 @@ std::unique_ptr CameraCapabilities::requestTemplateStill() const stillTemplate->appendEntry(ANDROID_NOISE_REDUCTION_MODE, noiseReduction); } + if (staticMetadata_->entryContains(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, + ANDROID_TONEMAP_MODE_HIGH_QUALITY)) { + uint8_t tonemapMode = ANDROID_TONEMAP_MODE_HIGH_QUALITY; + stillTemplate->appendEntry(ANDROID_TONEMAP_MODE, tonemapMode); + } + return stillTemplate; } @@ -1945,6 +2052,12 @@ std::unique_ptr CameraCapabilities::requestTemplateVideo() const previewTemplate->appendEntry(ANDROID_EDGE_MODE, edgeMode); } + if (staticMetadata_->entryContains(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, + ANDROID_TONEMAP_MODE_FAST)) { + uint8_t tonemapMode = ANDROID_TONEMAP_MODE_FAST; + previewTemplate->appendEntry(ANDROID_TONEMAP_MODE, tonemapMode); + } + /* * Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata * has been assembled as {{min, max} {max, max}}. diff --git a/src/android/camera_device.cpp b/src/android/camera_device.cpp index 7be0ce45..7cffb4b1 100644 --- a/src/android/camera_device.cpp +++ b/src/android/camera_device.cpp @@ -1014,6 +1014,95 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor) controls.set(controls::LensShadingMode, shadingMode); } + if (settings.getEntry(ANDROID_TONEMAP_MODE, &entry)) { + const int32_t data = static_cast(*entry.data.u8); + int32_t tonemapMode; + switch (data) { + case ANDROID_TONEMAP_MODE_CONTRAST_CURVE: + tonemapMode = controls::TonemapModeContrastCurve; + break; + case ANDROID_TONEMAP_MODE_FAST: + tonemapMode = controls::TonemapModeFast; + break; + case ANDROID_TONEMAP_MODE_HIGH_QUALITY: + tonemapMode = controls::TonemapModeHighQuality; + break; + case ANDROID_TONEMAP_MODE_GAMMA_VALUE: + tonemapMode = controls::TonemapModeGammaValue; + break; + case ANDROID_TONEMAP_MODE_PRESET_CURVE: + tonemapMode = controls::TonemapModePresetCurve; + break; + default: + LOG(HAL, Error) + << "Unknown tonemap mode: " << data; + return -EINVAL; + } + + controls.set(controls::TonemapMode, tonemapMode); + } + + bool curveUpdated = false; + tonemapCurve.resize(3); + static std::map curveTags = { + { ANDROID_TONEMAP_CURVE_RED, 0 }, + { ANDROID_TONEMAP_CURVE_GREEN, 1 }, + { ANDROID_TONEMAP_CURVE_BLUE, 2 }, + }; + + for (const std::pair &tag : curveTags) { + if (!settings.getEntry(tag.first, &entry)) + continue; + + tonemapCurve[tag.second].resize(entry.count); + for (unsigned int i = 0; i < entry.count; i++) + tonemapCurve[tag.second][i] = *(entry.data.f + i); + + curveUpdated = true; + } + + if (curveUpdated) { + size_t size = std::max(tonemapCurve[0].size(), + tonemapCurve[1].size()); + size = std::max(size, tonemapCurve[2].size()); + controls.set(controls::TonemapCurveSize, size); + + std::vector curve; + curve.resize(3 * size); + curve.insert(curve.begin(), + tonemapCurve[0].begin(), tonemapCurve[0].end()); + curve.insert(curve.begin() + size, + tonemapCurve[1].begin(), tonemapCurve[1].end()); + curve.insert(curve.begin() + 2 * size, + tonemapCurve[2].begin(), tonemapCurve[2].end()); + controls.set(controls::TonemapCurve, curve); + } + + if (settings.getEntry(ANDROID_TONEMAP_GAMMA, &entry)) { + const float data = *entry.data.f; + controls.set(controls::TonemapGamma, data); + } + + if (settings.getEntry(ANDROID_TONEMAP_PRESET_CURVE, &entry)) { + const int32_t data = static_cast(*entry.data.u8); + int32_t presetCurve; + switch (data) { + case ANDROID_TONEMAP_PRESET_CURVE_SRGB: + presetCurve = controls::TonemapPresetCurveSRGB; + break; + case ANDROID_TONEMAP_PRESET_CURVE_REC709: + presetCurve = controls::TonemapPresetCurveREC709; + break; + default: + LOG(HAL, Error) + << "Unknown tonemap mode: " << data; + return -EINVAL; + } + + controls.set(controls::TonemapPresetCurve, presetCurve); + + } + return 0; } @@ -1812,6 +1901,79 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons } } + if (metadata.contains(controls::TonemapMode)) { + bool valid; + switch (metadata.get(controls::TonemapMode)) { + case controls::TonemapModeContrastCurve: + value = ANDROID_TONEMAP_MODE_CONTRAST_CURVE; + valid = true; + break; + case controls::TonemapModeFast: + value = ANDROID_TONEMAP_MODE_FAST; + valid = true; + break; + case controls::TonemapModeHighQuality: + value = ANDROID_TONEMAP_MODE_HIGH_QUALITY; + valid = true; + break; + case controls::TonemapModeGammaValue: + value = ANDROID_TONEMAP_MODE_GAMMA_VALUE; + valid = true; + break; + case controls::TonemapModePresetCurve: + value = ANDROID_TONEMAP_MODE_PRESET_CURVE; + valid = true; + break; + default: + LOG(HAL, Error) << "Invalid tonemap mode"; + valid = false; + } + + /* Can be null on non-FULL */ + if (valid) + resultMetadata->addEntry(ANDROID_TONEMAP_MODE, value); + } + + if (metadata.contains(controls::TonemapCurve) && + metadata.contains(controls::TonemapCurveSize)) { + size_t size = metadata.get(controls::TonemapCurveSize); + + Span curve = metadata.get(controls::TonemapCurve); + Span red = curve.subspan(0, size); + Span green = curve.subspan(size, size); + Span blue = curve.subspan(2 * size, size); + + resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_RED, red); + resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_GREEN, green); + resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_BLUE, blue); + } + + if (metadata.contains(controls::TonemapGamma)) { + float gamma = metadata.get(controls::TonemapGamma); + resultMetadata->addEntry(ANDROID_TONEMAP_GAMMA, gamma); + } + + if (metadata.contains(controls::TonemapPresetCurve)) { + bool valid; + switch (metadata.get(controls::TonemapPresetCurve)) { + case controls::TonemapPresetCurveSRGB: + value = ANDROID_TONEMAP_PRESET_CURVE_SRGB; + valid = false; + break; + case controls::TonemapPresetCurveREC709: + value = ANDROID_TONEMAP_PRESET_CURVE_REC709; + valid = false; + break; + default: + LOG(HAL, Error) << "Invalid tonemap preset curve"; + valid = false; + } + + /* Can be null on non-FULL */ + if (valid) + resultMetadata->addEntry(ANDROID_TONEMAP_PRESET_CURVE, value); + } + /* * Return the result metadata pack even is not valid: get() will return * nullptr. diff --git a/src/android/camera_device.h b/src/android/camera_device.h index 01c269d3..2cab11c5 100644 --- a/src/android/camera_device.h +++ b/src/android/camera_device.h @@ -140,5 +140,8 @@ private: float lastAnalogueGain_; float lastDigitalGain_; + /* Build the tonemap curve incrementally */ + std::vector> tonemapCurve; + CameraMetadata lastSettings_; };