Hello!距这个小ISP的软件实现大概过去两年了,一直没有打起精神来将连载写完。但是我们绝不烂尾,绝不!

Image Signal Processing-第四章-LSC, CC的原理和软件实现

  • 1. Lens Shading Correction (LSC)
    • 1.1 LSC原理
    • 1.2 LSC软件实现
    • 1.3 LSC效果
  • 2. Color Correction (CC)
    • 2.1 CC原理
    • 2.2 CC软件实现
    • 2.3 CC效果

连载
Image Signal Processing(ISP)-第一章-ISP基础以及Raw的读取显示
Image Signal Processing(ISP)-第二章-Demosaic去马赛克以及BMP软件实现
Image Signal Processing(ISP)-第三章-BCL, WB, Gamma的原理和软件实现
Image Signal Processing(ISP)-第四章-LSC, CC的原理和软件实现

Github
BoyPao/ImageSignalProcessing-ISP

上文我们介绍了BLC,WB,Gamma三种基础的优化操作,并对图像数据进行了这几种基础矫正,图片的效果向人眼真实感受靠近。但明显的是,与商用isp处理的效果有非常大的差距,所以同志们要继续努力呀。
让我们先回顾一下上文结束时的修改效果以及图片仍然存在的问题吧。

此阶段主要问题:1.图片边角的亮度低,2. 图片色彩饱和度很低。

本文将介绍针对这两个问题的解决方法:1. LSC,2.CC
首先我们先解决亮度不均的问题。

1. Lens Shading Correction (LSC)

我们看到上图的亮度是不均匀的,较亮的部分集中在图像的中央,而周围的部分相比而言要暗很多。这种亮度不均的现象主要是由于镜头对光线的折射造成的。Lens shading译为镜头阴影,其指的就是这种现象。

如上图,光学镜头一般采用镜片组实现对外界光的采集,由于CMOS的尺寸一般较小,镜片组对光路的导向总体上需要是集中的。因此大部分光将打到CMOS的中间区域,而四周的光强会相对小一些。对于手机镜头,安防镜头,无人机镜头等这类小镜头来说,这种显现更为明显。

1.1 LSC原理

Lens Shading Correction(LSC)就是处理 lens shading 这种亮度不均的一种矫正方法。当然了,原理是非常简单的,我们只需将成像的四周进行相应的增益计算,就可以使四周亮度看起来和中央亮度近似了。

上图表示的是存在lens shading现象的镜头对白色卡片或墙面拍照输出图片的最中间的一行以及任意某行的亮度分布。对这两行的中央较亮的像素点,设置增益趋近于1,让他们尽量保持这个像素值,而对于两侧较暗的像素,逐渐增大增益以提高矫正后的像素值。用这样的方法,来解决lens shading。

说到Shading,还有一种叫color shading的现象。指的是由于镜片对不同频率的光折射率不同造成各种颜色的光的lens shading 程度轻重不同的现象。也就是说对同一镜片组来说折射率大的色光,其lens shading更严重一些。为解决这个问题,我们的方法也很简单,就是对各种颜色通道采取不同的增益,来分而治之。

这里需要强调的是,我们对各通道采取不同的增益并不意味着我们是在改变色调,因为事实上各通道的增益都是中间小四周大的,差异值是非常小的数字。而亮度值就是各个通道像素值经过加权平均所得,因此此处对各色彩通道采用略有差异的增益,实质上是希望改变整体亮度,而不是色调的。

1.2 LSC软件实现

以下贴出软件实现。

