ISP-鏡頭陰影校正(LSC)

概述

介紹

鏡頭陰影校正(Lens Shading Correction)是為了解決由于lens的光學特性婚惫,由于鏡頭對于光學折射不均勻導致的鏡頭周圍出現陰影的情況驹溃。

shading可以細分為luma shading和color shading:

  • luma shading:
    由于Lens的光學特性只壳,Sensor影像區(qū)的邊緣區(qū)域接收的光強比中心小,所造成的中心和四角亮度不一致的現象。鏡頭本身就是一個凸透鏡捅厂,由于凸透鏡原理避乏,中心的感光必然比周邊多爷耀。如圖所示:


    這里寫圖片描述
  • chrom/color shading:
    由于各種顏色的波長不同,經過了透鏡的折射拍皮,折射的角度也不一樣歹叮,因此會造成color shading的現象跑杭,這也是為什么太陽光經過三棱鏡可以呈現彩虹的效果。如圖所示:


    這里寫圖片描述

此外艘蹋,還有CRA的原因會導致shading現象的出現,這里不再贅述票灰,這里推薦《What's CRA》這篇文章女阀,詳細講述了由于鏡頭的CRA帶來的shading。

鏡頭CRA大于Sensor的CRA時容易出現Color shading屑迂;
鏡頭CRA小于Sensor的CRA時容易出現Len Shading浸策;
建議優(yōu)先保證Color Shading,因為Lens Shading比Color 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下拾徙,可以把強度給降低洲炊,防止圖像邊角噪聲壓不住的情況。

由于各個平臺不同尼啡,這里不做詳細介紹暂衡,想到再補充。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末崖瞭,一起剝皮案震驚了整個濱河市狂巢,隨后出現的幾起案子,更是在濱河造成了極大的恐慌读恃,老刑警劉巖隧膘,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件代态,死亡現場離奇詭異寺惫,居然都是意外死亡,警方通過查閱死者的電腦和手機蹦疑,發(fā)現死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門西雀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人歉摧,你說我怎么就攤上這事艇肴。” “怎么了叁温?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵再悼,是天一觀的道長。 經常有香客問我膝但,道長冲九,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任跟束,我火速辦了婚禮莺奸,結果婚禮上丑孩,老公的妹妹穿的比我還像新娘。我一直安慰自己灭贷,他們只是感情好温学,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著甚疟,像睡著了一般仗岖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上览妖,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天箩帚,我揣著相機與錄音,去河邊找鬼黄痪。 笑死紧帕,一個胖子當著我的面吹牛,可吹牛的內容都是我干的桅打。 我是一名探鬼主播是嗜,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挺尾!你這毒婦竟也來了鹅搪?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤遭铺,失蹤者是張志新(化名)和其女友劉穎丽柿,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體魂挂,經...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡甫题,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了涂召。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坠非。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖果正,靈堂內的尸體忽然破棺而出炎码,到底是詐尸還是另有隱情,我是刑警寧澤秋泳,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布潦闲,位于F島的核電站,受9級特大地震影響迫皱,放射性物質發(fā)生泄漏歉闰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一新娜、第九天 我趴在偏房一處隱蔽的房頂上張望概龄。 院中可真熱鬧蚕键,春花似錦、人聲如沸誊爹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽某弦。三九已至,卻和暖如春馆截,著一層夾襖步出監(jiān)牢的瞬間混卵,已是汗流浹背幕随。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工睦霎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人碑幅。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓异吻,卻偏偏與公主長得像篮昧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子酵颁,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容