| Message ID | 20260121173737.376113-10-kieran.bingham@ideasonboard.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
Hi Kieran, Thank you for the patch. Quoting Kieran Bingham (2026-01-21 18:37:28) > The RKISP1 supports a configurable Hue as part of the colour processing > unit (cproc). > > Implement the new control converting to the hardware scale accordingly > and report the applied control in the completed request metadata. > > This is implemented as a phase shift of the chrominance values between > -90 and +87.188 degrees according to the datasheet however the type > itself would imply that this is a range between -90 and 89.2969. > > Moreover, the hardware applies the inverse phase shift to the operation > expected and documented by libcamera, so we apply a negative scale when > converting the libcamera control to the Q<1,7> type, resulting in a > range of -89.2969 to +90.0 degrees control. > > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > --- > > v5: > - Use Q<1, 7> directly and handle scaling in cproc.cpp > - Use full string of quantized in debug log > - Invert/Negate the hardware hue direction > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > --- > src/ipa/rkisp1/algorithms/cproc.cpp | 28 ++++++++++++++++++++++++++++ > src/ipa/rkisp1/ipa_context.h | 3 +++ > 2 files changed, 31 insertions(+) > > diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp > index e9e2b5444bc9..7484e4780094 100644 > --- a/src/ipa/rkisp1/algorithms/cproc.cpp > +++ b/src/ipa/rkisp1/algorithms/cproc.cpp > @@ -37,8 +37,15 @@ namespace { > > constexpr float kDefaultBrightness = 0.0f; > constexpr float kDefaultContrast = 1.0f; > +constexpr float kDefaultHue = 0.0f; > constexpr float kDefaultSaturation = 1.0f; > > +/* > + * The Hue scale is negated as the hardware performs the opposite phase shift > + * to what is expected and defined from the libcamera Hue control value. > + */ > +constexpr float kHueScale = -90.0f; > + > } /* namespace */ > > /** > @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, > cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); > cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); > > + /* Hue adjustment is negated by kHueScale, min/max are swapped */ > + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, > + HueQ::TraitsType::min * kHueScale, > + kDefaultHue); > + > return 0; > } > > @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, > > cproc.brightness = BrightnessQ(kDefaultBrightness); > cproc.contrast = ContrastQ(kDefaultContrast); > + cproc.hue = HueQ(kDefaultHue); > cproc.saturation = SaturationQ(kDefaultSaturation); > > return 0; > @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, > LOG(RkISP1CProc, Debug) << "Set contrast to " << value; > } > > + const auto &hue = controls.get(controls::Hue); > + if (hue) { > + /* Scale the Hue from ]-90, +90] */ > + HueQ value = *hue / kHueScale; Here the Quantized type really makes things way easier... > + if (cproc.hue != value) { > + cproc.hue = value; > + update = true; > + } > + > + LOG(RkISP1CProc, Debug) << "Set hue to " << value; > + } > + > const auto saturation = controls.get(controls::Saturation); > if (saturation) { > SaturationQ value = *saturation; > @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, > > frameContext.cproc.brightness = cproc.brightness; > frameContext.cproc.contrast = cproc.contrast; > + frameContext.cproc.hue = cproc.hue; > frameContext.cproc.saturation = cproc.saturation; > frameContext.cproc.update = update; > } > @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, > config.setEnabled(true); > config->brightness = frameContext.cproc.brightness.quantized(); > config->contrast = frameContext.cproc.contrast.quantized(); > + config->hue = frameContext.cproc.hue.quantized(); > config->sat = frameContext.cproc.saturation.quantized(); > } > > @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, > { > metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); > metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); > + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); I just realize that you now carry the kHueScale all over. This is basically a similar problem like mapping float gains to sensor gain values. So in theory you could create specialized quantized traits that include the kHueScale. So maybe at some point we have a generalized ScaledFixedPointQTraits... :-) Wait this is interesting I need to write it... template<float S, typename T> struct ScaledQTraits { using QuantizedType = typename T::QuantizedType; static constexpr QuantizedType qMin = T::qMin; static constexpr QuantizedType qMax = T::qMax; static constexpr float toFloat(QuantizedType q) { return T::toFloat(q) * S; } static constexpr float min = toFloat(qMin); static constexpr float max = toFloat(qMax); static QuantizedType fromFloat(float v) { return T::fromFloat(v / S); } }; using HueQ = Quantized<ScaledQTraits<-90.0f, FixedPointQTraits<1, 7, uint8_t> >>; ... but requires C++-20 to compile due to the float template arg. Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> Cheers, Stefan > metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); > } > > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h > index cdb9a17b5adc..80b035044cda 100644 > --- a/src/ipa/rkisp1/ipa_context.h > +++ b/src/ipa/rkisp1/ipa_context.h > @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { > /* Fixed point types used by CPROC */ > using BrightnessQ = Q<1, 7>; > using ContrastQ = UQ<1, 7>; > +using HueQ = Q<1, 7>; > using SaturationQ = UQ<1, 7>; > > struct IPAHwSettings { > @@ -123,6 +124,7 @@ struct IPAActiveState { > struct { > BrightnessQ brightness; > ContrastQ contrast; > + HueQ hue; > SaturationQ saturation; > } cproc; > > @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { > struct { > BrightnessQ brightness; > ContrastQ contrast; > + HueQ hue; > SaturationQ saturation; > > bool update; > -- > 2.52.0 >
Quoting Stefan Klug (2026-01-23 14:14:32) > Hi Kieran, > > Thank you for the patch. > > Quoting Kieran Bingham (2026-01-21 18:37:28) > > The RKISP1 supports a configurable Hue as part of the colour processing > > unit (cproc). > > > > Implement the new control converting to the hardware scale accordingly > > and report the applied control in the completed request metadata. > > > > This is implemented as a phase shift of the chrominance values between > > -90 and +87.188 degrees according to the datasheet however the type > > itself would imply that this is a range between -90 and 89.2969. > > > > Moreover, the hardware applies the inverse phase shift to the operation > > expected and documented by libcamera, so we apply a negative scale when > > converting the libcamera control to the Q<1,7> type, resulting in a > > range of -89.2969 to +90.0 degrees control. > > > > Reviewed-by: Isaac Scott <isaac.scott@ideasonboard.com> > > Tested-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > > > --- > > > > v5: > > - Use Q<1, 7> directly and handle scaling in cproc.cpp > > - Use full string of quantized in debug log > > - Invert/Negate the hardware hue direction > > > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > --- > > src/ipa/rkisp1/algorithms/cproc.cpp | 28 ++++++++++++++++++++++++++++ > > src/ipa/rkisp1/ipa_context.h | 3 +++ > > 2 files changed, 31 insertions(+) > > > > diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp > > index e9e2b5444bc9..7484e4780094 100644 > > --- a/src/ipa/rkisp1/algorithms/cproc.cpp > > +++ b/src/ipa/rkisp1/algorithms/cproc.cpp > > @@ -37,8 +37,15 @@ namespace { > > > > constexpr float kDefaultBrightness = 0.0f; > > constexpr float kDefaultContrast = 1.0f; > > +constexpr float kDefaultHue = 0.0f; > > constexpr float kDefaultSaturation = 1.0f; > > > > +/* > > + * The Hue scale is negated as the hardware performs the opposite phase shift > > + * to what is expected and defined from the libcamera Hue control value. > > + */ > > +constexpr float kHueScale = -90.0f; > > + > > } /* namespace */ > > > > /** > > @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, > > cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); > > cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); > > > > + /* Hue adjustment is negated by kHueScale, min/max are swapped */ > > + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, > > + HueQ::TraitsType::min * kHueScale, > > + kDefaultHue); > > + > > return 0; > > } > > > > @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, > > > > cproc.brightness = BrightnessQ(kDefaultBrightness); > > cproc.contrast = ContrastQ(kDefaultContrast); > > + cproc.hue = HueQ(kDefaultHue); > > cproc.saturation = SaturationQ(kDefaultSaturation); > > > > return 0; > > @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, > > LOG(RkISP1CProc, Debug) << "Set contrast to " << value; > > } > > > > + const auto &hue = controls.get(controls::Hue); > > + if (hue) { > > + /* Scale the Hue from ]-90, +90] */ > > + HueQ value = *hue / kHueScale; > > Here the Quantized type really makes things way easier... > > > + if (cproc.hue != value) { > > + cproc.hue = value; > > + update = true; > > + } > > + > > + LOG(RkISP1CProc, Debug) << "Set hue to " << value; > > + } > > + > > const auto saturation = controls.get(controls::Saturation); > > if (saturation) { > > SaturationQ value = *saturation; > > @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, > > > > frameContext.cproc.brightness = cproc.brightness; > > frameContext.cproc.contrast = cproc.contrast; > > + frameContext.cproc.hue = cproc.hue; > > frameContext.cproc.saturation = cproc.saturation; > > frameContext.cproc.update = update; > > } > > @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, > > config.setEnabled(true); > > config->brightness = frameContext.cproc.brightness.quantized(); > > config->contrast = frameContext.cproc.contrast.quantized(); > > + config->hue = frameContext.cproc.hue.quantized(); > > config->sat = frameContext.cproc.saturation.quantized(); > > } > > > > @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, > > { > > metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); > > metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); > > + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); > > I just realize that you now carry the kHueScale all over. This is > basically a similar problem like mapping float gains to sensor gain > values. So in theory you could create specialized quantized traits that > include the kHueScale. So maybe at some point we have a generalized > ScaledFixedPointQTraits... :-) > > Wait this is interesting I need to write it... > > template<float S, typename T> > struct ScaledQTraits { > using QuantizedType = typename T::QuantizedType; > static constexpr QuantizedType qMin = T::qMin; > static constexpr QuantizedType qMax = T::qMax; > > static constexpr float toFloat(QuantizedType q) > { > return T::toFloat(q) * S; > } > > static constexpr float min = toFloat(qMin); > static constexpr float max = toFloat(qMax); > > static QuantizedType fromFloat(float v) > { > return T::fromFloat(v / S); > } > }; > > using HueQ = Quantized<ScaledQTraits<-90.0f, FixedPointQTraits<1, 7, uint8_t> >>; > Yes, I implemented this already (without float scales) - but Laurent asked me to remove it: [v4,10/21] ipa: libipa: fixedpoint: Provide a ScaledFixedPoint type - https://patchwork.libcamera.org/patch/25040/ [v4,11/21] test: libipa: Provide ScaledFixedPoint tests - https://patchwork.libcamera.org/patch/25041/ using HueQ = Quantized<ScaledFixedPointQTraits<Q1_7::TraitsType, 90>>; - https://patchwork.libcamera.org/patch/25045/ > ... but requires C++-20 to compile due to the float template arg. Perhaps a future optimisation after we move to C++20 if there's other users of scaled types. > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> Thanks Kieran > > Cheers, > Stefan > > > metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); > > } > > > > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h > > index cdb9a17b5adc..80b035044cda 100644 > > --- a/src/ipa/rkisp1/ipa_context.h > > +++ b/src/ipa/rkisp1/ipa_context.h > > @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { > > /* Fixed point types used by CPROC */ > > using BrightnessQ = Q<1, 7>; > > using ContrastQ = UQ<1, 7>; > > +using HueQ = Q<1, 7>; > > using SaturationQ = UQ<1, 7>; > > > > struct IPAHwSettings { > > @@ -123,6 +124,7 @@ struct IPAActiveState { > > struct { > > BrightnessQ brightness; > > ContrastQ contrast; > > + HueQ hue; > > SaturationQ saturation; > > } cproc; > > > > @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { > > struct { > > BrightnessQ brightness; > > ContrastQ contrast; > > + HueQ hue; > > SaturationQ saturation; > > > > bool update; > > -- > > 2.52.0 > >
diff --git a/src/ipa/rkisp1/algorithms/cproc.cpp b/src/ipa/rkisp1/algorithms/cproc.cpp index e9e2b5444bc9..7484e4780094 100644 --- a/src/ipa/rkisp1/algorithms/cproc.cpp +++ b/src/ipa/rkisp1/algorithms/cproc.cpp @@ -37,8 +37,15 @@ namespace { constexpr float kDefaultBrightness = 0.0f; constexpr float kDefaultContrast = 1.0f; +constexpr float kDefaultHue = 0.0f; constexpr float kDefaultSaturation = 1.0f; +/* + * The Hue scale is negated as the hardware performs the opposite phase shift + * to what is expected and defined from the libcamera Hue control value. + */ +constexpr float kHueScale = -90.0f; + } /* namespace */ /** @@ -53,6 +60,11 @@ int ColorProcessing::init(IPAContext &context, cmap[&controls::Contrast] = ControlInfo(0.0f, 1.993f, kDefaultContrast); cmap[&controls::Saturation] = ControlInfo(0.0f, 1.993f, kDefaultSaturation); + /* Hue adjustment is negated by kHueScale, min/max are swapped */ + cmap[&controls::Hue] = ControlInfo(HueQ::TraitsType::max * kHueScale, + HueQ::TraitsType::min * kHueScale, + kDefaultHue); + return 0; } @@ -66,6 +78,7 @@ int ColorProcessing::configure(IPAContext &context, cproc.brightness = BrightnessQ(kDefaultBrightness); cproc.contrast = ContrastQ(kDefaultContrast); + cproc.hue = HueQ(kDefaultHue); cproc.saturation = SaturationQ(kDefaultSaturation); return 0; @@ -107,6 +120,18 @@ void ColorProcessing::queueRequest(IPAContext &context, LOG(RkISP1CProc, Debug) << "Set contrast to " << value; } + const auto &hue = controls.get(controls::Hue); + if (hue) { + /* Scale the Hue from ]-90, +90] */ + HueQ value = *hue / kHueScale; + if (cproc.hue != value) { + cproc.hue = value; + update = true; + } + + LOG(RkISP1CProc, Debug) << "Set hue to " << value; + } + const auto saturation = controls.get(controls::Saturation); if (saturation) { SaturationQ value = *saturation; @@ -120,6 +145,7 @@ void ColorProcessing::queueRequest(IPAContext &context, frameContext.cproc.brightness = cproc.brightness; frameContext.cproc.contrast = cproc.contrast; + frameContext.cproc.hue = cproc.hue; frameContext.cproc.saturation = cproc.saturation; frameContext.cproc.update = update; } @@ -140,6 +166,7 @@ void ColorProcessing::prepare([[maybe_unused]] IPAContext &context, config.setEnabled(true); config->brightness = frameContext.cproc.brightness.quantized(); config->contrast = frameContext.cproc.contrast.quantized(); + config->hue = frameContext.cproc.hue.quantized(); config->sat = frameContext.cproc.saturation.quantized(); } @@ -154,6 +181,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context, { metadata.set(controls::Brightness, frameContext.cproc.brightness.value()); metadata.set(controls::Contrast, frameContext.cproc.contrast.value()); + metadata.set(controls::Hue, frameContext.cproc.hue.value() * kHueScale); metadata.set(controls::Saturation, frameContext.cproc.saturation.value()); } diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h index cdb9a17b5adc..80b035044cda 100644 --- a/src/ipa/rkisp1/ipa_context.h +++ b/src/ipa/rkisp1/ipa_context.h @@ -36,6 +36,7 @@ namespace ipa::rkisp1 { /* Fixed point types used by CPROC */ using BrightnessQ = Q<1, 7>; using ContrastQ = UQ<1, 7>; +using HueQ = Q<1, 7>; using SaturationQ = UQ<1, 7>; struct IPAHwSettings { @@ -123,6 +124,7 @@ struct IPAActiveState { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; } cproc; @@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext { struct { BrightnessQ brightness; ContrastQ contrast; + HueQ hue; SaturationQ saturation; bool update;