@@ -46,6 +46,43 @@ constexpr float kDefaultSaturation = 1.0f;
*/
constexpr float kHueScale = -90.0f;
+void applyRequestedBrightness(IPAActiveState &activeState)
+{
+ auto &cproc = activeState.cproc;
+
+ /*
+ * The CPROC module applies the transfer function
+ *
+ * Yout = Yin * contrast + brightness/2
+ *
+ * The calculations are done one bit wider than the input data to
+ * account for the maximum contrast of 2x without clamping. Brightness
+ * is applied after and therefore affects the output only with half the
+ * value.
+ *
+ * Most users expect that changing contrast doesn't change the middle
+ * gray and that the brightness value is normalized to one. So they
+ * expect a transfer function of
+ *
+ * Yout = (Yin - 0.5) * contrast + 0.5 + user brightness
+ *
+ * This can be achieved by computing the hardware brightness value from
+ * the user brightness and contrast:
+ *
+ * Yout = Yin * contrast + 0.5 - 0.5 * contrast + user brightness
+ * = Yin * contrast + (1 - contrast + 2 * user brightness) / 2
+ */
+ cproc.brightness = 1 - cproc.contrast.value() + 2 * cproc.requestedBrightness;
+
+ /*
+ * The cproc.brightness value is clamped to the hardware limits by the
+ * Quantized class. This effectively clamps the user brightness to
+ * limits that now depend on the contrast. Calculate the actual user
+ * brightness to report in metadata by inverting the formula.
+ */
+ cproc.actualBrightness = (cproc.brightness.value() - 1 + cproc.contrast.value()) / 2;
+}
+
} /* namespace */
/**
@@ -76,8 +113,10 @@ int ColorProcessing::configure(IPAContext &context,
{
auto &cproc = context.activeState.cproc;
- cproc.brightness = BrightnessQ(kDefaultBrightness);
+ cproc.requestedBrightness = kDefaultBrightness;
cproc.contrast = ContrastQ(kDefaultContrast);
+ applyRequestedBrightness(context.activeState);
+
cproc.hue = HueQ(kDefaultHue);
cproc.saturation = SaturationQ(kDefaultSaturation);
@@ -98,17 +137,6 @@ void ColorProcessing::queueRequest(IPAContext &context,
if (frame == 0)
update = true;
- const auto &brightness = controls.get(controls::Brightness);
- if (brightness) {
- BrightnessQ value = *brightness;
- if (cproc.brightness != value) {
- cproc.brightness = value;
- update = true;
- }
-
- LOG(RkISP1CProc, Debug) << "Set brightness to " << value;
- }
-
const auto &contrast = controls.get(controls::Contrast);
if (contrast) {
ContrastQ value = *contrast;
@@ -120,6 +148,19 @@ void ColorProcessing::queueRequest(IPAContext &context,
LOG(RkISP1CProc, Debug) << "Set contrast to " << value;
}
+ const auto &brightness = controls.get(controls::Brightness);
+ if (brightness)
+ cproc.requestedBrightness = *brightness;
+
+ if (update || brightness) {
+ BrightnessQ old = cproc.brightness;
+ applyRequestedBrightness(context.activeState);
+ if (cproc.brightness != old)
+ update = true;
+
+ LOG(RkISP1CProc, Debug) << "Set brightness to " << cproc.actualBrightness;
+ }
+
const auto &hue = controls.get(controls::Hue);
if (hue) {
/* Scale the Hue from ]-90, +90] */
@@ -144,6 +185,7 @@ void ColorProcessing::queueRequest(IPAContext &context,
}
frameContext.cproc.brightness = cproc.brightness;
+ frameContext.cproc.actualBrightness = cproc.actualBrightness;
frameContext.cproc.contrast = cproc.contrast;
frameContext.cproc.hue = cproc.hue;
frameContext.cproc.saturation = cproc.saturation;
@@ -179,7 +221,7 @@ void ColorProcessing::process([[maybe_unused]] IPAContext &context,
[[maybe_unused]] const rkisp1_stat_buffer *stats,
ControlList &metadata)
{
- metadata.set(controls::Brightness, frameContext.cproc.brightness.value());
+ metadata.set(controls::Brightness, frameContext.cproc.actualBrightness);
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());
@@ -232,9 +232,15 @@ namespace libcamera::ipa::rkisp1 {
/**
* \var IPAActiveState::cproc
* \brief State for the Color Processing algorithm
+ *
+ * \var IPAActiveState::cproc.requestedBrightness
+ * \brief Brightness level requested by the user
+ *
+ * \var IPAActiveState::cproc.actualBrightness
+ * \brief Brightness level after contrast compensation
*
- * \struct IPAActiveState::cproc.brightness
- * \brief Brightness level
+ * \var IPAActiveState::cproc.brightness
+ * \brief Hardware brightness level
*
* \var IPAActiveState::cproc.contrast
* \brief Contrast level
@@ -400,8 +406,11 @@ namespace libcamera::ipa::rkisp1 {
* \var IPAFrameContext::cproc
* \brief Color Processing parameters for this frame
*
- * \struct IPAFrameContext::cproc.brightness
- * \brief Brightness level
+ * \var IPAFrameContext::cproc.actualBrightness
+ * \brief Brightness level after contrast compensation
+ *
+ * \var IPAFrameContext::cproc.brightness
+ * \brief Hardware brightness level
*
* \var IPAFrameContext::cproc.contrast
* \brief Contrast level
@@ -118,6 +118,8 @@ struct IPAActiveState {
} ccm;
struct {
+ float requestedBrightness;
+ float actualBrightness;
BrightnessQ brightness;
ContrastQ contrast;
HueQ hue;
@@ -181,6 +183,7 @@ struct IPAFrameContext : public FrameContext {
} awb;
struct {
+ float actualBrightness;
BrightnessQ brightness;
ContrastQ contrast;
HueQ hue;