
目录
[一、 19 个 LV 锚点序列(8fps 为例)](#一、 19 个 LV 锚点序列(8fps 为例))
[三、按 LV 节点插值的完整步骤](#三、按 LV 节点插值的完整步骤)
[四、实例演示(19 节点 → 插值到 32 个中间 LV)](#四、实例演示(19 节点 → 插值到 32 个中间 LV))
[五、Q12 定点插值](#五、Q12 定点插值)
下述 博文中,详细阐述了根据Lv值进行亮度场景的划分原理。但是实际应用中,ISP驱动库是按照定点方式进行相应插值的。那么,如何下述博文所述方法转换为定点插值计算呢?
固定顺序的 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;
}