BZResult BZ_LensShadingCorrection(void* data, LIB_PARAMS* pParams, ISP_CALLBACKS CBs, ...)
{BZResult result = BZ_SUCCESS;(void)CBs;if (!data) {result = BZ_INVALID_PARAM;BZLoge("data is null! result:%d", result);}if (SUCCESS(result)) {int32_t width, height;LIB_BAYER_ORDER bayerOrder = LIB_BAYER_ORDER_NUM;int32_t i, j;float* pR = nullptr;float* pGr = nullptr;float* pGb = nullptr;float* pB = nullptr;float* pLut = nullptr;width = pParams->info.width;height = pParams->info.height;bayerOrder = pParams->info.bayerOrder;BZLogd("width:%d height:%d type:%d", width, height, bayerOrder);switch (bayerOrder){case LIB_RGGB:pR = pParams->LSC_param.GainCh1;pGr = pParams->LSC_param.GainCh2;pGb = pParams->LSC_param.GainCh3;pB = pParams->LSC_param.GainCh4;break;case LIB_BGGR:pB = pParams->LSC_param.GainCh1;pGb = pParams->LSC_param.GainCh2;pGr = pParams->LSC_param.GainCh3;pR = pParams->LSC_param.GainCh4;break;case LIB_GRBG:pGr = pParams->LSC_param.GainCh1;pR = pParams->LSC_param.GainCh2;pB = pParams->LSC_param.GainCh3;pGb = pParams->LSC_param.GainCh4;break;case LIB_GBRG:pGb = pParams->LSC_param.GainCh1;pB = pParams->LSC_param.GainCh2;pR = pParams->LSC_param.GainCh3;pGr = pParams->LSC_param.GainCh4;break;default:result = BZ_INVALID_PARAM;BZLoge("Unsupported bayer order:%d result:%d", bayerOrder, result);break;}if (SUCCESS(result)) {for (i = 0; i < height; i++) {for (j = 0; j < width; j++) {if (i % 2 == 0 && j % 2 == 0) {pLut = pB;}if (i % 2 == 0 && j % 2 == 1) {pLut = pGb;}if (i % 2 == 1 && j % 2 == 0) {pLut = pGr;}if (i % 2 == 1 && j % 2 == 1) {pLut = pR;}static_cast<uint16_t*>(data)[i * width + j] = static_cast<uint16_t*>(data)[i * width + j] *LSCInterpolation(width, height,*(pLut + (LSC_LUT_HEIGHT - 1) * i / height * LSC_LUT_WIDTH + (LSC_LUT_WIDTH - 1) * j / width),*(pLut + (LSC_LUT_HEIGHT - 1) * i / height * LSC_LUT_WIDTH + (LSC_LUT_WIDTH - 1) * j / width + 1),*(pLut + ((LSC_LUT_HEIGHT - 1) * i / height + 1) * LSC_LUT_WIDTH + (LSC_LUT_WIDTH - 1) * j / width),*(pLut + ((LSC_LUT_HEIGHT - 1) * i / height + 1) * LSC_LUT_WIDTH + (LSC_LUT_WIDTH - 1) * j / width + 1),i,j);}}}}return result;
}

上面的函数写了很多行,但其实前面大部分代码是支持各种Bayer排布的代码,主要是最后调用了LSCInterpolation()来获取每个像素点的增益,再乘上像素点的值来实现矫正。

LSCInterpolation()是一个增益插值函数。为啥需要这个函数呢?前面说到了对不同颜色需要分而治之,那么在bayer域做 lsc 就需要4个 lut (lut是啥上一章介绍过) 来支持 4个颜色通道,这会造成数据量变得庞大。所以采取将图片划分为少量几块,每一块用一个lut值,最后每个像素通过临域块的 lut 值插值得到该像素 lsc增益的方法来降低 lut 的数据量。

用到的LSCInterpolation() 函数如下。

float LSCInterpolation(int32_t WIDTH, int32_t HEIGHT,float LT, float RT, float LB, float RB,int32_t row, int32_t col)
{float TempT, TempB, result;TempT = LT - (LT - RT) * (col % (WIDTH / (LSC_LUT_WIDTH - 1))) * (LSC_LUT_WIDTH - 1) / WIDTH;TempB = LB - (LB - RB) * (col % (WIDTH / (LSC_LUT_WIDTH - 1))) * (LSC_LUT_WIDTH - 1) / WIDTH;result = TempT - (TempT - TempB) * (row % (HEIGHT / (LSC_LUT_HEIGHT - 1))) * (LSC_LUT_HEIGHT - 1) / HEIGHT;return result;
}

其lsc的部分lut如下。

