diff options
Diffstat (limited to 'src/ipa/raspberrypi/controller/rpi/awb.cpp')
-rw-r--r-- | src/ipa/raspberrypi/controller/rpi/awb.cpp | 213 |
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 ¶ms) { @@ -55,10 +60,10 @@ static void read_ct_curve(Pwl &ct_r, Pwl &ct_b, void AwbConfig::Read(boost::property_tree::ptree const ¶ms) { - 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 ¶ms) 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 ¶ms) 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; } } |