ISP Pipeline中Lv实现方式探究之四----正LV值定点实现

张开发
2026/6/7 23:22:16 15 分钟阅读
ISP Pipeline中Lv实现方式探究之四----正LV值定点实现
目录一、彻底避免「负数运算溢出 / 异常」二、Q12 定点数不会出现负数寄存器直接写入三、插值计算更稳定不会出现负增益 / 负亮度四、方便硬件做「查表法 LUT 寻址」五、避免 log2、浮点运算出现 NaN 或 Inf六、方便后期做校准、调试、显示七、代码实现计算正数Lv以下博文讲解了场景亮度划分Lv值可能得实现方式。ISP Pipeline中Lv实现方式探究之一ISP Pipeline中Lv实现方式探究之二ISP Pipeline中Lv实现方式探究之三--lv计算定点实现但是按照以上博文计算的lv值存在负数。而负数在一些应用场景可能存在一些问题点。因此最终实现方式应该如下图方式把Lv转换为正数。一、彻底避免「负数运算溢出 / 异常」嵌入式、ISP、MCU、DSP很多硬件模块不支持有符号数signed一旦出现负数会直接变成极大正数符号位翻转数据溢出调光异常、画面突变、死机加 6 偏置后所有 LV ≥ 0完全变成无符号数uint16/uint32安全使用二、Q12 定点数不会出现负数寄存器直接写入你的 LUT 是 Q12 格式用于AE 自动曝光ISP 亮度映射调光曲线负数在硬件寄存器里是非法值加偏置 →全部变成正整数直接写入寄存器不会报错、不会跳变三、插值计算更稳定不会出现负增益 / 负亮度插值公式LV LV[i] (LV[i1] - LV[i]) * t如果 LV 是负数差值可能反向插值结果可能突变亮度曲线不连续全部正数 → 插值全程单调平滑四、方便硬件做「查表法 LUT 寻址」绝大多数 ISP / AE 模块内部只支持无符号索引065535不支持负数索引加了 6 偏置后LV 范围变成0 ~ 18.91全部落在正数区间可以直接当作 LUT 索引使用五、避免 log2、浮点运算出现 NaN 或 Inf原始 LV 公式LV log2(1/(Texp * Gain))如果计算中出现负数log2 (负数) NaN非数值程序直接崩溃加偏置后所有值安全浮点运算永远稳定六、方便后期做校准、调试、显示正数优势界面显示好看不会出现 -2.35 LV调试不会出现负数干扰判断客户 / 平台更容易理解亮度等级方便转成0~100% 亮度七、代码实现计算正数Lv#include stdint.h #include stdio.h // 固定配置 #define ANCHOR_CNT 19 // 原始LV锚点数量 #define OUTPUT_POINTS 256 // 插值输出LUT长度 #define FPS_COUNT 5 // 帧率顺序30/25/20/10/8 #define Q12_SCALE 4096.0f // Q12定点格式 2^12 #define LV_OFFSET 6.0f // LV偏置6 确保无负数 // 原始LV锚点已6偏置全正数 const float lv_anchors[FPS_COUNT][ANCHOR_CNT] { // 30fps {18.91f,17.91f,16.91f,15.91f,14.91f,13.91f,12.91f,11.91f,10.91f, 10.91f,9.91f,8.91f,7.91f,6.91f,5.91f,4.91f,3.91f,2.91f,1.91f}, // 25fps {18.64f,17.64f,16.64f,15.64f,14.64f,13.64f,12.64f,11.64f,10.64f, 10.64f,9.64f,8.64f,7.64f,6.64f,5.64f,4.64f,3.64f,2.64f,1.64f}, // 20fps {18.32f,17.32f,16.32f,15.32f,14.32f,13.32f,12.32f,11.32f,10.32f, 10.32f,9.32f,8.32f,7.32f,6.32f,5.32f,4.32f,3.32f,2.32f,1.32f}, // 10fps {17.32f,16.32f,15.32f,14.32f,13.32f,12.32f,11.32f,10.32f,9.32f, 9.32f,8.32f,7.32f,6.32f,5.32f,4.32f,3.32f,2.32f,1.32f,0.32f}, // 8fps {17.00f,16.00f,15.00f,14.00f,13.00f,12.00f,11.00f,10.00f,9.00f, 9.00f,8.00f,7.00f,6.00f,5.00f,4.00f,3.00f,2.00f,1.00f,0.00f}, }; // 输出LUT缓存 float lv_lut_float[FPS_COUNT][OUTPUT_POINTS]; // 浮点结果全正 int32_t lv_lut_q12[FPS_COUNT][OUTPUT_POINTS]; // Q12定点结果全正 // 核心插值函数 float linear_interp(float idx, const float *anchor, int total_anchors) { // 1. 左点 i floor(idx) int i (int)idx; // 边界安全保护 if (i 0) return anchor[0]; if (i total_anchors - 1) return anchor[total_anchors - 1]; // 2. 比例 t idx - i float t idx - (float)i; // 3. 插值公式LV LV[i] (LV[i1] - LV[i]) * t return anchor[i] (anchor[i1] - anchor[i]) * t; } // 生成256点LUT主函数 void generate_lv_lut(void) { // 正确步长将 0~18 均匀映射到 0~255 float step (float)(ANCHOR_CNT - 1) / (OUTPUT_POINTS - 1); for (int fps 0; fps FPS_COUNT; fps) { for (int n 0; n OUTPUT_POINTS; n) { // 计算目标索引 idx float idx (float)n * step; // 执行插值 float val linear_interp(idx, lv_anchors[fps], ANCHOR_CNT); // 保存浮点值 lv_lut_float[fps][n] val; // Q12定点转换四舍五入无负数 lv_lut_q12[fps][n] (int32_t)(val * Q12_SCALE 0.5f); } } } // 打印Q12结果 void print_lut_result(void) { const char *fps_name[FPS_COUNT] {30fps, 25fps, 20fps, 10fps, 8fps}; for (int fps 0; fps FPS_COUNT; fps) { printf(\n// %s 256点 Q12 LUT (全正数) \n, fps_name[fps]); for (int i 0; i OUTPUT_POINTS; i) { printf(%6d,, lv_lut_q12[fps][i]); if ((i 1) % 16 0) printf(\n); } printf(\n); } } // 主函数 int main(void) { generate_lv_lut(); print_lut_result(); return 0; }

更多文章