【转】 ISP-镜头阴影校正(LSC)
转自:https://blog.csdn.net/xiaoyouck/article/details/77206505
介绍
镜头阴影校正(Lens Shading Correction)是为了解决由于lens的光学特性,由于镜头对于光学折射不均匀导致的镜头周围出现阴影的情况。
shading可以细分为luma shading和color shading:
luma shading:
由于Lens的光学特性,Sensor影像区的边缘区域接收的光强比中心小,所造成的中心和四角亮度不一致的现象。镜头本身就是一个凸透镜,由于凸透镜原理,中心的感光必然比周边多。如图所示:
chrom/color shading:
由于各种颜色的波长不同,经过了透镜的折射,折射的角度也不一样,因此会造成color shading的现象,这也是为什么太阳光经过三棱镜可以呈现彩虹的效果。如图所示:
此外,还有CRA的原因会导致shading现象的出现,这里不再赘述,这里推荐《What’s CRA》这篇文章,详细讲述了由于镜头的CRA带来的shading。
影响
luma shading:会造成图像边角偏暗,就是所谓的暗角。
color shading:中心和四周颜色不一致,体现出来一般为中心或者四周偏色。如图所示:
校正
lens shading的校正是分别对于bayer的四个通道进行校正,每个通道的校正过程是相对独立的过程。
考虑到芯片设计的成本,因此一般情况下不会存储整幅图像的lut,目前主流的都是存储128*128个点的增益,利用双线性插值的方法计算每个pixel的增益。
算法
由于条件限制,图像仅用于算法验证,不做图像质量评判标准
这里写了一个shading的算法,将图像分为16x16的方块,求取每个交点的增益值,对平面进行四次方拟合,分别计算了luma shading 和 chrom shading,先计算出来一个lut用于存储,校正的世行通过对这个lut进行双线性插值得到每个pixel的值乘以原本像素点。
16x16的分块并非固定,可以对块的大小进行调整,比如中心块偏大,靠近边缘的方块变小,这些都是可以自定义的,本算法由于做演示使用,故不做其他功能。如图所示:
code
由于代码量较大,这里分别附上一部分算法
shading lut caculate:
function [image_r_gain, image_gr_gain, image_gb_gain, image_b_gain] = ...
isp_lsc_lut(image_r, image_gr, image_gb, image_b, side_num)
[height, width] = size(image_r);
side_y = floor(height/side_num);
side_x = floor(width/side_num); % figure,imshow(image_r);
% hold on;
% for k=0:side_num
% line_x = side_x * k;
% line_y = side_y * k;
% if(k==side_num && line_y ~= width) line_y = height;end
% if(k==side_num && line_x ~= width) line_x = width;end
% line([line_x,line_x],[0,height],'Color','red');
% line([0,width], [line_y, line_y],'Color','red');
% % line(Xd,Yd,'Color','red');
% end
% hold off %% compress resolution
image_point = zeros(side_num,side_num);
for i = 0:side_num
for j = 0:side_num
x_clip = floor([j*side_x - side_x/2, j*side_x + side_x/2]);
y_clip = floor([i*side_y - side_y/2, i*side_y + side_y/2]);
if(i==side_num && y_clip(2) ~= height) y_clip(2) = height;end
if(j==side_num && x_clip(2) ~= width) x_clip(2) = width;end
x_clip(x_clip<1) = 1;x_clip(x_clip>width) = width;
y_clip(y_clip<1) = 1;y_clip(y_clip>height) = height;
data_r_in = image_r(y_clip(1):y_clip(2), x_clip(1):x_clip(2));
image_r_point(i+1,j+1) = mean(mean(data_r_in));
data_gr_in = image_gr(y_clip(1):y_clip(2), x_clip(1):x_clip(2));
image_gr_point(i+1,j+1) = mean(mean(data_gr_in));
data_gb_in = image_gb(y_clip(1):y_clip(2), x_clip(1):x_clip(2));
image_gb_point(i+1,j+1) = mean(mean(data_gb_in));
data_b_in = image_b(y_clip(1):y_clip(2), x_clip(1):x_clip(2));
image_b_point(i+1,j+1) = mean(mean(data_b_in));
end
end % figure,imshow(uint8(image_r_point));
%% caculate lsc luma gain
for i = 1:side_num+1
for j = 1:side_num+1
image_r_luma_gain_point(i,j) = mean2(image_r_point(uint8(side_num/2)-1:uint8(side_num/2)+1, uint8(side_num/2)-1:uint8(side_num/2)+1)) / image_r_point(i,j);
image_gr_luma_gain_point(i,j) = mean2(image_gr_point(uint8(side_num/2)-1:uint8(side_num/2)+1, uint8(side_num/2)-1:uint8(side_num/2)+1)) / image_gr_point(i,j);
image_gb_luma_gain_point(i,j) = mean2(image_gb_point(uint8(side_num/2)-1:uint8(side_num/2)+1, uint8(side_num/2)-1:uint8(side_num/2)+1)) / image_gb_point(i,j);
image_b_luma_gain_point(i,j) = mean2(image_b_point(uint8(side_num/2)-1:uint8(side_num/2)+1, uint8(side_num/2)-1:uint8(side_num/2)+1)) / image_b_point(i,j);
end
end
bilinear interpolation:
image_r_luma_gain_reshape = reshape(image_r_luma_gain_point, [], 1);
image_gr_luma_gain_reshape = reshape(image_gr_luma_gain_point, [], 1);
image_gb_luma_gain_reshape = reshape(image_gb_luma_gain_point, [], 1);
image_b_luma_gain_reshape = reshape(image_b_luma_gain_point, [], 1);
for i = 1:17
for j = 1:17
x((i-1)*17+j) = i;
y((i-1)*17+j) = j;
end
end
x=x';
y=y';
% scatter3(x,y,image_r_luma_gain_reshape)
% hold on
Z=[ones(length(x),1),x,y,x.^2,x.*y,y.^2,x.^3,x.^2.*y,x.*y.^2,y.^3];
[x y]=meshgrid(1:17,1:17);
A=Z\image_r_luma_gain_reshape;
image_r_luma_gain=A(1)+A(2)*x+A(3)*y+A(4)*x.^2+A(5)*x.*y+A(6)*y.^2+A(7)*x.^3+A(8)*x.^2.*y+A(9)*x.*y.^2+A(10)*y.^3;
A=Z\image_gr_luma_gain_reshape;
image_gr_luma_gain=A(1)+A(2)*x+A(3)*y+A(4)*x.^2+A(5)*x.*y+A(6)*y.^2+A(7)*x.^3+A(8)*x.^2.*y+A(9)*x.*y.^2+A(10)*y.^3;
A=Z\image_gb_luma_gain_reshape;
image_gb_luma_gain=A(1)+A(2)*x+A(3)*y+A(4)*x.^2+A(5)*x.*y+A(6)*y.^2+A(7)*x.^3+A(8)*x.^2.*y+A(9)*x.*y.^2+A(10)*y.^3;
A=Z\image_b_luma_gain_reshape;
image_b_luma_gain=A(1)+A(2)*x+A(3)*y+A(4)*x.^2+A(5)*x.*y+A(6)*y.^2+A(7)*x.^3+A(8)*x.^2.*y+A(9)*x.*y.^2+A(10)*y.^3;
% surf(x,y,image_r_luma_gain)
% hold on
% surf(x,y,image_r_luma_gain_point) %% calulate lsc chroma gain
for i = 1:side_num+1
for j = 1:side_num+1
image_r_chroma_gain(i,j) = image_r_luma_gain(i,j) - image_r_luma_gain_point(i,j);
image_gr_chroma_gain(i,j) = image_gr_luma_gain(i,j) - image_gr_luma_gain_point(i,j);
image_gb_chroma_gain(i,j) = image_gb_luma_gain(i,j) - image_gb_luma_gain_point(i,j);
image_b_chroma_gain(i,j) = image_b_luma_gain(i,j) - image_b_luma_gain_point(i,j);
end
end
%% caculate lsc result gain
image_r_gain = image_r_luma_gain - image_r_chroma_gain;
image_gr_gain = image_gr_luma_gain - image_gr_chroma_gain;
image_gb_gain = image_gb_luma_gain - image_gb_chroma_gain;
image_b_gain = image_b_luma_gain - image_b_chroma_gain; function image_gain_lut = lsc_data_gain_interpolation(image_gain, height, width, side_num)
side_y_ori = floor(height/side_num);
side_x_ori = floor(width/side_num);
k = 0;
l = 0;
[gain_height, gain_width] = size(image_gain);
for i = 1:gain_height-1
for j = 1:gain_width-1
data_gain_11 = image_gain(i, j);
data_gain_12 = image_gain(i, j+1);
data_gain_21 = image_gain(i+1, j);
data_gain_22 = image_gain(i+1, j+1);
if(j == gain_width-1 && ((j-1)*side_x + l) ~= width)
side_x = width - (j-1)*side_x_ori;
else
side_x = side_x_ori;
end if(i == gain_width-1 && ((i-1)*side_y + k) ~= width)
side_y = height - (i-1)*side_y_ori;
else
side_y = side_y_ori;
end for k = 1:side_y
for l = 1:side_x
label_y1 = 1;
label_x1 = 1;
label_y2 = side_y;
label_x2 = side_x;
image_gain_lut((i-1)*side_y_ori + k, (j-1)*side_x_ori + l) = ...
data_gain_22/(label_x2-label_x1)/(label_y2-label_y1)* ...
(l - label_x1) * (k - label_y1) + ...
data_gain_21/(label_x2-label_x1)/(label_y2-label_y1)* ...
(label_x2 - l) * (k - label_y1) + ...
data_gain_12/(label_x2-label_x1)/(label_y2-label_y1)* ...
(l - label_x1) * (label_y2 - k) + ...
data_gain_11/(label_x2-label_x1)/(label_y2-label_y1)* ...
(label_x2 - l) * (label_y2 - k);
end
end
end
end
end
效果展示:
实验条件有限,图片有水波纹,仅用于理解算法
original image:
luma shading
chroma shading:
luma shading + chroma shading:
tuning
LSC的tuning一定要把校正图采集好,一般情况下raw图的G通道中心亮度在8bit的70%~80%之间,由于在不同色温情况下是经过插值的,因此需要校正多个光源,一般情况下TL84、D65、A光源下进行校正。将得到的LUT写入RAM中即可
注意:采集的raw图不要有filcker。
LSC强度一般是可调的,由于图像边角的增益会很大,因此在高倍gain下,可以把强度给降低,防止图像边角噪声压不住的情况。
由于各个平台不同,这里不做详细介绍,想到再补充。
【转】 ISP-镜头阴影校正(LSC)的更多相关文章
- ISP基本框架及算法介绍
什么是ISP,他的工作原理是怎样的? ISP是Image Signal Processor的缩写,全称是影像处理器.在相机成像的整个环节中,它负责接收感光元件(Sensor)的原始信号数据,可以理解为 ...
- 摄像头 ISP 调试的入门之谈(经验总结)
在讲述本文之前,我尽量以一个什么也不清楚的初学到入门的用词来阐述什么是 ISP 调试,以及为什么需要调试. 如果你从来都没有接触过什么是摄像头 ISP 调试,我想这个文章可以给你一些启发和关键词. 因 ...
- 摄像头ISP系统原理(中)
摄像头ISP系统原理(中) AF(FOCUS)----自动对焦 根据光学知识,景物在传感器上成像最清晰时处于合焦平面上.通过更改 LENS 的位置,使得景物在传感器上清晰的成像,是 ISP FOCUS ...
- 【转】 ISP概述、工作原理及架构
1.概述 ISP全称Image Signal Processing,即图像信号处理.主要用来对前端图像传感器输出信号处理的单元,以匹配不同厂商的图象传感器. ISP 通过一系列数字图像处理算法完成对数 ...
- Hi3516EV300专业型HD IP Camera SoC
Hi3516EV300芯片特点: 处理器内核 ARM Cortex A7@ 900MHz,32KB I-Cache,32KB D-Cache /128KB L2 cache 支持 Neon 加速,集成 ...
- 全志T8智能汽车方案芯片参数介绍
T8处理器代表了Allwinner在智能汽车市场上的最新成就.T8适用于需要三维图形.高级视频处理.精密相机.多种连接选项和高水平系统集成的应用程序.它将把先进的消费电子体验带入未来的汽车,实现高性能 ...
- 基于Hi3559AV100 RFCN实现细节解析-(3)系统输入VI分析(HiISP)二 :
下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,整个过程涉及到VI.VDEC.VPSS.VGS.VO.NNIE,其中涉及的内容,大家可以参考之前我写的博客: 基于Hi3559AV10 ...
- Android Camera2 参数调节关键字翻译集合,常用关键字解析
https://blog.csdn.net/qq_29333911/article/details/79400617 black_level_lock黑电平补偿是否锁定当前值,或者可以自由更改.col ...
- 摄像头驱动OV7725学习笔记连载(一):OV7725 电器特性和时序图
OV(豪威科技)已经被中国财团收购.这个昔日的大佬,最终走下神坛. 关于OVsensor的资料包括,OV7725的简介(OmniVsion_OV7725),OV7725的数据手册(OV7725_Dat ...
随机推荐
- python基础,if语句,while循环
if语句: ①2选一 ④多选一 if: 条件 ...
- Consul部署架构
Consul 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接,用于实现分布式系统的服务发现与配置. 应用Consul提供的服务需要建立Consul集群.在Consul方案中,每个 ...
- 理解linux 密码存储
1. 传统上,linux把加密(哈希)的密码保存在/etc/passwd文件中,passwd文件的格式如下: smithj:x:561:561:Joe Smith:/home/smithj:/bin/ ...
- python基础(4)-元组&字典&字符串&队列
元组(tuple) #元组相对列表来说不同之处是只读不可写 读操作和列表一致 letter_tuple = ('a','b','c','d'); print(letter_tuple[0]);#res ...
- linux delete file
今天不小心生成了这么个文件名的文件-ep-ser 然后 rm -ep-ser就删除不了,它认为-e是option 后来,用rm ./-ep-ser就顺利删除了,哈哈,教训啊
- python队列基本使用
Python queue队列 作用: 解耦:使程序直接实现松耦合,修改一个函数,不会有串联关系. 提高处理效率:FIFO = 现进先出,LIFO = 后入先出. 队列: 队列可以并发的派多个线程, ...
- Oracle sqlplus的输出表的排版,数据表发生折行问题
当查寻数据表的时候,会发生折行的问题 这时,我们可以用下面的语句 设置每行显示的记录长度:set linesize 300; ---> 每行显示300个字符. 设置每页显示的 ...
- 利用tcp三次握手,使用awl伪装MAC地址进行多线程SYN洪水攻击
SYN洪水攻击概述:SYN洪水攻击主要源于: tcp协议的三次握手机制tcp协议面向链接的协议SYN洪水攻击的过程:在服务端返回一个确认的SYN-ACK包的时候有个潜在的弊端,如果发起的客户是一个不存 ...
- ASP.NET MVC案例教程(一) 准备
ASP.NET MVC案例教程(一) 前言 ASP.NET MVC作为微软官方的MVC解决方案,推出有一段时间了.可以说自动推出以来,一直广受关注.在经历了漫长的Preview之后,前几天终于推出了其 ...
- 发布WebApi项目时包含XML文档文件
Open your publishprofile (*.pubxml) and include this code into "Project" element: <Item ...