ISP-BLC(Black Level Correction)

文章介绍了ISP-BLC(BlackLevelCorrection)在图像处理中的作用,详细讲解了黑电平的产生原因,包括暗电流和传感器ADC转换精度的影响。通过采集黑电平数据并进行矫正,以改善图像暗部细节。文章还提供了C语言实现的BLC算法,并讨论了定点化处理以适应嵌入式设备的需求,最后提到了算法优化的关键点,如避免除法运算。
摘要由CSDN通过智能技术生成

ISP-BLC(Black Level Correction)

1. BL 的定义

BL 一般翻译为黑电平。实际AD芯片的精度不足以将电压值很小的一部分转换出来,芯片厂会刻意添加一个固定的偏移量pedestal以达到阈值转换电压。sensor的电路本身会存在暗电流,导致在没有光线照射的时候,像素单位也有一定的输出电压。实际过程中暗电流会因为曝光时间(温度) 和 AGain(亮度增益) 变化且不均匀,特别是在低照度场景,Sensor的曝光时间偏高且增益较高,图像的BL分布会不均匀。然而,一般场景处理时通常是一个均值。

2.BL 产生的原因

2.1暗电流

暗电流(dark current),也称无照电流,指在没有光照射的状态下,在太阳电池、光敏二极管、光导电元件、光电管等的受光元件中流动的电流,一般由于载流子的扩散或者器件内部缺陷造成。目前常用的CMOS就是光电器件,所以也会有暗电流,导致光照为0的时候也有电压输出。这样导致在全黑场景下,图相的输出不为0。

 

上图是二极管的伏安特性曲线,从图中可以看出在反向截止区域电流并不是完全为0,而我们的COMS内部其实也是PN结构成的,所以符合该特性,并且光电二极管是工作在反向电压下,所以无光照是的这个微小电流就是暗电流。

2.2 sensor ADC转换精度

sensor将模拟信号转换为数字信号时,由于转换精度限制无法将电压值很小的一部分给区分开来,故需要加上一个值来保证图像暗部细节。

 

结合上图所示:

            1. sensor 输出到ISP模块前有AD转换过程,然后AD芯片都或有一个灵敏度,当电压低于该阈值时无法进行AD转换,所以就需要添加一个偏置量,使得原本低于阈值的部分也可以被AD转换。
            2. 人眼构造特性同样对暗部(8Bit低于64)区间更加敏感,而对亮度变化(8Bit 200-255)区间变化不敏感,因此需要将上述曲线AB 添加偏置量N后上移。牺牲图像高亮区域(人眼不敏感区间),提升原图像暗区的亮度,从而保留更多的细节。

3. BL -->BLC 黑电平矫正

参考海思安防和华为手机ISP pipeLine等,BLC都是作为ISP PipeLine的第一个模块,BLC的数值跟图像Bit相关。而且BLC校正值跟后续图像ISP 模块的 AWBG、CCM等颜色模块关联性较强。即:BLC看是简单,其计算精确度,直接会影响图像的颜色风格,根据经验BLC值矫正过多图像偏绿,矫正过少,图像偏蓝紫。

BLC算法目前分为sensor端和ISP 端两部分,目前笔者对前部分sensor端处理接触较少,故不作为本文介绍终点。文本主要是结合笔者的工作中工程经验,从实际应用较多的方案ISP端处理方法进行讲解。

3.1 BLC值确定

一般拿到一个sensor和对应的工装,都需要在标准光源箱下采集D75 D65 D50 CWF TL84 A H光等7种光源下的MCC、ISO12233、灰卡等数据利用Imatest进行测评。BLC矫正数据采集方法:

  1. 关闭光源箱和环境等,用黑布蒙住镜头,连续采集3张Raw数据,并做保存。

  2. 确定Raw数据的BayerPattern, 统计出每个通道R GR GB B的像素均值,Avg(R) Avg(GR) Avg(GB) Avg(B),即可作为图像像素的黑电平值。

4. BLC算法实现

本文采用C语言实现BLC的核心代码:

RET_STATUS isp_blc::isp_blc_process(uint16* pusSrcIn, uint16* pusDstOut)
{
    RET_STATUS RET = RET_SUCESS;
    uint16* pusSrc = pusSrcIn;
    uint16* pusDst = pusDstOut;
    int iTmpVal = 0;
    int iIndex  = 0;
    int iMaxVal = 0;
    for (int y = 0; y < rawHeight; y++)
    {
        for (int x = 0; x < rawWidth; x++)
        {
            //确定bayerPattern
            iIndex = SelectIndex(bayerPatern, y, x);
            //1. 减去黑电平值,像素取值范围发生变化[-blc,iMaxVal - blc]
            iTmpVal = (1.0f * pusSrc[y * rawWidth + x] - sBlackLevel[iIndex]);
            //2.将减去黑电平,取值范围映射至[0, iMaxVal]
            iTmpVal = (iTmpVal * 1.0f) / (1.0f* (iMaxVal  - sBlackLevel[iIndex])) * iMaxVal;
            pusDstOut[y * rawWidth + x] = CLIP(iTmpVal, 0, iMaxVal);
        }
    }
    return RET;
}

上述算法模型开发已经完成,大家注意到中间的计算过程数据都是浮点运算(float-point)。接触过ISP算法的同学应该有所了解,基本应用场景基本是嵌入式设备,ARM、DSP以及FPGA等。因此,当算法在实际移动设备上运行时,由于浮点运算复杂度远大于一个整数运算,FPGA芯片不支持浮点(float-point)运算。本文给出其算法定点化(fix-point)代码:

