news 2026/4/24 0:48:38

ISP Pipeline中Lv实现方式探究之六--lv值计算再优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ISP Pipeline中Lv实现方式探究之六--lv值计算再优化
  • 目录

    一、优劣对比

    1️⃣ 查表 + 线性插值

    ✅ 优点

    ❌ 缺点

    适用场景

    2️⃣ 归一化 + 多项式逼近(如 2 次 / 3 次多项式)

    ✅ 优点

    ❌ 缺点

    适用场景

    二、代码实现

    三、总结


  • ISP Pipeline中Lv实现方式探究之一
  • ISP Pipeline中Lv实现方式探究之二
  • ISP Pipeline中Lv实现方式探究之三--lv计算定点实现
  • ISP Pipeline中Lv实现方式探究之四----正LV值定点实现
  • ISP Pipeline中Lv实现方式探究之五--lv值计算框架优化

一、优劣对比

如上博文,都是讲解了Lv计算的基本原理。实际在ISP驱动中使用的是LV的定点数据进行相应的后续操作。比如根据Lv定点值进行线性插值,判断当前场景亮度等级等。博文ISP Pipeline中Lv实现方式探究之五--lv值计算框架优化使用的是多项式+归一化的方式进行Lv定点计算。为了提高速度,我们使用查找表+线性插值进行相应的优化。其两种方式的优劣为:

1️⃣查表 + 线性插值

✅ 优点

速度极快:只有移位、查表、乘法

  • 10~20 个时钟周期搞定
  • 无循环、无迭代

实现简单、不易出错

  • 逻辑固定,调试一次永久用

定点运算完美适配

  • Q10/Q15 随便用
  • 无溢出风险(控制好位数)

精度可控

  • 64 表:低精度
  • 128 表:中等精度(最常用)
  • 256 表:高精度

无除法、无复杂运算→ 单片机 / DSP/ASIC/FPGA 最爱

❌ 缺点

适用场景

90% 嵌入式项目、电机控制、仪表、电源、音频、通信


2️⃣归一化 + 多项式逼近(如 2 次 / 3 次多项式)

公式示例:

log2(1+x) ≈ a0 + a1*x + a2*x² + a3*x³

  • 占用 Flash
    • 256 表 ≈ 512 字节
    • 64 表 ≈ 128 字节
  • 精度不如高阶多项式

✅ 优点

几乎不占 Flash

  • 只有几个系数,20 字节以内

精度极高

  • 3 次多项式误差 < 0.001%

适合超小资源 MCU(如 8051、tiny 单片机)

❌ 缺点

适用场景

极小 Flash、极高精度、非常低速的系统

优劣总结:

  • 运算量大
    • 乘法多、平方、立方
    • 速度比 LUT 慢 3~10 倍
  • 定点容易溢出
    • 平方 / 立方会扩位,必须小心处理
  • 调试难、系数难拟合
    • 系数需要 MATLAB/Python 拟合
    • 改一次精度重算一次
指标查表 + 线性插值归一化 + 多项式
速度⭐⭐⭐⭐⭐ 最快⭐⭐ 慢
Flash 占用⭐⭐ 中等⭐⭐⭐⭐⭐ 极小
定点实现难度⭐⭐ 简单⭐⭐⭐⭐⭐ 难
精度⭐⭐⭐ 良好⭐⭐⭐⭐⭐ 极高
调试难度⭐ 极易⭐⭐⭐⭐⭐ 难
嵌入式推荐度⭐⭐⭐⭐⭐ 首选⭐⭐⭐ 特殊场景

二、代码实现

Q10定点实现代码:

