您當前的位置:首頁 > 攝影

晶片設計-lut表與線性插值

作者:由 0errors0warnings 發表于 攝影時間:2022-06-25

修訂記錄

版本

日期

說明

v0。1

2022。06。25

初版

為什麼要用lut?

lut表在影象處理領域是一個非常常見的操作。假設我們想實現下面一條對映曲線f(x),該如何實現呢?

它不嚴格符合某一多元或多次方程,不能用一個計算公式實現。

我們也不可能將每個點都儲存在mem當中,假設你的資料位寬是16bit,那就需要(2^16)*16bit=1024Kb大小的mem,土豪都頂不住。

處理辦法是選取若干取樣點,只儲存這些取樣點的x/y座標,其餘點則透過兩側取樣點線性插值得到。誠然,這種做法得到的曲線不能完美適配預設曲線,但在資源與效果上做到了較好的平衡。

晶片設計-lut表與線性插值

lut內容與插值方法

使用lut時,我們需要考慮如下問題:

lut表中放什麼?或者說軟體需要配置哪些東西到硬體?

如何進行lut表定址

如何插值?

線性插值公式:

dout = y0 + (y1-y0)*(din-x0)/(x1-x0)

其中x0,x1,y0,y1表示區間左右兩側的x,y座標。從這個公式上看,lut表需要包括x,y座標,插值時還需要一個除法器,但根據實際情況,其實可以有所最佳化。

均勻取樣,且取樣區間數為2的冪

這種情況對硬體來說是最友好的。假設資料位寬為16bit,取樣區間數是1024,則:

資料高位din[15:6]就是x0, +1就是x1。lut表中就不需要存x軸座標了

資料地位din[5:0]就是(din-x0)

同時,區間步長x_step(即x1-x0)也是2的冪:2^16/1024 = 2^6,因此除法可以用移位實現。

插值公式簡化為:

dout = y0 + (y1-y0)*din[5:0] >> 6

另外,如果x_step不是2的冪,那也不需要除法器,可以設定一個倒數暫存器

x_inv = 1/(x1-x0) = 1/x_step

則:dout = y0 + (y1-y0)*(din-x0)*x_inv

再進一步,倒數暫存器設定為:

inv = (y1-y0)/(x1-x0) = y_step/x_step

則:dout = y0 + (din-x0)*inv

均勻取樣,且取樣區間數不是2的冪

假設資料位寬是16bit, 取樣區間數是1000, 判斷din落在哪個區間時:RangeIdx = din/1000

這種做法貌似節約了mem深度,但需要額外消耗除法器。所以我們一般不這樣幹,寧願把取樣區間數向上增加到2的冪。

分段均勻取樣

均勻取樣收到的限制較多,因此有了分段均勻的操作。

把x軸分成若干段落(Segment),數量

不要求

為2的冪,但不要太多。

把每個段落分成若干區間(range),數量

要求

為2的冪。比如我們可以給暗部區域分配多些區間,亮部區域少些區間。

段落內區間步長相等,段落間區間步長不要求相等,區間步長要求為2的冪

晶片設計-lut表與線性插值

當輸出輸入後:

首先得判斷落在哪個段落中,由於段落是非均勻的,因此這裡只能靠if_else來實現,這也就是段落數不能太多的原因。

if(din < SegNode0) //SegNode0表示段落節點

SegIdx <= 0; //SegIdx表示段落索引

else if(din < SegNode1)

SedIdx <= 1;

……

判斷好區間後,由於段落內是均勻取樣,因此按均勻取樣的做法進行即可。

mem儲存形式

根據上面描述,我們需要從lut表中獲取到y0,y1的值。如果資料是連續進來的,意味著我們得一拍內獲取到y0,y1兩個點,因此lut在mem中如何儲存很關鍵。推薦下面做法:

設定2個ram,寬度等於lut表寬度,深度等於lut深度的1/2。lut資料在2個ram中交替儲存。這種做法軟體寫入簡單,邏輯取值也方便,同時ram資源消耗也較少。

RAM0

RAM1

lut_data0

lut_data1

lut_data2

lut_data3

……

……

lut_data1022

lut_data1023

參考文件

ISP——Gamma Correction - 知乎 (zhihu。com)

標簽: LUT  取樣  y0  DIN  x0