#define BLCFIXQUM       (14)
RET_STATUS isp_blc::isp_blc_process_Fix(uint16* pusSrcIn, uint16* pusDstOut)
{
    RET_STATUS RET = RET_SUCESS;
    uint16* pusSrc = pusSrcIn;
    uint16* pusDst = pusDstOut;
    int iScaleFix = 0;
    int iTmpVal = 0;
    int iIndex  = 0;
​
    int iMaxVal = (1 << rawBit) - 1;//此处左移运算符必须加括号吗?
    int iAvgBlc = (sBlackLevel[R_INDEX] + sBlackLevel[GR_INDEX] + sBlackLevel[GB_INDEX] + sBlackLevel[B_INDEX]) >> 2;
    //
    for (int y = 0; y < rawHeight; y++)
    {
        for (int x = 0; x < rawWidth; x++)
        {
            //确定bayerPattern
            iIndex = SelectIndex(bayerPatern, y, x);
            //1. 减去黑电平值,像素取值范围发生变化[0, iMaxVal - blc]
            iTmpVal = (pusSrc[y * rawWidth + x] - sBlackLevel[iIndex]);
            //2. 将减去黑电平,取值范围映射至[0, iMaxVal]
            iScaleFix = iTmpVal * (1 << BLCFIXQUM) / (iMaxVal - sBlackLevel[iIndex]);
            iTmpVal = (iScaleFix + (1 << (BLCFIXQUM - 1)) * iMaxVal) >> BLCFIXQUM;
            pusDstOut[y * rawWidth + x] = CLIP(iTmpVal, 0, iMaxVal);
        }
    }
    return RET;
}

上述代码完成定点化部分后,基本可以在各类嵌入式芯片上运行;由于ISP算法处理图像是逐像素处理密集计算,往往对算力要求较高且算力消耗较大。代码执行各类运算无外乎加法、减法、乘法、除法以及函数math库提供的运算。其中加法和减法所需指令周期最少(一般为1个指令周期),其次是乘法(ARM一般是4个指令周期),除法消耗指令周期最大特别是有符号数的浮点运算。因此,一个ISP算法完成后,需要继续优化,遵循的核心原则:

  • 不用除法,将除法转成乘法

    除法--->乘法的转换一般是通过LutTable完成且必须定点化

  • 乘法转换成2的幂运算,2的幂运算可用移位操作完成比如 :

    $$
    9=2^3+1,x * 9 = x *(2^3+1) = (x<<3) + x
    $$

上述将一个乘法转换成移位和加运算,极大降低运算所消耗资源。

继续优化上述定点化代码,去除无除法运算版本如下:

RET_STATUS isp_blc_process_Fix(uint16* pusSrcIn, uint16* pusDstOut, short rawBit, short *sBlackLevel, eBayerPattern bayerPatern)
{
    RET_STATUS RET = RET_SUCESS;
    uint16* pusSrc = pusSrcIn;
    uint16* pusDst = pusDstOut;
    long long  iScaleFix = 0;
    long long  iTmpVal = 0;
    int iIndex  = 0;
​
    int iMaxVal = (1 << rawBit) - 1;//此处左移运算符必须加括号 其优先级小于 -
    //1. 设置12Bit 查找表
    for (int y = 0; y < RAW_HEIGHT; y++)
    {
        for (int x = 0; x < RAW_WIDTH; x++)
        {
            iIndex = SelectIndex(bayerPatern, y, x);
            //1. 减去黑电平值,像素取值范围发生变化[0, iMaxVal - blc]
            iTmpVal = (pusSrc[y * RAW_WIDTH + x] - sBlackLevel[iIndex]);
            iTmpVal = CLIP(iTmpVal, 0, iMaxVal);
            //2. 将减去黑电平,取值范围映射至[0, iMaxVal]
            iScaleFix = iMaxVal * LutTable[(iMaxVal - sBlackLevel[iIndex])];
            iTmpVal = (iTmpVal * iMaxVal + (1 << (BLCFIXQUM - 1))) >> BLCFIXQUM;
            pusDstOut[y * RAW_WIDTH + x] = CLIP(iTmpVal, 0, iMaxVal);
        }
    }
    return RET;
}

查找表LutTable的生成结合除法简化和定点化,对于10Bit图像 (iMaxVal - sBlackLevel[iIndex]) 取值范围:[0, iMaxVal]。LutTable生成方式如下:

    FILE* fpLog = fopen("weight.txt", "wb");
    for (int i = 1; i < 1024; i++)
    {
        iTmp = (1.0f / i) * (1 << 14);
        fprintf(fp, "%d, ",iTmp);
        if ((i + 1) % 32 == 0)
        {
            fprintf(fp, "\n");
        }
    }
    fclose(fpLog);

至此BLC算法开发与定点化彻底完成,总结如下:

  1. 黑电平指BL需在灯箱全黑环境下拍摄Raw数据,根据Raw数据值标定出实际BL值。

  2. 根据BLC算法原理完成算法初版,一般为浮点数运算C Model。

  3. 将初始C Model 进行定点化,具体定点化精度根据实际应用场景确定(一般需要测试看图像效果),若定点化精度不够,则需要增加位宽,重新定点化。

  4. 定点化后需要跟浮点版本进行比价,查看图像效果要保证完全一致且无明显错误值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值