LSC_PARAM LSCPARM_1920x1080_D65_1000Lux = {//bGain{{ 2.934418,   2.6059866,  2.220798,   1.94021428, 1.74472368, 1.600148,   1.50334871, 1.44453084, 1.42655516, 1.45087564, 1.51691735, 1.620021,   1.77389872, 1.984156,   2.26868367, 2.685018,   3.086324   },{ 2.780221,   2.42652345, 2.055793,   1.81020319, 1.61290979, 1.47168612, 1.37039435, 1.312223,   1.292875,   1.31676388, 1.381954,   1.48782563, 1.63721418, 1.83661532, 2.09502649, 2.48011971, 2.89426875 },。。。。。。}//gbGain{{ 3.20395136, 2.84868073, 2.42183638, 2.12311363, 1.91179454, 1.756917,   1.64773118, 1.58069026, 1.55754256, 1.5834856,  1.645207,   1.74964738, 1.90877008, 2.1196,     2.41837454, 2.8678813,  3.28876615 },{ 3.04708457, 2.663059,   2.24919081, 1.97792578, 1.76208353, 1.60036659, 1.48812246, 1.417937,   1.39421225, 1.41655445, 1.48318923, 1.59559619, 1.75348139, 1.96583772, 2.233202,   2.655679,   3.10571742 },。。。。。。}//grGain{{ 3.25572634, 2.89292026, 2.4517138,  2.14141822, 1.91608775, 1.75056577, 1.6297735,  1.55698335, 1.53054082, 1.55629158, 1.62307167, 1.74096286, 1.90679991, 2.12313032, 2.435579,   2.887383,   3.319794   },{ 3.1139915,  2.71121383, 2.28292322, 2.001553,   1.773555,   1.60334086, 1.47839546, 1.40285826, 1.37624037, 1.39933944, 1.46882379, 1.58867931, 1.75515807, 1.97776151, 2.256206,   2.685162,   3.150439   },。。。。。。}//rGain{{ 3.26140237, 2.90829229, 2.44395447, 2.14117551, 1.92580986, 1.76098931, 1.64909816, 1.57704759, 1.554534,   1.58389091, 1.65474868, 1.77599037, 1.95483339, 2.17210364, 2.49666047, 2.99027562, 3.42638421 },{ 3.10735345, 2.697388,   2.26802278, 1.99615026, 1.77538717, 1.60802543, 1.48951221, 1.41702485, 1.39288378, 1.420929,   1.49541867, 1.61999846, 1.79255354, 2.01902175, 2.304034,   2.75090647, 3.242971   },。。。。。。}

在ISP中调用上面介绍的函数以实现LSC操作。

1.3 LSC效果

让我们看看矫正后的结果吧。

可以看到四周的亮度被提高了,看起来整幅图片的亮度的分布比较均匀了。

让我们看看与商用isp的对比。

现在看来我们急需要解决饱和度的问题了。那还等什么呢?

2. Color Correction (CC)

要解决饱和度低的问题,就需要对色彩进行矫正,也就是这一章我们要介绍的color correction。
对颜色的矫正需求来源于sensor输出的数据和人眼对实物的感受之间存在的色彩差异。而Color correction其本质就是对数据进行人眼真实感受值映射的一种过程。

但无论我们采用什么样的映射方法,首先要确定的是描述颜色的方法。此前我们说过该 ISP 的 CC 操作位于 RGB域,也就是说当前步骤采用RGB色彩空间来描述颜色。

所以我们的CC就是在RGB域进行的对sensor输出数据向人眼真实感受值的一种映射。

2.1 CC原理

为了实现这种映射过程,我们需要获取到数据和感官真实值之间的具体的映射方式。确定映射方式的一种方法是标定法。即利用标定好的某种颜色的标准值和sensor拍摄该值实物图片所输出值做差,来求得CC对该颜色需要做的映射。

我们常见的color checker 色卡就是标定好的颜色,我们知道色卡上每个色块在RGB域的真实感受值,所以我们可以在某种色温环境下对该色卡进行拍摄,输出一份未作CC的数据,用这个数据和标定值做差来确定为该色温环境下这个sensor需要做的CC映射。

此后在该色温环境下使用该sensor时,对其输出作CC,就可以还原出人眼真实感受值了。

由于在RGB域有三个通道,我们的这种映射关系可以由矩阵来表示,这就是我们常说的Color Correction Metric(CCM)。

2.2 CC软件实现

cc的软件实现采用了CCM进行矩阵计算,参考了imtest官网对CCM的介绍。以下贴出代码。

BZResult BZ_ColorCorrection(void* data, LIB_PARAMS* pParams, ISP_CALLBACKS CBs, ...)
{BZResult result = BZ_SUCCESS;(void)CBs;if (!data) {result = BZ_INVALID_PARAM;BZLoge("data is null! result:%d", result);}if (SUCCESS(result)) {int32_t width, height;float* pCcm;int32_t i, j;width = pParams->info.width;height = pParams->info.height;pCcm = pParams->CC_param.CCM;BZLogd("width:%d height:%d", width, height);for (i = 0; i < CCM_HEIGHT; i++) {BZLogd("CCM[%d]:%f CCM[%d]:%f CCM[%d]:%f", i * CCM_WIDTH, pCcm[i * CCM_WIDTH],i * CCM_WIDTH + 1, pCcm[i * CCM_WIDTH + 1],i * CCM_WIDTH + 2, pCcm[i * CCM_WIDTH + 2]);}Mat CCM = Mat::zeros(3, 3, CV_32FC1);for (i = 0; i < 3; i++) {for (j = 0; j < 3; j++) {CCM.at<float>(i, j) = *(pCcm + i * CCM_WIDTH + j);}}uint16_t* B = static_cast<uint16_t*>(data);uint16_t* G = B + width * height;uint16_t* R = G + width * height;Mat outMatric;Mat inMatric(width * height, 3, CV_32FC1);for (i = 0; i < height * width; i++) {inMatric.at<float>(i, 0) = (float)B[i];inMatric.at<float>(i, 1) = (float)G[i];inMatric.at<float>(i, 2) = (float)R[i];}outMatric = inMatric * CCM;for (i = 0; i < height * width; i++) {if (outMatric.at<float>(i, 0) > 1023)outMatric.at<float>(i, 0) = 1023;if (outMatric.at<float>(i, 0) < 0)outMatric.at<float>(i, 0) = 0;if (outMatric.at<float>(i, 1) > 1023)outMatric.at<float>(i, 1) = 1023;if (outMatric.at<float>(i, 1) < 0)outMatric.at<float>(i, 1) = 0;if (outMatric.at<float>(i, 2) > 1023)outMatric.at<float>(i, 2) = 1023;if (outMatric.at<float>(i, 2) < 0)outMatric.at<float>(i, 2) = 0;B[i] = (uint16_t)outMatric.at<float>(i, 0);G[i] = (uint16_t)outMatric.at<float>(i, 1);R[i] = (uint16_t)outMatric.at<float>(i, 2);}}return result;
}

这里采用了Opencv的 Mat 类快速地实现矩阵相乘,由于结果可能超出此时10bit 的编码上限,因此需要对超出的数据进行修正,修正为10bit的上限值1023。

这里的CCM为:

CC_PARAM CCPARAM_1920x1080_D65_1000Lux{{{ 1.819, -0.248,     0.213  },{ -1.069,  1.322,      -1.078 },{ 0.250,   -0.074,     1.865  }}
};

由CCM第一行可以看出,我们对B通道的映射是将其蓝色分量进行强调,红色分量稍微提高,绿色分量稍微减弱,总体上实现了B通道数据在RGB域向B轴正方向的偏移,以此提高了B通道的饱和度。第二行和第三行都实现了类似的偏移,饱和度都得到了提高。需注意,该CCM只适用于在D65光源下我测试使用的这个sensor。

2.3 CC效果

现在让我们看看CC处理前后的效果对比吧。

很明显经过CC,图片的色彩得到了改善。

让我们看一下这一章介绍的LSC以及CC的总体效果吧。

如此看来,是不是已经很接近商用ISP的效果了呢?让我们放大看看细节吧。

可以看出目前我们的ISP已经对raw作出了很大的改善,但我们的输出有很多红绿的色斑,并且对比度不够高,锐度不够强呀。哈哈哈哈,所以下一章,也就是此系列的终章,我会针对这个两个问题做一些改善。

敬请期待!

Image Signal Processing(ISP)-第四章-LSC, CC的原理和软件实现相关推荐

  1. Image Signal Processing(ISP)-第三章-BCL, WB, Gamma的原理和软件实现

    Hello!ISP系列文章终于更新了,距离上一篇文章发布已经过去半年多啦!哈哈,虽然这段时间没有写文,但是这个简单ISP的代码还是有更新的哦,有兴趣的朋友可以到Github查看.话不多说,我们接着讲I ...

  2. Image Signal Processing(ISP)-第二章-Demosaic去马赛克以及BMP软件实现

    Hello!ISP的基础知识分享第二章终于来了!最近精力都投入到了工作上,真是没时间写东西.但一位大佬私信我催更,着实让我感动.即使我的文字只有一个人看,那我也会写下去,而且泡做事怎么会半途而费呢? ...

  3. 鸟哥的Linux私房菜(服务器)- 第四章、连上 Internet

    第四章.连上 Internet 最近更新日期:2011/07/20 终于要来到修改 Linux 网络参数的章节了!在第二章的网络基础中, 我们知道主机要连上 Internet 需要一些正确的网络参数设 ...

  4. Color ScienceImage Signal Processing

    Color Science & Image Signal Processing  人眼/相机成像原理  颜色定量分析(xyz,lab,RGB,YUV)  ISP颜色重建相关 ###成像原理 成 ...

  5. [2021 CVPR] 可逆网络实现 RAW 和 RGB 图像转换:Invertible Image Signal Processing

    Invertible Image Signal Processing [pdf] 目录 Abstract Introduction Traditional ISP Analysis Method Ab ...

  6. 计算机网络第四章ppt谢希仁,计算机网络课件-谢希仁(第四章).ppt

    计算机网络课件-谢希仁(第四章) 路由器之间交换信息 RIP协议让互联网中的所有路由器都和自己的相邻路由器不断交换路由信息,并不断更新其路由表,使得从每一个路由器到每一个目的网络的路由都是最短的(即跳 ...

  7. 系统架构师学习笔记_第十四章_连载

    第十四章  基于ODP的架构师实践 14.1  基于ODP的架构开发过程 系统架构 反映了功能在系统系统构件中的 分布.基础设施相关技术.架构设计模式 等,它包含了架构的 原则 和 方法.构件关系 与 ...

  8. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

    python全栈开发笔记第二模块 第四章 :常用模块(第二部分)     一.os 模块的 详解 1.os.getcwd()    :得到当前工作目录,即当前python解释器所在目录路径 impor ...

  9. IEEE Signal Processing Letters

    原文链接:https://signalprocessingsociety.org/publications-resources/ieee-signal-processing-letters/infor ...

最新文章

  1. Cocos Creator 的 动作(Action)系统:moveBy的使用
  2. Oracle12c多租户如何启动关闭CDB或PDB (PDB自动启动)
  3. Virtualbox虚机无法启动因断电
  4. .NET Core + Kubernetes:Pod
  5. source insight 解决自动缩进 和 TAB键=4个SPACE
  6. 用postman做接口测试
  7. leetcode621 贪心:任务安排
  8. java applet运行jmx,通过tomcat设置jvm及添加jmx远程访问、gc输出日志
  9. corosynclib+drbd+mysql组合应用
  10. 【MacOS】MacOS 添加虚拟打印机
  11. 在官网下载linux版本的tomcat
  12. lame编程实现wav转mp3后时长错误的问题
  13. Redis的前前后后左左右右
  14. 想知道表格文字识别怎么弄?分享2个表格识别的方法
  15. pythonstdin_理解Python中的stdin stdout stderr - The Hard Way Is Easier
  16. vs2019编译错误:LINK : fatal error LNK1104: 无法打开文件“atls.lib”
  17. STM32F4xx系列使用HAL库配置SPI-读写FLASH
  18. 再谈BPM(业务流程管理)为企业带来哪些提升
  19. QQ表情里的股市晴雨表
  20. 柱状图python_鬼灭之刃:鬼杀队九柱实力排行,霞柱是强弱分界线

热门文章

  1. 微信小程序使用canvas绘图,并保存下载到本地。圆形头像,虚线网络图片
  2. Luogu P4516 [JSOI2018] 潜入行动
  3. webrtc 前期准备工作
  4. 2023年人工智能GPT-4时代,最新13个ChatGPT商业市场AIGC应用正在掀起革命性变革!
  5. java数组删除元素_java删除数组中的某一个元素的方法
  6. 易基因:小檗碱通过介导m6A mRNA甲基化调控斑马鱼肝细胞氧化应激、凋亡和自噬|科研进展
  7. 关于s19赛季服务器维修,S19赛季更新,你必须知道的五点改动,了解后上分不再是难事!...
  8. 翌加科技:教抖音小店商家采取措施避免售后争议
  9. 数据采集方式有哪些,都有什么特点?
  10. ELK 收集 Java 后台日志