From e6db90d5ac42517ad9b3001a5904d2739f09b991 Mon Sep 17 00:00:00 2001 From: Michael Kunz Date: Tue, 18 Aug 2026 18:20:23 +0200 Subject: [PATCH] libcamera: Adding LensShadingCorrection maps and ToneCurve to controls metadata Having the LensShadingCorrection maps and ToneCurves available in frame metadata allows creating a DNG file with all necessary information so that the DNG matches the JPEG image without colour casts. I opened a PR in raspberrypi/rpicam-apps (https://github.com/raspberrypi/rpicam-apps/pull/928) improving the colour accuracy of said DNG files created by these apps. The information needed is currently not provided by libcamera in the frame metadata. This patch intends to add LensShadingCorrection maps and ToneCurve to the controls metadata in libcamera, so that DNG files can be written with correct colours. Signed-off-by: Michael Kunz --- src/ipa/rpi/common/ipa_base.cpp | 51 +++++++++++++++++++++++++++-- src/ipa/rpi/common/ipa_base.h | 1 + src/ipa/rpi/common/meson.build | 2 +- src/ipa/rpi/controller/rpi/alsc.cpp | 3 ++ src/libcamera/control_ids_core.yaml | 38 +++++++++++++++++++++ 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index 88040af32..359c5b9d6 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -12,21 +12,25 @@ #include #include + #include #include #include "controller/af_algorithm.h" #include "controller/af_status.h" #include "controller/agc_algorithm.h" +#include "controller/alsc_status.h" #include "controller/awb_algorithm.h" #include "controller/awb_status.h" #include "controller/black_level_status.h" #include "controller/ccm_algorithm.h" #include "controller/ccm_status.h" #include "controller/contrast_algorithm.h" +#include "controller/contrast_status.h" #include "controller/denoise_algorithm.h" #include "controller/hdr_algorithm.h" #include "controller/lux_status.h" +#include "controller/noise_status.h" #include "controller/sharpen_algorithm.h" #include "controller/statistics.h" #include "controller/sync_algorithm.h" @@ -82,6 +86,7 @@ const ControlInfoMap::Map ipaControls{ static_cast(defaultMaxFrameDuration.get()), Span{ { static_cast(defaultMinFrameDuration.get()), static_cast(defaultMinFrameDuration.get()) } }) }, + { &controls::EnableLensShadingCorrectionMapOutput, ControlInfo(false, true, false) }, { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) }, { &controls::rpi::StatsOutputEnable, ControlInfo(false, true, false) }, { &controls::rpi::CnnEnableInputTensor, ControlInfo(false, true, false) }, @@ -129,7 +134,7 @@ LOG_DEFINE_CATEGORY(IPARPI) namespace ipa::RPi { IpaBase::IpaBase() - : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), statsMetadataOutput_(false), + : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), lscMapsOutput_(false), statsMetadataOutput_(false), stitchSwapBuffers_(false), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0), firstStart_(true), flickerState_({ 0, 0s }), cnnEnableInputTensor_(false), awbEnabled_(true) { @@ -245,7 +250,6 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa agcStatus.exposureTime = defaultExposureTime; agcStatus.analogueGain = defaultAnalogueGain; applyAGC(&agcStatus, ctrls); - } result->sensorControls = std::move(ctrls); @@ -795,8 +799,8 @@ static const std::map HdrModeTable = { void IpaBase::applyControls(const ControlList &controls) { - using RPiController::AgcAlgorithm; using RPiController::AfAlgorithm; + using RPiController::AgcAlgorithm; using RPiController::ContrastAlgorithm; using RPiController::DenoiseAlgorithm; using RPiController::HdrAlgorithm; @@ -1478,6 +1482,10 @@ void IpaBase::applyControls(const ControlList &controls) break; } + case controls::ENABLE_LENS_SHADING_CORRECTION_MAP_OUTPUT: + lscMapsOutput_ = ctrl.second.get(); + break; + case controls::rpi::STATS_OUTPUT_ENABLE: statsMetadataOutput_ = ctrl.second.get(); break; @@ -1762,6 +1770,43 @@ void IpaBase::reportMetadata(unsigned int ipaContext) static_cast(kpiInfo->dspRuntime) }); } + NoiseStatus *noiseStatus = rpiMetadata.getLocked("noise.status"); + if (noiseStatus) { + float noiseProfile[] = { static_cast(noiseStatus->noiseSlope), + static_cast(noiseStatus->noiseConstant) }; + + libcameraMetadata_.set(controls::NoiseProfile, noiseProfile); + } + + ContrastStatus *contrastStatus = rpiMetadata.getLocked("contrast.status"); + if (contrastStatus && contrastStatus->gammaCurve.size() > 0) { + std::vector contrast; + contrast.reserve(contrastStatus->gammaCurve.size() * 2); + + contrastStatus->gammaCurve.map([&](double x, double y) { + contrast.emplace_back(static_cast(x)); + contrast.emplace_back(static_cast(y)); + }); + libcameraMetadata_.set(controls::ToneCurve, contrast); + } + + if (lscMapsOutput_) { + AlscStatus *alscStatus = rpiMetadata.getLocked("alsc.status"); + if (alscStatus) { + uint32_t elements = alscStatus->cols * alscStatus->rows; + std::vector map(3 * elements); + + std::copy(alscStatus->r.begin(), alscStatus->r.end(), map.begin() + 0 * elements); + std::copy(alscStatus->g.begin(), alscStatus->g.end(), map.begin() + 1 * elements); + std::copy(alscStatus->b.begin(), alscStatus->b.end(), map.begin() + 2 * elements); + + uint32_t sizeTable[] = { 3, alscStatus->cols, alscStatus->rows }; + libcameraMetadata_.set(controls::LensShadingCorrectionMaps, map); + libcameraMetadata_.set(controls::LensShadingCorrectionMapSize, sizeTable); + libcameraMetadata_.set(controls::EnableLensShadingCorrectionMapOutput, true); + } + } + metadataReady.emit(libcameraMetadata_); } diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h index 941a3b8f0..ff69f329e 100644 --- a/src/ipa/rpi/common/ipa_base.h +++ b/src/ipa/rpi/common/ipa_base.h @@ -68,6 +68,7 @@ class IpaBase : public IPARPiInterface std::deque frameLengths_; utils::Duration lastTimeout_; ControlList libcameraMetadata_; + bool lscMapsOutput_; bool statsMetadataOutput_; /* Remember the HDR status after a mode switch. */ diff --git a/src/ipa/rpi/common/meson.build b/src/ipa/rpi/common/meson.build index 73d2ee732..cb3555a80 100644 --- a/src/ipa/rpi/common/meson.build +++ b/src/ipa/rpi/common/meson.build @@ -5,7 +5,7 @@ rpi_ipa_common_sources = files([ ]) rpi_ipa_common_includes = [ - include_directories('..'), + include_directories('..','../..'), ] rpi_ipa_common_deps = [ diff --git a/src/ipa/rpi/controller/rpi/alsc.cpp b/src/ipa/rpi/controller/rpi/alsc.cpp index 4c852db04..0222da723 100644 --- a/src/ipa/rpi/controller/rpi/alsc.cpp +++ b/src/ipa/rpi/controller/rpi/alsc.cpp @@ -409,6 +409,9 @@ void Alsc::prepare(Metadata *imageMetadata) status.r = prevSyncResults_[0].data(); status.g = prevSyncResults_[1].data(); status.b = prevSyncResults_[2].data(); + status.cols = config_.tableSize.width; + status.rows = config_.tableSize.height; + imageMetadata->set("alsc.status", status); /* * Put the results in the global metadata as well. This will be used by diff --git a/src/libcamera/control_ids_core.yaml b/src/libcamera/control_ids_core.yaml index bde5c6b01..db115e643 100644 --- a/src/libcamera/control_ids_core.yaml +++ b/src/libcamera/control_ids_core.yaml @@ -1374,4 +1374,42 @@ controls: The nominal range is [-180, 180], where 0° leaves hues unchanged and the range wraps around continuously, with 180° == -180°. + - EnableLensShadingCorrectionMapOutput: + type: bool + direction: inout + description: | + Indicates if the lens shading correction maps should be set in frame- + metadata for each frame. Returns true if tables are successfully set. + + - LensShadingCorrectionMaps: + type: float + direction: out + description: | + A map giving the lens shading correction factors. Dimesnions of the + returned table is [number of channels, sizeX, sizeY] (planar tables) + size: [n] + + - LensShadingCorrectionMapSize: + type: uint32_t + direction: out + description: | + The number of channels/maps and the size of the lens shading correction + maps in pixels. E.g. [3, 32, 32] + size: [3] + + - ToneCurve: + type: float + direction: out + description: | + A profile tone curve to apply on linear RGB - as it seems it has the sRGB + gamma curve baked in. + size: [n] + + - NoiseProfile: + type: float + direction: out + description: | + The noise profile [Scale, Offset]. + size: [2] + ...