Skip to content

Commit

Permalink
libcamera: software_isp: Move color mappings out of debayering
Browse files Browse the repository at this point in the history
Constructing the color mapping tables is related to stats rather than
debayering, where they are applied.  Let's move the corresponding code
to stats processing.

The same applies to the auxiliary gamma table.  As the gamma value is
currently fixed and used in a single place, with the temporary exception
mentioned below, there is no need to share it anywhere anymore.

It's necessary to initialize SoftwareIsp::debayerParams_ to default
values.  These initial values are used for the first two frames, before
they are changed based on determined stats.  To avoid sharing the gamma
value constant in artificial ways, we use 0.5 directly in the
initialization.  This all is not a particularly elegant thing to do,
such a code belongs conceptually to the similar code in stats
processing, but doing better is left for larger refactoring.

This is a preliminary step towards building this functionality on top of
libipa/algorithm.h, which should follow.

Signed-off-by: Milan Zamazal <[email protected]>
Reviewed-by: Andrei Konovalov <[email protected]>
Reviewed-by: Laurent Pinchart <[email protected]>
Signed-off-by: Laurent Pinchart <[email protected]>
  • Loading branch information
mz-pdm authored and pinchartl committed Jun 1, 2024
1 parent 4e13c6f commit 539c62f
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 80 deletions.
18 changes: 9 additions & 9 deletions include/libcamera/internal/software_isp/debayer_params.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2023, Red Hat Inc.
* Copyright (C) 2023, 2024 Red Hat Inc.
*
* Authors:
* Hans de Goede <[email protected]>
Expand All @@ -10,20 +10,20 @@

#pragma once

#include <array>
#include <stdint.h>

namespace libcamera {

struct DebayerParams {
static constexpr unsigned int kGain10 = 256;
static constexpr unsigned int kRGBLookupSize = 256;

unsigned int gainR;
unsigned int gainG;
unsigned int gainB;
using ColorLookupTable = std::array<uint8_t, kRGBLookupSize>;

float gamma;
/**
* \brief Level of the black point, 0..255, 0 is no correction.
*/
unsigned int blackLevel;
ColorLookupTable red;
ColorLookupTable green;
ColorLookupTable blue;
};

} /* namespace libcamera */
51 changes: 42 additions & 9 deletions src/ipa/simple/soft_simple.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Simple Software Image Processing Algorithm module
*/

#include <cmath>
#include <numeric>
#include <stdint.h>
#include <sys/mman.h>
Expand Down Expand Up @@ -84,6 +85,10 @@ class IPASoftSimple : public ipa::soft::IPASoftInterface
ControlInfoMap sensorInfoMap_;
BlackLevel blackLevel_;

static constexpr unsigned int kGammaLookupSize = 1024;
std::array<uint8_t, kGammaLookupSize> gammaTable_;
int lastBlackLevel_ = -1;

int32_t exposureMin_, exposureMax_;
int32_t exposure_;
double againMin_, againMax_, againMinStep_;
Expand Down Expand Up @@ -246,7 +251,6 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
if (ignoreUpdates_ > 0)
blackLevel_.update(histogram);
const uint8_t blackLevel = blackLevel_.get();
params_->blackLevel = blackLevel;

/*
* Black level must be subtracted to get the correct AWB ratios, they
Expand All @@ -263,13 +267,42 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
/*
* Calculate red and blue gains for AWB.
* Clamp max gain at 4.0, this also avoids 0 division.
* Gain: 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
*/
params_->gainR = sumR <= sumG / 4 ? 1024 : 256 * sumG / sumR;
params_->gainB = sumB <= sumG / 4 ? 1024 : 256 * sumG / sumB;

const unsigned int gainR = sumR <= sumG / 4 ? 1024 : 256 * sumG / sumR;
const unsigned int gainB = sumB <= sumG / 4 ? 1024 : 256 * sumG / sumB;
/* Green gain and gamma values are fixed */
params_->gainG = 256;
params_->gamma = 0.5;
constexpr unsigned int gainG = 256;

/* Update the gamma table if needed */
if (blackLevel != lastBlackLevel_) {
constexpr float gamma = 0.5;
const unsigned int blackIndex = blackLevel * kGammaLookupSize / 256;
std::fill(gammaTable_.begin(), gammaTable_.begin() + blackIndex, 0);
const float divisor = kGammaLookupSize - blackIndex - 1.0;
for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
gammaTable_[i] = UINT8_MAX *
std::pow((i - blackIndex) / divisor, gamma);

lastBlackLevel_ = blackLevel;
}

