一、FPGA实现AWB的方式
1.fpga实时统计灰点区域内的R/G/B分量求和
2.fpga实时统计灰点区域内的像素计数
3.awb_process()函数做白平衡校准的增益计算,以及做置信度的判断和限幅处理
4.将R/G/B颜色增益寄存器写入到逻辑AWB算法IP
二、AWB核心原理
灰度世界法+灰点椭圆/矩形区域法
1.计算每个像素的色度比rg = R/G,bg=B/G
2.硬件只统计落入"灰色区域"(AWB 窗口)内的像素:以参考灰点 (center_rg, center_bg) 为中心、rg_radius/bg_radius 为半宽的区域(配合 h_rg/l_rg/h_bg/l_bg 上下界、ymin/ymax 亮度范围过滤);
3.累加区域内的 ΣR、ΣG、ΣB 和像素计数 ΣN;
4.cpu计算
--若 ΣN 占总像素比例不足 awb_pcnt_(置信度不足),保持旧增益;
--否则计算 rcoe = ΣG/ΣR(即 1/(ΣR/ΣG)),bcoe = ΣG/ΣB,gcoe = 1;
--用 awb_ycoe_ 做亮度补偿(保持统计亮度不变),并限幅到 0.8, 1.2 附近;
--写回增益寄存器
--awb_init_radius 的作用即根据 h/l 上下界初始化灰点中心和半径。
三、关键代码
1.结构体成员
/* ---- AWB 运行时数据 ---- */
float awb_center_rg_; // 灰点中心 rg = (h_rg + l_rg) / 2
float awb_center_bg_; // 灰点中心 bg = (h_bg + l_bg) / 2
float awb_rcoe_; // 当前 R 增益
float awb_gcoe_; // 当前 G 增益
float awb_bcoe_; // 当前 B 增益
u32 total_pix_cnt_; // 全帧像素数 = rows * cols
2.灰点区域初始化
/* 由 h/l 上下限推算灰点中心与椭圆半径
* center = (high + low) / 2
* radius = (high - low) / 2 (并限制最大半径,防止区域过大失去灰点意义)
*/
static void awb_init_radius(AWB* ths)
{
float h_rg = ths->param_.awb_h_rg_.get_value(&ths->param_.awb_h_rg_);
float l_rg = ths->param_.awb_l_rg_.get_value(&ths->param_.awb_l_rg_);
float h_bg = ths->param_.awb_h_bg_.get_value(&ths->param_.awb_h_bg_);
float l_bg = ths->param_.awb_l_bg_.get_value(&ths->param_.awb_l_bg_);
ths->awb_center_rg_ = 0.5f * (h_rg + l_rg);
ths->awb_center_bg_ = 0.5f * (h_bg + l_bg);
float r1 = h_rg / (ths->awb_center_rg_ + 0.01f) - 1.0f; // 0.01 防除0
float r2 = h_bg / (ths->awb_center_bg_ + 0.01f) - 1.0f;
if (r1 > 0.3f) r1 = 0.3f; /* rg_radius 上限 */
if (r2 > 0.3f) r2 = 0.3f; /* bg_radius 上限 */
ths->param_.awb_rg_radius_.set_value(&ths->param_.awb_rg_radius_, r1);
ths->param_.awb_bg_radius_.set_value(&ths->param_.awb_bg_radius_, r2);
/* 同步到 FPGA 寄存器(低16位/高16位拆分,Q格式) */
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_CENTER_RG, ths->awb_center_rg_);
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_CENTER_BG, ths->awb_center_bg_);
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_RG_RADIUS, r1);
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_BG_RADIUS, r2);
}
3.awb_process核心处理步骤
static void awb_process(AWB* ths)
{
XhlsIsp3AParam* p = &ths->param_;
/* 0. 手动模式:仅透传手动增益,不做统计闭环 */
if (p->awb_auto_.get_value(p->awb_auto_) == 0) {
ths->awb_rcoe_ = p->awb_rcoe_.get_value(p->awb_rcoe_);
ths->awb_gcoe_ = p->awb_gcoe_.get_value(p->awb_gcoe_);
ths->awb_bcoe_ = p->awb_bcoe_.get_value(p->awb_bcoe_);
awb_write_gain(ths);
return;
}
u32 r_sum = ths->base_.regs_.get_value(&ths->base_.regs_, AWB_STAT_R_SUM);
u32 g_sum = ths->base_.regs_.get_value(&ths->base_.regs_, AWB_STAT_G_SUM);
u32 b_sum = ths->base_.regs_.get_value(&ths->base_.regs_, AWB_STAT_B_SUM);
u32 n_cnt = ths->base_.regs_.get_value(&ths->base_.regs_, AWB_STAT_PIX_CNT);
float pcnt = p->awb_pcnt_.get_value(p->awb_pcnt_);
if (n_cnt < (u32)(ths->total_pix_cnt_ * pcnt) || n_cnt == 0) {
return; /* 样本不足,保持上一帧增益 */
}
if (g_sum == 0) return; /* 防除0 */
float rg_aver = (float)r_sum / (float)g_sum; /* ΣR / ΣG */
float bg_aver = (float)b_sum / (float)g_sum; /* ΣB / ΣG */
float rcoe = 1.0f / (rg_aver + 0.01f);
float gcoe = 1.0f;
float bcoe = 1.0f / (bg_aver + 0.01f);
float ycoe = p->awb_ycoe_.get_value(p->awb_ycoe_);
rcoe = rcoe * (1.0f - ycoe) + ycoe * gcoe;
bcoe = bcoe * (1.0f - ycoe) + ycoe * gcoe;
rcoe = STD_MAX(0.8f, STD_MIN(1.2f, rcoe));
bcoe = STD_MAX(0.8f, STD_MIN(1.2f, bcoe));
ths->awb_rcoe_ = rcoe;
ths->awb_gcoe_ = gcoe;
ths->awb_bcoe_ = bcoe;
awb_write_gain(ths);
}
static void awb_write_gain(AWB* ths)
{
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_R_COE, ths->awb_rcoe_);
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_G_COE, ths->awb_gcoe_);
ths->base_.regs_.set_value_float(&ths->base_.regs_, AWB_REG_B_COE, ths->awb_bcoe_);
}
四、参数说明
1.参数协同逻辑
┌─ ymin/ymax:亮度窗口过滤
像素筛选 ─────┤
└─ (rg∈l_rg, h_rg) 且 (bg∈l_bg, h_bg),且在半径 rg_radius/bg_radius 内
↓
统计:ΣR、ΣG、ΣB、ΣN ──→ pcnt 置信度门槛(不足则冻结增益)
↓
增益:rcoe=ΣG/ΣR, bcoe=ΣG/ΣB ──→ ycoe 亮度补偿 ──→ 0.8,1.2 限幅 ──→ 写回寄存器
--偏色严重难收敛 → 放大 rg_radius/bg_radius、降低 awb_pcnt_;
--画面易被彩色物体带偏 → 缩小半径、提高 pcnt、收紧 h/l_rg、h/l_bg;
--白平衡切换时画面闪烁 → 提高 ycoe(亮度补偿)或软件上对增益做斜坡过渡;
--暗场/逆光场景偏色 → 调整 ymin_/ymax_ 把无效像素排除出统计。
2.参数说明
参数
awb_mode:AWB 模块使能,其中0=关闭(透传),1=使能写入 CTRL bit0;菜单可切换
awb_auto:自动/手动模式,其中1=自动闭环(统计→计算增益→写回);0=手动,直接使用下面的 r/g/b 系数
awb_rcoe:R 通道增益,手动模式或自动模式下写回的当前 R 增益,典型 0.5~2.0
awb_gcoe:G 通道增益,灰世界法中固定为 1.0(G 作为归一化基准)
awb_bcoe:B 通道增益,同 rcoe,对应 B 通道
awb_ycoe:亮度补偿系数 0~1:rcoe' = rcoe·(1−ycoe) + ycoe。ycoe 越大,白平衡调整时越倾向保持亮度恒定,防止画面整体明暗跳变;典型 0.3~0.5
awb_pcnt:置信度阈值 灰点区域内像素数占全帧的最小比例(如 0.1 即 10%)。低于该值认为统计不可靠(场景无有效灰点/大面积单一色),保持上一帧增益不更新,防止误收敛
awb_h_rg:灰点区域 rg 上限 判定"灰色像素"时允许的最大 R/G 比,如 1.2
awb_l_rg:灰点区域 rg 下限 允许的最小 R/G 比,如 0.8
awb_h_bg:灰点区域 bg 上限 允许的最大 B/G 比,如 1.2
awb_l_bg:灰点区域 bg 下限 允许的最小 B/G 比,如 0.8
awb_rg_radius:rg 方向灰点半径 由 init_radius 依据 (h_rg−l_rg)/2 计算,限制在 0.3 以内;半径越大,允许参与统计的"近似灰"像素越多(收敛快但易受彩色物体干扰)
awb_bg_radius:bg 方向灰点半径 同上,作用于 B/G 方向
awb_ymin:亮度过滤下限 Y 低于该值的暗像素不参与统计(避免暗噪声使白平衡偏移)
awb_ymax:亮度过滤上限 Y 高于该值的过曝像素不参与统计(过曝像素 RGB 相等、无色度信息)
五、灰度区域调整
灰点区域 指在 R/G(rg)- B/G(bg)色度平面上判定"该像素接近灰色"的判定窗口,由以下参数共同控制:
判定条件:l_rg ≤ rg ≤ h_rg 且 l_bg ≤ h_bg 之间
且到中心点的归一化距离 ≤ 半径
中心:center_rg = (h_rg + l_rg) / 2
半径:radius = (h_rg - l_rg) / 2 (awb_init_radius 计算,上限 0.3)
六、置信度阈值设置
本帧落在灰点区域内的像素数 n_cnt,必须达到全帧总像素数 total_pix_cnt_ × awb_pcnt_,本次白平衡计算才有效;否则冻结上一帧增益,防止误收敛。
过低(<0.05):少量彩色噪声即可改变白平衡,画面易"漂色";
过高(>0.5):光源变化时长时间不更新,色彩偏移不能及时纠正;
该值只影响更新频率门槛,不影响每次更新的增益计算精度,应与灰点区域大小配合调整:区域小 → pcnt 适当调低;区域大 → pcnt 适当调高。
七、增益限幅
- 防止单帧突变(闪烁)
光源瞬间变化或统计噪声导致计算的增益异常(例如 rg 突然变成 2.0 → rcoe = 0.5),若不限幅,画面会在一帧内发生剧烈色偏跳变。限幅把每帧最大调整量限制在 ±20%,保证过渡平滑。
- 防止算法误收敛
当灰点区域被彩色物体占据(例如大面积红色屋顶),灰世界假设失效,可能算出极端增益(如 rcoe=0.3、bcoe=1.8)。限幅作为最后一道安全网,即使前面 pcnt 置信度判断漏过,也不会让画面出现严重失真。
- 保护下游模块的动态范围
过大的通道增益会使该通道像素饱和截断(clip),破坏色度信息,影响后续 CCM(颜色校正矩阵)、Gamma 等模块的效果。±20% 的范围是白平衡增量调节的合理工程区间。