目录
[二、代码实现(C 语言,适配 ISP Pipeline)](#二、代码实现(C 语言,适配 ISP Pipeline))
[1. 标定数据表设计](#1. 标定数据表设计)
[2. 误差计算与匹配](#2. 误差计算与匹配)
[3. 线性插值核心逻辑](#3. 线性插值核心逻辑)
[4. 安全校验与限幅](#4. 安全校验与限幅)
[四、ISP Pipeline 集成注意事项](#四、ISP Pipeline 集成注意事项)
一、核心思路
白平衡色温估算的本质是 **"查表 + 插值"**:
- 标定数据准备:预先在多个标准色温下(如 2800K、4000K、6500K、9300K)标定出对应的 R/G、B/G 基准比值,形成色温 - 比值映射表;
- 实时统计:从 ISP 获取当前画面的平均 R/G、B/G 比值(白平衡统计数据);
- 匹配与插值:将实时比值与标定表中的基准比值对比,找到最接近的两个色温点,通过线性插值计算出精确的当前色温。
二、代码实现(C 语言,适配 ISP Pipeline)
以下代码完整实现了基于标定数据和实时统计值的色温估算,包含标定表管理、比值匹配、线性插值核心逻辑:
cpp
#include <stdint.h>
#include <stddef.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
// ========== 1. 标定数据定义(核心:不同色温下的R/G、B/G基准值) ==========
// 白平衡标定结构体:色温 ↔ R/G、B/G比值
typedef struct {
uint32_t color_temp; // 色温(K,开尔文)
float r_over_g; // 标准色温下的R/G比值
float b_over_g; // 标准色温下的B/G比值
} WB_Calib_Data;
// 典型色温标定表(需根据实际硬件标定,示例包含4个常用色温点)
// 注意:标定数据需按色温升序排列,方便后续插值
static const WB_Calib_Data wb_calib_table[] = {
{2800, 1.65f, 0.55f}, // 暖黄光(钨丝灯)
{4000, 1.30f, 0.70f}, // 暖白光(荧光灯)
{6500, 1.00f, 1.00f}, // 日光(D65标准)
{9300, 0.80f, 1.20f} // 冷白光(阴天)
};
#define CALIB_TABLE_SIZE (sizeof(wb_calib_table) / sizeof(WB_Calib_Data))
// ========== 2. 白平衡统计数据结构体(ISP输出) ==========
typedef struct {
float avg_r_over_g; // 画面平均R/G比值
float avg_b_over_g; // 画面平均B/G比值
uint32_t valid_pixels; // 有效统计像素数(过滤黑/白点)
} WB_Stats_Data;
// ========== 3. 核心函数:色温估算 ==========
/**
* @brief 基于标定数据和实时WB统计值,估算当前色温
* @param wb_stats ISP输出的实时R/G、B/G统计值
* @return 估算的色温值(K),失败返回0
*/
uint32_t estimate_color_temperature(const WB_Stats_Data *wb_stats) {
// 安全校验:统计数据无效时返回0
if (wb_stats == NULL || wb_stats->valid_pixels == 0 ||
wb_stats->avg_r_over_g <= 0 || wb_stats->avg_b_over_g <= 0) {
return 0;
}
// 步骤1:计算实时比值与标定数据的误差(加权误差,兼顾R/G和B/G)
float min_error = 1000.0f;
int closest_idx = -1;
for (int i = 0; i < CALIB_TABLE_SIZE; i++) {
// 计算R/G、B/G的绝对误差,加权求和(权重可根据硬件调整)
float r_error = fabs(wb_stats->avg_r_over_g - wb_calib_table[i].r_over_g);
float b_error = fabs(wb_stats->avg_b_over_g - wb_calib_table[i].b_over_g);
float total_error = (r_error * 0.6f) + (b_error * 0.4f); // R/G权重更高
if (total_error < min_error) {
min_error = total_error;
closest_idx = i;
}
}
// 步骤2:如果匹配到精确值(误差极小),直接返回对应色温
if (min_error < 0.01f) { // 误差阈值,可根据标定精度调整
return wb_calib_table[closest_idx].color_temp;
}
// 步骤3:线性插值计算精确色温(核心:在两个相邻标定点间插值)
// 找到相邻的两个标定点(左:比当前比值色温低,右:比当前比值色温高)
int left_idx = -1, right_idx = -1;
// 情况1:最接近点是第一个(色温最低)
if (closest_idx == 0) {
left_idx = 0;
right_idx = 1;
}
// 情况2:最接近点是最后一个(色温最高)
else if (closest_idx == CALIB_TABLE_SIZE - 1) {
left_idx = CALIB_TABLE_SIZE - 2;
right_idx = CALIB_TABLE_SIZE - 1;
}
// 情况3:最接近点在中间,判断左右
else {
float error_left = fabs(wb_stats->avg_r_over_g - wb_calib_table[closest_idx-1].r_over_g) +
fabs(wb_stats->avg_b_over_g - wb_calib_table[closest_idx-1].b_over_g);
float error_right = fabs(wb_stats->avg_r_over_g - wb_calib_table[closest_idx+1].r_over_g) +
fabs(wb_stats->avg_b_over_g - wb_calib_table[closest_idx+1].b_over_g);
left_idx = (error_left < error_right) ? closest_idx-1 : closest_idx;
right_idx = (error_left < error_right) ? closest_idx : closest_idx+1;
}
// 确保插值区间有效
if (left_idx < 0 || right_idx >= CALIB_TABLE_SIZE || left_idx == right_idx) {
return wb_calib_table[closest_idx].color_temp;
}
// 步骤4:基于R/G比值进行线性插值(R/G对色温更敏感)
WB_Calib_Data left = wb_calib_table[left_idx];
WB_Calib_Data right = wb_calib_table[right_idx];
// 计算实时R/G在左右两个标定R/G之间的比例
float ratio = (wb_stats->avg_r_over_g - left.r_over_g) / (right.r_over_g - left.r_over_g);
// 线性插值计算色温
uint32_t estimated_kt = (uint32_t)(left.color_temp + ratio * (right.color_temp - left.color_temp));
// 步骤5:限幅(避免估算色温超出标定范围)
estimated_kt = (estimated_kt < wb_calib_table[0].color_temp) ? wb_calib_table[0].color_temp : estimated_kt;
estimated_kt = (estimated_kt > wb_calib_table[CALIB_TABLE_SIZE-1].color_temp) ? wb_calib_table[CALIB_TABLE_SIZE-1].color_temp : estimated_kt;
return estimated_kt;
}
// ========== 测试用例 ==========
int main() {
// 测试场景1:实时统计值接近6500K(日光)
WB_Stats_Data stats1 = {
.avg_r_over_g = 1.02f,
.avg_b_over_g = 0.98f,
.valid_pixels = 100000 // 有效统计像素数
};
uint32_t kt1 = estimate_color_temperature(&stats1);
printf("【场景1】实时R/G=1.02, B/G=0.98 → 估算色温:%d K(接近6500K)\n", kt1);
// 测试场景2:实时统计值在4000K和6500K之间
WB_Stats_Data stats2 = {
.avg_r_over_g = 1.15f, // 4000K(1.30)和6500K(1.00)的中间值
.avg_b_over_g = 0.85f, // 4000K(0.70)和6500K(1.00)的中间值
.valid_pixels = 100000
};
uint32_t kt2 = estimate_color_temperature(&stats2);
printf("【场景2】实时R/G=1.15, B/G=0.85 → 估算色温:%d K(4000-6500K插值)\n", kt2);
// 测试场景3:实时统计值接近2800K(暖黄光)
WB_Stats_Data stats3 = {
.avg_r_over_g = 1.63f,
.avg_b_over_g = 0.56f,
.valid_pixels = 100000
};
uint32_t kt3 = estimate_color_temperature(&stats3);
printf("【场景3】实时R/G=1.63, B/G=0.56 → 估算色温:%d K(接近2800K)\n", kt3);
return 0;
}
三、关键代码解释
1. 标定数据表设计
cpp
static const WB_Calib_Data wb_calib_table[] = {
{2800, 1.65f, 0.55f}, // 暖黄光
{4000, 1.30f, 0.70f}, // 暖白光
{6500, 1.00f, 1.00f}, // 日光
{9300, 0.80f, 1.20f} // 冷白光
};
- 标定表需按色温升序排列,方便后续插值计算;
- 每个色温点对应一组 R/G、B/G 基准值(由硬件在标准色温箱中实测得到);
- 6500K(D65)是白平衡基准,此时 R/G=1、B/G=1,画面无偏色。
2. 误差计算与匹配
cpp
float total_error = (r_error * 0.6f) + (b_error * 0.4f);
- 计算实时比值与标定比值的绝对误差,对 R/G 赋予更高权重(R/G 对色温变化更敏感);
- 找到误差最小的标定点,作为色温估算的基础。
3. 线性插值核心逻辑
cpp
float total_error = (r_error * 0.6f) + (b_error * 0.4f);
- 当实时比值不在标定点上时,通过线性插值计算精确色温;
- 示例中选择 R/G 作为插值依据(也可选择 B/G 或两者加权,需根据硬件标定精度调整);
- 公式原理:
目标值 = 左点值 + 比例 * (右点值 - 左点值)。
4. 安全校验与限幅
- 对无效统计数据(如无有效像素、比值为 0)返回 0,避免计算错误;
- 对插值结果限幅,确保估算色温不超出标定表的最小 / 最大色温范围,避免异常值。
四、ISP Pipeline 集成注意事项
- 标定数据优化 :
- 实际应用中,标定点应更密集(如每 500K 一个点),提升色温估算精度;
- 标定数据需与具体传感器匹配(不同传感器的 R/G、B/G 基准值不同)。
- 实时统计数据获取 :
- ISP 需从 RGB RAW 数据(或 YUV 的 RGB 转换数据)中统计画面平均 R/G、B/G 比值;
- 统计时需过滤黑电平、过曝像素(如 R/G/B <16 或> 235),避免异常值影响。
- 性能优化 :
- 标定表大小固定,可将匹配和插值逻辑用硬件实现(ISP 内置算法),保证实时性;
- 对高分辨率画面,可下采样统计(如每 4x4 像素取一个点),减少计算量。
- 精度调整 :
- 误差阈值(
0.01f)可根据标定精度调整,阈值越小,仅当比值极接近标定值时才直接返回,否则插值; - 权重系数(0.6/0.4)可通过实测调整,使估算色温更贴合实际场景。
- 误差阈值(
总结
- 色温估算核心是 **"标定表匹配 + 线性插值"**:先找到最接近的标定色温点,再通过插值得到精确值;
- 关键数据:预先标定的 "色温 - R/G/B/G" 映射表、ISP 实时统计的画面平均 R/G、B/G 比值;
- 核心优化:对 R/G 赋予更高权重、过滤异常统计值、插值结果限幅,保证估算精度和稳定性。