for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
constexpr unsigned int div =
DebayerParams::kRGBLookupSize * DebayerParams::kGain10 /
kGammaLookupSize;
unsigned int idx;

/* Apply gamma after gain! */
idx = std::min({ i * gainR / div, (kGammaLookupSize - 1) });
params_->red[i] = gammaTable_[idx];

idx = std::min({ i * gainG / div, (kGammaLookupSize - 1) });
params_->green[i] = gammaTable_[idx];

idx = std::min({ i * gainB / div, (kGammaLookupSize - 1) });
params_->blue[i] = gammaTable_[idx];
}

setIspParams.emit();

Expand All @@ -290,7 +323,7 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)
* https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf
*/
const unsigned int blackLevelHistIdx =
params_->blackLevel / (256 / SwIspStats::kYHistogramSize);
blackLevel / (256 / SwIspStats::kYHistogramSize);
const unsigned int histogramSize =
SwIspStats::kYHistogramSize - blackLevelHistIdx;
const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount;
Expand Down Expand Up @@ -338,8 +371,8 @@ void IPASoftSimple::processStats(const ControlList &sensorControls)

LOG(IPASoft, Debug) << "exposureMSV " << exposureMSV
<< " exp " << exposure_ << " again " << again_
<< " gain R/B " << params_->gainR << "/" << params_->gainB
<< " black level " << params_->blackLevel;
<< " gain R/B " << gainR << "/" << gainB
<< " black level " << static_cast<unsigned int>(blackLevel);
}

