summaryrefslogtreecommitdiff
path: root/src/ipa/raspberrypi/controller/rpi/awb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipa/raspberrypi/controller/rpi/awb.cpp')
-rw-r--r--src/ipa/raspberrypi/controller/rpi/awb.cpp213
1 files changed, 126 insertions, 87 deletions
diff --git a/src/ipa/raspberrypi/controller/rpi/awb.cpp b/src/ipa/raspberrypi/controller/rpi/awb.cpp
index a5536e47..5cfd33a3 100644
--- a/src/ipa/raspberrypi/controller/rpi/awb.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/awb.cpp
@@ -5,19 +5,24 @@
* awb.cpp - AWB control algorithm
*/
-#include "../logging.hpp"
+#include <libcamera/base/log.h>
+
#include "../lux_status.h"
#include "awb.hpp"
using namespace RPiController;
+using namespace libcamera;
+
+LOG_DEFINE_CATEGORY(RPiAwb)
#define NAME "rpi.awb"
#define AWB_STATS_SIZE_X DEFAULT_AWB_REGIONS_X
#define AWB_STATS_SIZE_Y DEFAULT_AWB_REGIONS_Y
-const double Awb::RGB::INVALID = -1.0;
+// todo - the locking in this algorithm needs some tidying up as has been done
+// elsewhere (ALSC and AGC).
void AwbMode::Read(boost::property_tree::ptree const &params)
{
@@ -55,10 +60,10 @@ static void read_ct_curve(Pwl &ct_r, Pwl &ct_b,
void AwbConfig::Read(boost::property_tree::ptree const &params)
{
- RPI_LOG("AwbConfig");
bayes = params.get<int>("bayes", 1);
frame_period = params.get<uint16_t>("frame_period", 10);
startup_frames = params.get<uint16_t>("startup_frames", 10);
+ convergence_frames = params.get<unsigned int>("convergence_frames", 3);
speed = params.get<double>("speed", 0.05);
if (params.get_child_optional("ct_curve"))
read_ct_curve(ct_r, ct_b, params.get_child("ct_curve"));
@@ -100,8 +105,8 @@ void AwbConfig::Read(boost::property_tree::ptree const &params)
if (bayes) {
if (ct_r.Empty() || ct_b.Empty() || priors.empty() ||
default_mode == nullptr) {
- RPI_WARN(
- "Bayesian AWB mis-configured - switch to Grey method");
+ LOG(RPiAwb, Warning)
+ << "Bayesian AWB mis-configured - switch to Grey method";
bayes = false;
}
}
@@ -120,6 +125,7 @@ Awb::Awb(Controller *controller)
async_abort_ = async_start_ = async_started_ = async_finished_ = false;
mode_ = nullptr;
manual_r_ = manual_b_ = 0.0;
+ first_switch_mode_ = true;
async_thread_ = std::thread(std::bind(&Awb::asyncFunc, this));
}
@@ -128,8 +134,8 @@ Awb::~Awb()
{
std::lock_guard<std::mutex> lock(mutex_);
async_abort_ = true;
- async_signal_.notify_one();
}
+ async_signal_.notify_one();
async_thread_.join();
}
@@ -145,7 +151,7 @@ void Awb::Read(boost::property_tree::ptree const &params)
void Awb::Initialise()
{
- frame_count2_ = frame_count_ = frame_phase_ = 0;
+ frame_count_ = frame_phase_ = 0;
// Put something sane into the status that we are filtering towards,
// just in case the first few frames don't have anything meaningful in
// them.
@@ -163,48 +169,92 @@ void Awb::Initialise()
sync_results_.gain_b = 1.0;
}
prev_sync_results_ = sync_results_;
+ async_results_ = sync_results_;
+}
+
+unsigned int Awb::GetConvergenceFrames() const
+{
+ // If not in auto mode, there is no convergence
+ // to happen, so no need to drop any frames - return zero.
+ if (!isAutoEnabled())
+ return 0;
+ else
+ return config_.convergence_frames;
}
void Awb::SetMode(std::string const &mode_name)
{
- std::unique_lock<std::mutex> lock(settings_mutex_);
mode_name_ = mode_name;
}
void Awb::SetManualGains(double manual_r, double manual_b)
{
- std::unique_lock<std::mutex> lock(settings_mutex_);
// If any of these are 0.0, we swich back to auto.
manual_r_ = manual_r;
manual_b_ = manual_b;
+ // If not in auto mode, set these values into the sync_results which
+ // means that Prepare() will adopt them immediately.
+ if (!isAutoEnabled()) {
+ sync_results_.gain_r = prev_sync_results_.gain_r = manual_r_;
+ sync_results_.gain_g = prev_sync_results_.gain_g = 1.0;
+ sync_results_.gain_b = prev_sync_results_.gain_b = manual_b_;
+ }
+}
+
+void Awb::SwitchMode([[maybe_unused]] CameraMode const &camera_mode,
+ Metadata *metadata)
+{
+ // On the first mode switch we'll have no meaningful colour
+ // temperature, so try to dead reckon one if in manual mode.
+ if (!isAutoEnabled() && first_switch_mode_ && config_.bayes) {
+ Pwl ct_r_inverse = config_.ct_r.Inverse();
+ Pwl ct_b_inverse = config_.ct_b.Inverse();
+ double ct_r = ct_r_inverse.Eval(ct_r_inverse.Domain().Clip(1 / manual_r_));
+ double ct_b = ct_b_inverse.Eval(ct_b_inverse.Domain().Clip(1 / manual_b_));
+ prev_sync_results_.temperature_K = (ct_r + ct_b) / 2;
+ sync_results_.temperature_K = prev_sync_results_.temperature_K;
+ }
+ // Let other algorithms know the current white balance values.
+ metadata->Set("awb.status", prev_sync_results_);
+ first_switch_mode_ = false;
+}
+
+bool Awb::isAutoEnabled() const
+{
+ return manual_r_ == 0.0 || manual_b_ == 0.0;
}
void Awb::fetchAsyncResults()
{
- RPI_LOG("Fetch AWB results");
+ LOG(RPiAwb, Debug) << "Fetch AWB results";
async_finished_ = false;
async_started_ = false;
- sync_results_ = async_results_;
+ // It's possible manual gains could be set even while the async
+ // thread was running, so only copy the results if still in auto mode.
+ if (isAutoEnabled())
+ sync_results_ = async_results_;
}
-void Awb::restartAsync(StatisticsPtr &stats, std::string const &mode_name,
- double lux)
+void Awb::restartAsync(StatisticsPtr &stats, double lux)
{
- RPI_LOG("Starting AWB thread");
+ LOG(RPiAwb, Debug) << "Starting AWB calculation";
// this makes a new reference which belongs to the asynchronous thread
statistics_ = stats;
// store the mode as it could technically change
- auto m = config_.modes.find(mode_name);
+ auto m = config_.modes.find(mode_name_);
mode_ = m != config_.modes.end()
? &m->second
: (mode_ == nullptr ? config_.default_mode : mode_);
lux_ = lux;
frame_phase_ = 0;
- async_start_ = true;
async_started_ = true;
- size_t len = mode_name.copy(async_results_.mode,
- sizeof(async_results_.mode) - 1);
+ size_t len = mode_name_.copy(async_results_.mode,
+ sizeof(async_results_.mode) - 1);
async_results_.mode[len] = '\0';
+ {
+ std::lock_guard<std::mutex> lock(mutex_);
+ async_start_ = true;
+ }
async_signal_.notify_one();
}
@@ -215,13 +265,12 @@ void Awb::Prepare(Metadata *image_metadata)
double speed = frame_count_ < (int)config_.startup_frames
? 1.0
: config_.speed;
- RPI_LOG("Awb: frame_count " << frame_count_ << " speed " << speed);
+ LOG(RPiAwb, Debug)
+ << "frame_count " << frame_count_ << " speed " << speed;
{
std::unique_lock<std::mutex> lock(mutex_);
- if (async_started_ && async_finished_) {
- RPI_LOG("AWB thread finished");
+ if (async_started_ && async_finished_)
fetchAsyncResults();
- }
}
// Finally apply IIR filter to results and put into metadata.
memcpy(prev_sync_results_.mode, sync_results_.mode,
@@ -236,9 +285,10 @@ void Awb::Prepare(Metadata *image_metadata)
prev_sync_results_.gain_b = speed * sync_results_.gain_b +
(1.0 - speed) * prev_sync_results_.gain_b;
image_metadata->Set("awb.status", prev_sync_results_);
- RPI_LOG("Using AWB gains r " << prev_sync_results_.gain_r << " g "
- << prev_sync_results_.gain_g << " b "
- << prev_sync_results_.gain_b);
+ LOG(RPiAwb, Debug)
+ << "Using AWB gains r " << prev_sync_results_.gain_r << " g "
+ << prev_sync_results_.gain_g << " b "
+ << prev_sync_results_.gain_b;
}
void Awb::Process(StatisticsPtr &stats, Metadata *image_metadata)
@@ -246,28 +296,20 @@ void Awb::Process(StatisticsPtr &stats, Metadata *image_metadata)
// Count frames since we last poked the async thread.
if (frame_phase_ < (int)config_.frame_period)
frame_phase_++;
- if (frame_count2_ < (int)config_.startup_frames)
- frame_count2_++;
- RPI_LOG("Awb: frame_phase " << frame_phase_);
- if (frame_phase_ >= (int)config_.frame_period ||
- frame_count2_ < (int)config_.startup_frames) {
+ LOG(RPiAwb, Debug) << "frame_phase " << frame_phase_;
+ // We do not restart the async thread if we're not in auto mode.
+ if (isAutoEnabled() &&
+ (frame_phase_ >= (int)config_.frame_period ||
+ frame_count_ < (int)config_.startup_frames)) {
// Update any settings and any image metadata that we need.
- std::string mode_name;
- {
- std::unique_lock<std::mutex> lock(settings_mutex_);
- mode_name = mode_name_;
- }
struct LuxStatus lux_status = {};
lux_status.lux = 400; // in case no metadata
if (image_metadata->Get("lux.status", lux_status) != 0)
- RPI_LOG("No lux metadata found");
- RPI_LOG("Awb lux value is " << lux_status.lux);
+ LOG(RPiAwb, Debug) << "No lux metadata found";
+ LOG(RPiAwb, Debug) << "Awb lux value is " << lux_status.lux;
- std::unique_lock<std::mutex> lock(mutex_);
- if (async_started_ == false) {
- RPI_LOG("AWB thread starting");
- restartAsync(stats, mode_name, lux_status.lux);
- }
+ if (async_started_ == false)
+ restartAsync(stats, lux_status.lux);
}
}
@@ -287,8 +329,8 @@ void Awb::asyncFunc()
{
std::lock_guard<std::mutex> lock(mutex_);
async_finished_ = true;
- sync_signal_.notify_one();
}
+ sync_signal_.notify_one();
}
}
@@ -297,16 +339,16 @@ static void generate_stats(std::vector<Awb::RGB> &zones,
double min_G)
{
for (int i = 0; i < AWB_STATS_SIZE_X * AWB_STATS_SIZE_Y; i++) {
- Awb::RGB zone; // this is "invalid", unless R gets overwritten later
+ Awb::RGB zone;
double counted = stats[i].counted;
if (counted >= min_pixels) {
zone.G = stats[i].g_sum / counted;
if (zone.G >= min_G) {
zone.R = stats[i].r_sum / counted;
zone.B = stats[i].b_sum / counted;
+ zones.push_back(zone);
}
}
- zones.push_back(zone);
}
}
@@ -336,7 +378,7 @@ double Awb::computeDelta2Sum(double gain_r, double gain_b)
double delta_r = gain_r * z.R - 1 - config_.whitepoint_r;
double delta_b = gain_b * z.B - 1 - config_.whitepoint_b;
double delta2 = delta_r * delta_r + delta_b * delta_b;
- //RPI_LOG("delta_r " << delta_r << " delta_b " << delta_b << " delta2 " << delta2);
+ //LOG(RPiAwb, Debug) << "delta_r " << delta_r << " delta_b " << delta_b << " delta2 " << delta2;
delta2 = std::min(delta2, config_.delta_limit);
delta2_sum += delta2;
}
@@ -399,10 +441,11 @@ double Awb::coarseSearch(Pwl const &prior)
double prior_log_likelihood =
prior.Eval(prior.Domain().Clip(t));
double final_log_likelihood = delta2_sum - prior_log_likelihood;
- RPI_LOG("t: " << t << " gain_r " << gain_r << " gain_b "
- << gain_b << " delta2_sum " << delta2_sum
- << " prior " << prior_log_likelihood << " final "
- << final_log_likelihood);
+ LOG(RPiAwb, Debug)
+ << "t: " << t << " gain_r " << gain_r << " gain_b "
+ << gain_b << " delta2_sum " << delta2_sum
+ << " prior " << prior_log_likelihood << " final "
+ << final_log_likelihood;
points_.push_back(Pwl::Point(t, final_log_likelihood));
if (points_.back().y < points_[best_point].y)
best_point = points_.size() - 1;
@@ -413,7 +456,7 @@ double Awb::coarseSearch(Pwl const &prior)
mode_->ct_hi);
}
t = points_[best_point].x;
- RPI_LOG("Coarse search found CT " << t);
+ LOG(RPiAwb, Debug) << "Coarse search found CT " << t;
// We have the best point of the search, but refine it with a quadratic
// interpolation around its neighbours.
if (points_.size() > 2) {
@@ -422,15 +465,16 @@ double Awb::coarseSearch(Pwl const &prior)
t = interpolate_quadatric(points_[best_point - 1],
points_[best_point],
points_[best_point + 1]);
- RPI_LOG("After quadratic refinement, coarse search has CT "
- << t);
+ LOG(RPiAwb, Debug)
+ << "After quadratic refinement, coarse search has CT "
+ << t;
}
return t;
}
void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
{
- int span_r, span_b;
+ int span_r = -1, span_b = -1;
config_.ct_r.Eval(t, &span_r);
config_.ct_b.Eval(t, &span_b);
double step = t / 10 * config_.coarse_step * 0.1;
@@ -475,8 +519,9 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
double gain_r = 1 / r_test, gain_b = 1 / b_test;
double delta2_sum = computeDelta2Sum(gain_r, gain_b);
points[j].y = delta2_sum - prior_log_likelihood;
- RPI_LOG("At t " << t_test << " r " << r_test << " b "
- << b_test << ": " << points[j].y);
+ LOG(RPiAwb, Debug)
+ << "At t " << t_test << " r " << r_test << " b "
+ << b_test << ": " << points[j].y;
if (points[j].y < points[best_point].y)
best_point = j;
}
@@ -493,17 +538,18 @@ void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior)
double gain_r = 1 / r_test, gain_b = 1 / b_test;
double delta2_sum = computeDelta2Sum(gain_r, gain_b);
double final_log_likelihood = delta2_sum - prior_log_likelihood;
- RPI_LOG("Finally "
+ LOG(RPiAwb, Debug)
+ << "Finally "
<< t_test << " r " << r_test << " b " << b_test << ": "
<< final_log_likelihood
- << (final_log_likelihood < best_log_likelihood ? " BEST"
- : ""));
+ << (final_log_likelihood < best_log_likelihood ? " BEST" : "");
if (best_t == 0 || final_log_likelihood < best_log_likelihood)
best_log_likelihood = final_log_likelihood,
best_t = t_test, best_r = r_test, best_b = b_test;
}
t = best_t, r = best_r, b = best_b;
- RPI_LOG("Fine search found t " << t << " r " << r << " b " << b);
+ LOG(RPiAwb, Debug)
+ << "Fine search found t " << t << " r " << r << " b " << b;
}
void Awb::awbBayes()
@@ -517,13 +563,14 @@ void Awb::awbBayes()
Pwl prior = interpolatePrior();
prior *= zones_.size() / (double)(AWB_STATS_SIZE_X * AWB_STATS_SIZE_Y);
prior.Map([](double x, double y) {
- RPI_LOG("(" << x << "," << y << ")");
+ LOG(RPiAwb, Debug) << "(" << x << "," << y << ")";
});
double t = coarseSearch(prior);
double r = config_.ct_r.Eval(t);
double b = config_.ct_b.Eval(t);
- RPI_LOG("After coarse search: r " << r << " b " << b << " (gains r "
- << 1 / r << " b " << 1 / b << ")");
+ LOG(RPiAwb, Debug)
+ << "After coarse search: r " << r << " b " << b << " (gains r "
+ << 1 / r << " b " << 1 / b << ")";
// Not entirely sure how to handle the fine search yet. Mostly the
// estimated CT is already good enough, but the fine search allows us to
// wander transverely off the CT curve. Under some illuminants, where
@@ -531,8 +578,9 @@ void Awb::awbBayes()
// though I probably need more real datasets before deciding exactly how
// this should be controlled and tuned.
fineSearch(t, r, b, prior);
- RPI_LOG("After fine search: r " << r << " b " << b << " (gains r "
- << 1 / r << " b " << 1 / b << ")");
+ LOG(RPiAwb, Debug)
+ << "After fine search: r " << r << " b " << b << " (gains r "
+ << 1 / r << " b " << 1 / b << ")";
// Write results out for the main thread to pick up. Remember to adjust
// the gains from the ones that the "canonical sensor" would require to
// the ones needed by *this* sensor.
@@ -544,7 +592,7 @@ void Awb::awbBayes()
void Awb::awbGrey()
{
- RPI_LOG("Grey world AWB");
+ LOG(RPiAwb, Debug) << "Grey world AWB";
// Make a separate list of the derivatives for each of red and blue, so
// that we can sort them to exclude the extreme gains. We could
// consider some variations, such as normalising all the zones first, or
@@ -576,27 +624,18 @@ void Awb::awbGrey()
void Awb::doAwb()
{
- if (manual_r_ != 0.0 && manual_b_ != 0.0) {
- async_results_.temperature_K = 4500; // don't know what it is
- async_results_.gain_r = manual_r_;
- async_results_.gain_g = 1.0;
- async_results_.gain_b = manual_b_;
- RPI_LOG("Using manual white balance: gain_r "
- << async_results_.gain_r << " gain_b "
- << async_results_.gain_b);
- } else {
- prepareStats();
- RPI_LOG("Valid zones: " << zones_.size());
- if (zones_.size() > config_.min_regions) {
- if (config_.bayes)
- awbBayes();
- else
- awbGrey();
- RPI_LOG("CT found is "
- << async_results_.temperature_K
- << " with gains r " << async_results_.gain_r
- << " and b " << async_results_.gain_b);
- }
+ prepareStats();
+ LOG(RPiAwb, Debug) << "Valid zones: " << zones_.size();
+ if (zones_.size() > config_.min_regions) {
+ if (config_.bayes)
+ awbBayes();
+ else
+ awbGrey();
+ LOG(RPiAwb, Debug)
+ << "CT found is "
+ << async_results_.temperature_K
+ << " with gains r " << async_results_.gain_r
+ << " and b " << async_results_.gain_b;
}
}