/////////////////////////Q10定点输入 //============================================================================= // 1. 64点 LUT 版本 //============================================================================= #define LUT_SIZE_64 64 static const int16_t log2_table_q10_64[LUT_SIZE_64 + 1] = { 0, 23, 45, 68, 90, 111, 132, 153, 174, 194, 214, 234, 254, 273, 292, 311, 330, 348, 366, 384, 402, 419, 436, 454, 470, 487, 504, 520, 536, 552, 568, 584, 599, 614, 629, 644, 659, 674, 689, 703, 717, 731, 745, 759, 773, 787, 800, 813, 827, 840, 853, 866, 879, 891, 904, 916, 929, 941, 953, 965, 977, 989, 1001, 1012, 1024, }; int32_t q10_log2_64lut(uint32_t x) { uint32_t val = x; int32_t exp = 0; if (x <= 0) return 0; // 归一化到 [1024, 2047] while (val >= 2UL * Q10_SCALE) { val >>= 1; exp++; } while (val < Q10_SCALE) { val <<= 1; exp--; } uint32_t delta = val - Q10_SCALE; uint8_t idx = (uint8_t)(delta >> 4); // 64点:右移4位 uint8_t frac = (uint8_t)(delta & 0x0F); // 插值 0~15 int16_t y0 = log2_table_q10_64[idx]; int16_t y1 = log2_table_q10_64[idx + 1]; // 插值公式:y0 + (y1-y0)*frac/16 int32_t interp = y0 + (((int32_t)(y1 - y0) * frac) >> 4); int32_t res = (exp * Q10_SCALE) + interp; return res; } //============================================================================= // 2. 128点 LUT 版本 //============================================================================= #define LUT_SIZE_128 128 static const int16_t log2_table_q10_128[LUT_SIZE_128 + 1] = { 0, 11, 23, 34, 45, 57, 68, 79, 90, 100, 111, 122, 132, 143, 153, 164, 174, 184, 194, 204, 214, 224, 234, 244, 254, 264, 273, 283, 292, 302, 311, 320, 330, 339, 348, 357, 366, 375, 384, 393, 402, 411, 419, 428, 436, 445, 454, 462, 470, 479, 487, 495, 504, 512, 520, 528, 536, 544, 552, 560, 568, 576, 584, 591, 599, 607, 614, 622, 629, 637, 644, 652, 659, 667, 674, 681, 689, 696, 703, 710, 717, 724, 731, 738, 745, 752, 759, 766, 773, 780, 787, 793, 800, 807, 813, 820, 827, 833, 840, 846, 853, 859, 866, 872, 879, 885, 891, 898, 904, 910, 916, 922, 929, 935, 941, 947, 953, 959, 965, 971, 977, 983, 989, 995, 1001, 1007, 1012, 1018, 1024, }; int32_t q10_log2_128lut(uint32_t x) { uint32_t val = x; int32_t exp = 0; if (x <= 0) return 0; while (val >= 2UL * Q10_SCALE) { val >>= 1; exp++; } while (val < Q10_SCALE) { val <<= 1; exp--; } uint32_t delta = val - Q10_SCALE; uint8_t idx = (uint8_t)(delta >> 3); // 128点:右移3位 uint8_t frac = (uint8_t)(delta & 0x07); // 插值 0~7 int16_t y0 = log2_table_q10_128[idx]; int16_t y1 = log2_table_q10_128[idx + 1]; // 插值公式:y0 + (y1-y0)*frac/8 int32_t interp = y0 + (((int32_t)(y1 - y0) * frac) >> 3); int32_t res = (exp * Q10_SCALE) + interp; return res; } //256 LUT // Q10 定点数定义:1位符号 + 5位整数 + 10位小数,缩放系数 2^10 = 1024 // 高精度 256 点 log2 真值表 // 对应区间 [1.0, 2.0),步长 1/256,存储 Q10 格式的 log2(m) 真值 static const int16_t log2_table_q10_256[LUT_SIZE_256 + 1] = { 0, 6, 11, 17, 23, 29, 34, 40, 45, 51, 57, 62, 68, 73, 79, 84, 90, 95, 100, 106, 111, 116, 122, 127, 132, 138, 143, 148, 153, 159, 164, 169, 174, 179, 184, 189, 194, 199, 204, 209, 214, 219, 224, 229, 234, 239, 244, 249, 254, 259, 264, 268, 273, 278, 283, 288, 292, 297, 302, 306, 311, 316, 320, 325, 330, 334, 339, 343, 348, 353, 357, 362, 366, 371, 375, 380, 384, 388, 393, 397, 402, 406, 411, 415, 419, 424, 428, 432, 436, 441, 445, 449, 454, 458, 462, 466, 470, 475, 479, 483, 487, 491, 495, 500, 504, 508, 512, 516, 520, 524, 528, 532, 536, 540, 544, 548, 552, 556, 560, 564, 568, 572, 576, 580, 584, 587, 591, 595, 599, 603, 607, 610, 614, 618, 622, 626, 629, 633, 637, 641, 644, 648, 652, 656, 659, 663, 667, 670, 674, 678, 681, 685, 689, 692, 696, 699, 703, 707, 710, 714, 717, 721, 724, 728, 731, 735, 738, 742, 745, 749, 752, 756, 759, 763, 766, 770, 773, 776, 780, 783, 787, 790, 793, 797, 800, 803, 807, 810, 813, 817, 820, 823, 827, 830, 833, 837, 840, 843, 846, 850, 853, 856, 859, 863, 866, 869, 872, 875, 879, 882, 885, 888, 891, 894, 898, 901, 904, 907, 910, 913, 916, 919, 922, 926, 929, 932, 935, 938, 941, 944, 947, 950, 953, 956, 959, 962, 965, 968, 971, 974, 977, 980, 983, 986, 989, 992, 995, 998, 1001, 1004, 1007, 1010, 1012, 1015, 1018, 1021, 1024, }; /** * @brief Q10 定点数 log2 计算 (查表 + 线性插值,高精度无错误) * @param x 输入:Q10 格式有符号定点数 (x > 0) * @return 输出:Q10 格式 log2(x) */ int32_t q10_log2_256lut(int32_t x) { // 1. 输入保护:log2 仅支持正数 if (x <= 0) { return 0; // 非法输入返回0 } int32_t val = x; int32_t exponent = 0; // 2. 核心:归一化,将 val 缩放到 [1024, 2047] (Q10 的 [1.0, 2.0)) while (val >= 2 * Q10_SCALE) { val >>= 1; exponent++; } while (val < Q10_SCALE) { val <<= 1; exponent--; } // 3. 计算查表索引 + 插值小数位 (100% 正确) uint32_t delta = val - Q10_SCALE; // 范围 [0, 1023] uint8_t idx = delta >> 2; // 高8位:0~255 查表索引 uint8_t frac = delta & 0x03; // 低2位:0~3 插值小数 // 4. 线性插值(防溢出 + 高精度) int16_t y0 = log2_table_q10_256[idx]; int16_t y1 = (idx < 255) ? log2_table_q10_256[idx + 1] : y0; int32_t interp = y0 + ((int32_t)(y1 - y0) * frac) / 4; // 5. 合并结果:log2(x) = 指数 + 小数部分 int32_t result = (exponent * Q10_SCALE) + interp; return result; } void test_q10_log2(void) { uint32_t i = 0; // log2(10) ≈ 3.3219 int32_t res64, res128, res256; FILE *fp = fopen("lv_fixed_q10_LUT-new.txt", "w"); fprintf(fp, "ID Q10-64 LUT Q10 64 lut 浮点 Q10-128 LUT Q10-128 LUT 浮点 Q10-256 LUT Q10-256 LUT 浮点 理论值\n"); for (i = 1024; i <= 1024 * 512; i+=103) { res64 = q10_log2_64lut(i); res128 = q10_log2_128lut(i); res256 = q10_log2_256lut(i); fprintf(fp, "%.4f %4d %.6f %4d %.6f %4d %.6f %.6f\n", (float)i / 1024, res64, (float)res64 / Q10_SCALE, res128, (float)res128 / Q10_SCALE, res256, (float)res256 / Q10_SCALE, log2f((float)i/1024.0)); //fprintf(fp, "%.6f %d %.6f %.6f\n", (float)i / 1024, res256, (float)res256 / Q10_SCALE, log2f((float)i / 1024.0)); } } int main() { test_q10_log2(); return 0; }