void IPASoftSimple::updateExposure(double exposureMSV)
Expand Down
29 changes: 14 additions & 15 deletions src/libcamera/software_isp/debayer.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2023, Linaro Ltd
* Copyright (C) 2023, Red Hat Inc.
* Copyright (C) 2023, 2024 Red Hat Inc.
*
* Authors:
* Hans de Goede <[email protected]>
Expand All @@ -24,29 +24,28 @@ namespace libcamera {
*/

/**
* \var DebayerParams::gainR
* \brief Red gain
*
* 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
* \var DebayerParams::kRGBLookupSize
* \brief Size of a color lookup table
*/

/**
* \var DebayerParams::gainG
* \brief Green gain
*
* 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
* \typedef DebayerParams::ColorLookupTable
* \brief Type of the lookup tables for red, green, blue values
*/

/**
* \var DebayerParams::gainB
* \brief Blue gain
*
* 128 = 0.5, 256 = 1.0, 512 = 2.0, etc.
* \var DebayerParams::red
* \brief Lookup table for red color, mapping input values to output values
*/

/**
* \var DebayerParams::green
* \brief Lookup table for green color, mapping input values to output values
*/

/**
* \var DebayerParams::gamma
* \brief Gamma correction, 1.0 is no correction
* \var DebayerParams::blue
* \brief Lookup table for blue color, mapping input values to output values
*/

/**
Expand Down
43 changes: 7 additions & 36 deletions src/libcamera/software_isp/debayer_cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "debayer_cpu.h"

#include <math.h>
#include <stdlib.h>
#include <time.h>

Expand All @@ -35,7 +34,7 @@ namespace libcamera {
* \param[in] stats Pointer to the stats object to use
*/
DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
: stats_(std::move(stats)), gammaCorrection_(1.0), blackLevel_(0)
: stats_(std::move(stats))
{
/*
* Reading from uncached buffers may be very slow.
Expand All @@ -47,9 +46,9 @@ DebayerCpu::DebayerCpu(std::unique_ptr<SwStatsCpu> stats)
*/
enableInputMemcpy_ = true;

/* Initialize gamma to 1.0 curve */
for (unsigned int i = 0; i < kGammaLookupSize; i++)
gamma_[i] = i / (kGammaLookupSize / kRGBLookupSize);
/* Initialize color lookup tables */
for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++)
red_[i] = green_[i] = blue_[i] = i;

for (unsigned int i = 0; i < kMaxLineBuffers; i++)
lineBuffers_[i] = nullptr;
Expand Down Expand Up @@ -698,37 +697,9 @@ void DebayerCpu::process(FrameBuffer *input, FrameBuffer *output, DebayerParams
clock_gettime(CLOCK_MONOTONIC_RAW, &frameStartTime);
}

/* Apply DebayerParams */
if (params.gamma != gammaCorrection_ || params.blackLevel != blackLevel_) {
const unsigned int blackIndex =
params.blackLevel * kGammaLookupSize / 256;
std::fill(gamma_.begin(), gamma_.begin() + blackIndex, 0);
const float divisor = kGammaLookupSize - blackIndex - 1.0;
for (unsigned int i = blackIndex; i < kGammaLookupSize; i++)
gamma_[i] = UINT8_MAX * powf((i - blackIndex) / divisor, params.gamma);

gammaCorrection_ = params.gamma;
blackLevel_ = params.blackLevel;
}

if (swapRedBlueGains_)
std::swap(params.gainR, params.gainB);

for (unsigned int i = 0; i < kRGBLookupSize; i++) {
constexpr unsigned int div =
kRGBLookupSize * DebayerParams::kGain10 / kGammaLookupSize;
unsigned int idx;

/* Apply gamma after gain! */
idx = std::min({ i * params.gainR / div, (kGammaLookupSize - 1) });
red_[i] = gamma_[idx];

idx = std::min({ i * params.gainG / div, (kGammaLookupSize - 1) });
green_[i] = gamma_[idx];

idx = std::min({ i * params.gainB / div, (kGammaLookupSize - 1) });
blue_[i] = gamma_[idx];
}
green_ = params.green;
red_ = swapRedBlueGains_ ? params.blue : params.red;
blue_ = swapRedBlueGains_ ? params.red : params.blue;

/* Copy metadata from the input buffer */
FrameMetadata &metadata = output->_d()->metadata();
Expand Down
11 changes: 3 additions & 8 deletions src/libcamera/software_isp/debayer_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,12 @@ class DebayerCpu : public Debayer, public Object
void process2(const uint8_t *src, uint8_t *dst);
void process4(const uint8_t *src, uint8_t *dst);

static constexpr unsigned int kGammaLookupSize = 1024;
static constexpr unsigned int kRGBLookupSize = 256;
/* Max. supported Bayer pattern height is 4, debayering this requires 5 lines */
static constexpr unsigned int kMaxLineBuffers = 5;

std::array<uint8_t, kGammaLookupSize> gamma_;
std::array<uint8_t, kRGBLookupSize> red_;
std::array<uint8_t, kRGBLookupSize> green_;
std::array<uint8_t, kRGBLookupSize> blue_;
DebayerParams::ColorLookupTable red_;
DebayerParams::ColorLookupTable green_;
DebayerParams::ColorLookupTable blue_;
debayerFn debayer0_;
debayerFn debayer1_;
debayerFn debayer2_;
Expand All @@ -146,8 +143,6 @@ class DebayerCpu : public Debayer, public Object
unsigned int xShift_; /* Offset of 0/1 applied to window_.x */
bool enableInputMemcpy_;
bool swapRedBlueGains_;
float gammaCorrection_;
unsigned int blackLevel_;
unsigned int measuredFrames_;
int64_t frameProcessTime_;
/* Skip 30 frames for things to stabilize then measure 30 frames */
Expand Down
24 changes: 21 additions & 3 deletions src/libcamera/software_isp/software_isp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include "libcamera/internal/software_isp/software_isp.h"

#include <cmath>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
Expand All @@ -18,6 +20,7 @@
#include "libcamera/internal/framebuffer.h"
#include "libcamera/internal/ipa_manager.h"
#include "libcamera/internal/mapped_framebuffer.h"
#include "libcamera/internal/software_isp/debayer_params.h"

#include "debayer_cpu.h"

Expand Down Expand Up @@ -63,10 +66,25 @@ LOG_DEFINE_CATEGORY(SoftwareIsp)
* handler
*/
SoftwareIsp::SoftwareIsp(PipelineHandler *pipe, const CameraSensor *sensor)
: debayerParams_{ DebayerParams::kGain10, DebayerParams::kGain10,
DebayerParams::kGain10, 0.5f, 0 },
dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
: dmaHeap_(DmaHeap::DmaHeapFlag::Cma | DmaHeap::DmaHeapFlag::System)
{
/*
* debayerParams_ must be initialized because the initial value is used for
* the first two frames, i.e. until stats processing starts providing its
* own parameters.
*
* \todo This should be handled in the same place as the related
* operations, in the IPA module.
*/
std::array<uint8_t, 256> gammaTable;
for (unsigned int i = 0; i < 256; i++)
gammaTable[i] = UINT8_MAX * std::pow(i / 256.0, 0.5);
for (unsigned int i = 0; i < DebayerParams::kRGBLookupSize; i++) {
debayerParams_.red[i] = gammaTable[i];
debayerParams_.green[i] = gammaTable[i];
debayerParams_.blue[i] = gammaTable[i];
}

if (!dmaHeap_.isValid()) {
LOG(SoftwareIsp, Error) << "Failed to create DmaHeap object";
return;
Expand Down

0 comments on commit 539c62f

Please sign in to comment.