ISP Pipeline中Lv实现方式探究之三--lv计算定点实现

目录

[一、 19 个 LV 锚点序列(8fps 为例)](#一、 19 个 LV 锚点序列(8fps 为例))

二、核心:一维均匀线性插值(按节点数插值)

[三、按 LV 节点插值的完整步骤](#三、按 LV 节点插值的完整步骤)

[四、实例演示(19 节点 → 插值到 32 个中间 LV)](#四、实例演示(19 节点 → 插值到 32 个中间 LV))

[五、Q12 定点插值](#五、Q12 定点插值)

六、具体实现


下述 博文中,详细阐述了根据Lv值进行亮度场景的划分原理。但是实际应用中,ISP驱动库是按照定点方式进行相应插值的。那么,如何下述博文所述方法转换为定点插值计算呢?

  1. ISP Pipeline中Lv实现方式探究之一
  2. ISP Pipeline中Lv实现方式探究之二

固定顺序的 19 个 LV 锚点 → 均匀插值生成任意中间 LV 值


一、 19 个 LV 锚点序列(8fps 为例)

8fps LV 序列(19 个锚点)

cpp 复制代码
[11.00, 10.00, 9.00, 8.00, 7.00, 6.00, 5.00, 4.00, 3.00,
3.00, 2.00, 1.00, 0.00, -1.00, -2.00, -3.00, -4.00, -5.00, -6.00]

N = 19 个节点 这些是插值基准锚点,中间值全部由它们计算。


二、核心:一维均匀线性插值(按节点数插值)

公式

python 复制代码
目标索引 idx(0 ~ total-1)
找到所在区间:
区间左点 i = floor(idx)
区间右点 i+1
比例 t = idx - i

中间LV = LV[i] + (LV[i+1] - LV[i]) * t

理解

  • 19 个节点 = 18 段插值区间
  • 每一段两个相邻锚点之间,均匀过渡
  • 你想要多少个中间点,就能生成多少个

三、按 LV 节点插值的完整步骤

步骤 1:确定要输出的总点数

例如:

  • 生成 256 点 LUT
  • 1024 点 LUT
  • 或任意数量

步骤 2:把 19 个锚点映射到目标长度

复制代码
step = (19 - 1) / (输出点数 - 1)

每一步走 step 个锚点单位。

步骤 3:逐点计算插值

复制代码
for k in 0 ... 输出点数-1:
    idx = k * step
    i = int(idx)
    t = idx - i
    outLV[k] = LV[i] + (LV[i+1] - LV[i]) * t

四、实例演示(19 节点 → 插值到 32 个中间 LV)

8fps LV 为例:

复制代码
锚点LV[19] = {
11.00,10.00,9.00,8.00,7.00,6.00,5.00,4.00,3.00,
3.00,2.00,1.00,0.00,-1.00,-2.00,-3.00,-4.00,-5.00,-6.00
};

插值到 32 点:

复制代码
step = (19-1)/(32-1) = 18/31 ≈ 0.5806

计算前 5 个插值结果

复制代码
k=0 → idx=0.0000 → LV=11.00
k=1 → idx=0.5806 → LV=11.00 - 1×0.58 = 10.42
k=2 → idx=1.1613 → LV=10.00 - 1×0.16 = 9.84
k=3 → idx=1.7419 → LV=10.00 - 1×0.74 = 9.26
k=4 → idx=2.3226 → LV=9.00 - 1×0.32 = 8.68
...

五、Q12 定点插值

1. 锚点 Q12 数组

cpp 复制代码
const int32_t LV8_Q12[19] = {
45056,40960,36864,32768,28672,24576,20480,16384,12288,
12288,8192,4096,0,-4096,-8192,-12288,-16384,-20480,-24576
};

2. 插值函数

cpp 复制代码
// 一维线性插值(Q12格式)
// x: 输入索引 0~18
// lut: 锚点表
// len: 锚点数量 =19
int32_t interp_lv_q12(float x, const int32_t *lut, int len)
{
    int i = (int)x;
    float t = x - i;
    
    if(i >= len-1) return lut[len-1];
    if(i < 0) return lut[0];
    
    int32_t y0 = lut[i];
    int32_t y1 = lut[i+1];
    return y0 + (int32_t)((y1 - y0) * t);
}

3. 生成任意长度 LUT

cpp 复制代码
// 示例:19点 → 插值生成 256 点 LV LUT
#define OUT_POINTS 256
int32_t LV8_OUT[OUT_POINTS];

void generate_lut(void)
{
    float step = (19.0f - 1.0f) / (OUT_POINTS - 1);
    for(int k=0; k<OUT_POINTS; k++)
    {
        float x = k * step;
        LV8_OUT[k] = interp_lv_q12(x, LV8_Q12, 19);
    }
}

六、具体实现

cpp 复制代码
#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

// ==================== 19个LV原始锚点(正确顺序) ====================
const float lv_anchors[FPS_COUNT][ANCHOR_CNT] = {
    // 30fps
    {12.91f,11.91f,10.91f,9.91f,8.91f,7.91f,6.91f,5.91f,4.91f,
     4.91f,3.91f,2.91f,1.91f,0.91f,-0.09f,-1.09f,-2.09f,-3.09f,-4.09f},
    // 25fps
    {12.64f,11.64f,10.64f,9.64f,8.64f,7.64f,6.64f,5.64f,4.64f,
     4.64f,3.64f,2.64f,1.64f,0.64f,-0.36f,-1.36f,-2.36f,-3.36f,-4.36f},
    // 20fps
    {12.32f,11.32f,10.32f,9.32f,8.32f,7.32f,6.32f,5.32f,4.32f,
     4.32f,3.32f,2.32f,1.32f,0.32f,-0.68f,-1.68f,-2.68f,-3.68f,-4.68f},
    // 10fps
    {11.32f,10.32f,9.32f,8.32f,7.32f,6.32f,5.32f,4.32f,3.32f,
     3.32f,2.32f,1.32f,0.32f,-0.68f,-1.68f,-2.68f,-3.68f,-4.68f,-5.68f},
    // 8fps
    {11.00f,10.00f,9.00f,8.00f,7.00f,6.00f,5.00f,4.00f,3.00f,
     3.00f,2.00f,1.00f,0.00f,-1.00f,-2.00f,-3.00f,-4.00f,-5.00f,-6.00f},
};

// ==================== 输出LUT ====================
float lv_lut_float[FPS_COUNT][OUTPUT_POINTS];  // 浮点结果
int32_t lv_lut_q12[FPS_COUNT][OUTPUT_POINTS]; // Q12定点结果

// ==================== 核心插值函数(严格按你公式) ====================
// 公式:
// i = floor(idx)
// t = idx - i
// LV = LV[i] + (LV[i+1] - LV[i]) * t
float linear_interp(float idx, const float *anchor, int anchor_num)
{
    // 左区间索引
    int i = (int)idx;

    // 安全边界
    if (i < 0)                  return anchor[0];
    if (i >= anchor_num - 1)    return anchor[anchor_num - 1];

    // 比例 t
    float t = idx - (float)i;

    // 插值计算
    return anchor[i] + (anchor[i+1] - 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_result(void)
{
    const char *fps_list[] = {"30fps", "25fps", "20fps", "10fps", "8fps"};

    for (int fps = 0; fps < FPS_COUNT; fps++)
    {
        printf("\n// ========== %s 256点 Q12 LUT ==========\n", fps_list[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_result();
    return 0;
}
相关推荐
西岸行者3 小时前
BF信号是如何多路合一的
算法
大熊背3 小时前
ISP Pipeline中Lv实现方式探究之二
自动白平衡·自动曝光·lv·isppipeline·bv
大熊背3 小时前
ISP中Lv和ISO系统并存的意义
自动曝光·iso·lv·isppipeline
大熊背3 小时前
ISP Pipeline中Lv实现方式探究之一
算法·自动白平衡·自动曝光
罗西的思考4 小时前
【OpenClaw】通过 Nanobot 源码学习架构---(5)Context
人工智能·算法·机器学习
Liudef064 小时前
后量子密码学(PQC)深度解析:算法原理、标准进展与软件开发行业的影响
算法·密码学·量子计算
OYpBNTQXi5 小时前
SEAL全同态加密CKKS方案入门详解
算法·机器学习·同态加密
yuannl106 小时前
数据结构----队列的实现
数据结构
蚂蚁数据AntData6 小时前
破解AI“机器味“困境:HeartBench评测实践详解
大数据·人工智能·算法·机器学习·语言模型·开源