结果对比,64LUT/128LUT/256LUT计算的结果一致,和理论值差距比较小。后面三个为理论值和三个查找表计算你的值差值。对比如下:

Q8定点实现代码:

static const int16_t log2_table_q8_64[LUT_SIZE_64 + 1] = { 0, 6, 11, 17, 22, 28, 33, 38, 44, 49, 54, 59, 63, 68, 73, 78, 82, 87, 92, 96, 100, 105, 109, 113, 118, 122, 126, 130, 134, 138, 142, 146, 150, 154, 157, 161, 165, 169, 172, 176, 179, 183, 186, 190, 193, 197, 200, 203, 207, 210, 213, 216, 220, 223, 226, 229, 232, 235, 238, 241, 244, 247, 250, 253, 256, }; static const int16_t log2_table_q8_128[LUT_SIZE_128 + 1] = { 0, 3, 6, 9, 11, 14, 17, 20, 22, 25, 28, 30, 33, 36, 38, 41, 44, 46, 49, 51, 54, 56, 59, 61, 63, 66, 68, 71, 73, 75, 78, 80, 82, 85, 87, 89, 92, 94, 96, 98, 100, 103, 105, 107, 109, 111, 113, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 155, 157, 159, 161, 163, 165, 167, 169, 170, 172, 174, 176, 178, 179, 181, 183, 185, 186, 188, 190, 192, 193, 195, 197, 198, 200, 202, 203, 205, 207, 208, 210, 212, 213, 215, 216, 218, 220, 221, 223, 224, 226, 228, 229, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246, 247, 249, 250, 252, 253, 255, 256, }; static const int16_t log2_table_q8_256[LUT_SIZE_256 + 1] = { 0, 1, 3, 4, 6, 7, 9, 10, 11, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30, 32, 33, 34, 36, 37, 38, 40, 41, 42, 44, 45, 46, 47, 49, 50, 51, 52, 54, 55, 56, 57, 59, 60, 61, 62, 63, 65, 66, 67, 68, 69, 71, 72, 73, 74, 75, 77, 78, 79, 80, 81, 82, 84, 85, 86, 87, 88, 89, 90, 92, 93, 94, 95, 96, 97, 98, 99, 100, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 183, 184, 185, 185, 186, 187, 188, 189, 190, 191, 192, 192, 193, 194, 195, 196, 197, 198, 198, 199, 200, 201, 202, 203, 203, 204, 205, 206, 207, 208, 208, 209, 210, 211, 212, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 228, 228, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 249, 250, 251, 252, 252, 253, 254, 255, 255, 256, }; //= == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == = // Q8 格式 log2 计算(64/128/256 LUT) // 关键:Q8 归一化到 [256,511] → delta 8bit //============================================================================= int32_t q8_log2_64lut(int32_t x) { uint32_t val = (uint32_t)x; int32_t exp = 0; if (x <= 0) return 0; while (val >= 2UL * Q8_SCALE){ val >>= 1; exp++; } while (val < Q8_SCALE) { val <<= 1; exp--; } uint32_t d = val - Q8_SCALE; // [0,255] uint8_t idx = d >> 2; // 64点:右移2位 uint8_t frac = d & 0x03; // 插值 0~3 int32_t y0 = log2_table_q8_64[idx]; int32_t y1 = log2_table_q8_64[idx + 1]; int32_t interp = y0 + ((y1 - y0)*frac) >> 2; return (exp * Q8_SCALE) + interp; // 输出 Q10 } int32_t q8_log2_128lut(int32_t x) { uint32_t val = (uint32_t)x; int32_t exp = 0; if (x <= 0) return 0; while (val >= 2UL * Q8_SCALE){ val >>= 1; exp++; } while (val < Q8_SCALE) { val <<= 1; exp--; } uint32_t d = val - Q8_SCALE; uint8_t idx = d >> 1; uint8_t frac = d & 0x01; int32_t y0 = log2_table_q8_128[idx]; int32_t y1 = log2_table_q8_128[idx + 1]; int32_t interp = y0 + ((y1 - y0)*frac) >> 1; return (exp * Q8_SCALE) + interp; } int32_t q8_log2_256lut(int32_t x) { uint32_t val = (uint32_t)x; int32_t exp = 0; if (x <= 0) return 0; while (val >= 2UL * Q8_SCALE){ val >>= 1; exp++; } while (val < Q8_SCALE) { val <<= 1; exp--; } uint8_t d = val - Q8_SCALE; uint8_t idx = d; uint8_t frac = 0; int32_t y0 = log2_table_q8_256[idx]; return (exp * Q8_SCALE) + y0; } void test_q8_log2(void) { uint32_t i = 0; // log2(10) ≈ 3.3219 int32_t res64, res128, res256; FILE *fp = fopen("lv_fixed_q8_LUT-new.txt", "w"); fprintf(fp, "ID Q10-64 LUT Q10 64 lut 浮点 Q10-128 LUT Q10-128 LUT 浮点 Q10-256 LUT Q10-256 LUT 浮点 理论值\n"); for (i = 256; i <= 256 * 512; i += 26) { res64 = q8_log2_64lut(i); res128 = q8_log2_128lut(i); res256 = q8_log2_256lut(i); fprintf(fp, "%.4f %4d %.6f %4d %.6f %4d %.6f %.6f\n", (float)i / 256, res64, (float)res64 / Q8_SCALE, res128, (float)res128 / Q8_SCALE, res256, (float)res256 / Q8_SCALE, log2f((float)i / 256.0)); //fprintf(fp, "%.6f %d %.6f %.6f\n", (float)i / 1024, res256, (float)res256 / Q10_SCALE, log2f((float)i / 1024.0)); } } int main() { test_q8_log2(); return 0; }

结果对比,64LUT/128LUT/256LUT计算中,256LUT的结果精度比较高,和理论值差距比较小。后面三个为理论值和三个查找表计算的差值。对比如下:

三、总结

兼顾速度和内存等多方面因素,我们可以考虑是使用Q10 的64LUT计算log2定点数据。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/24 0:42:08

终极指南:如何用LitCAD免费开源CAD软件快速上手二维绘图

终极指南&#xff1a;如何用LitCAD免费开源CAD软件快速上手二维绘图 【免费下载链接】LitCAD A very simple CAD developed by C#. 项目地址: https://gitcode.com/gh_mirrors/li/LitCAD LitCAD是一款基于C#开发的轻量级开源CAD软件&#xff0c;专为二维绘图设计而生。如…

作者头像 李华