影象處理演算法——卷積
本文索引:
一、 什麼是卷積?
在影象處理中,卷積操作指的是使用一個卷積核對影象中的每個畫素進行一系列操作。
卷積核(運算元)是用來做影象處理時的矩陣,影象處理時也稱為掩膜,是與原影象做運算的引數。卷積核通常是一個四方形的網格結構(例如3*3的矩陣或畫素區域),該區域上每個方格都有一個權重值。
使用卷積進行計算時,需要將卷積核的中心放置在要計算的畫素上,一次計算核中每個元素和其覆蓋的影象畫素值的乘積並求和,得到的結構就是該位置的新畫素值。
以下兩個運算元中演示了具體的卷積計算過程。
二、 相關運算元
定義:
即,其中h稱為相關核(Kernel).
步驟:
- 1)滑動核,使其中心位於輸入影象g的(i,j)畫素上
- 2)利用上式求和,得到輸出影象的(i,j)畫素值
- 3)充分上面操縱,直到求出輸出影象的所有畫素值
【例】
原始畫素矩陣為:
卷積模板h為:
計算輸出影象的(2,4)元素=1*8+8*1+15*6+7*3+14*5+16*7+13*4+20*9+22*2=585
如圖所示:
三、 卷積運算元
定義:
即
步驟:
- 1)將核圍繞中心旋轉180度
- 2)滑動核,使其中心位於輸入影象g的(i,j)畫素上
- 3)利用上式求和,得到輸出影象的(i,j)畫素值
- 4)充分上面操縱,直到求出輸出影象的所有畫素值
例:計算輸出影象的(2,4)元素=1*2+8*9+15*4+7*7+14*5+16*3+13*6+20*1+22*8=575
如圖所示:
四、 邊緣效應
當對影象邊緣的進行濾波時,核的一部分會位於影象邊緣外面。
常用的策略包括:
- 1)使用常數填充:imfilter預設用0填充,這會造成處理後的影象邊緣是黑色的。
- 2)複製邊緣畫素:I3 = imfilter(I,h,’replicate’);
五、 常用的卷積核及其用途
- 1)低通濾波器(常用於計算模糊後的效果)
⎡⎣⎢1/91/91/91/91/91/91/91/91/9⎤⎦⎥
⎡⎣⎢1/101/101/101/102/101/101/101/101/10⎤⎦⎥
⎡⎣⎢1/162/161/162/164/162/161/162/161/16⎤⎦⎥ 2)高斯濾波器(常用於計算高斯模糊後的效果)
高斯模糊的卷積核也是一個正方形的濾波核,其中每個元素通過以下公式計算得出:
G(x,y)=12πσ2⋅ex2+y22σ2
該公式中σ是標準方差(一般取值為1),x和y分別對應了當前位置到卷積核中心的整數距離。通過這個公式,就可以計算出高斯核中每個位置對應的值。為了保證濾波後的影象不會變暗,需要對高斯核中的權重進行歸一化。3)邊緣檢測(常用於計算影象邊緣或者說梯度值)
⎡⎣⎢−10−1040−10−1⎤⎦⎥
六、 一個例子——使用卷積實現模糊效果
我們將對下面這張圖進行模糊處理:
以下為compute shader中關於卷積處理的程式碼:
[numthreads(32,32,1)]
void Dim_Main (uint3 id : SV_DispatchThreadID)
{
float sumR = 0;
float sumG = 0;
float sumB = 0;
float sumA = 0;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
sumR += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].r * convolutionTempBuffer[(i+1)*3+(j+1)];
sumG += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].g * convolutionTempBuffer[(i+1)*3+(j+1)];
sumB += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].b * convolutionTempBuffer[(i+1)*3+(j+1)];
sumA += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].a * convolutionTempBuffer[(i+1)*3+(j+1)];
}
}
texBuffer[id.x*texWidth[0]+id.y].r = sumR;
texBuffer[id.x*texWidth[0]+id.y].g = sumG;
texBuffer[id.x*texWidth[0]+id.y].b = sumB;
texBuffer[id.x*texWidth[0]+id.y].a = sumA;
Result[id.xy] = float4(sumR, sumG, sumB, sumA);
}
效果如圖所示:
圖中可以明顯的看到左右兩邊有明顯的黑色線條,原圖中是沒有這樣的黑色的,產生這種效果的原因是本文中之前提到過的邊緣效應。下面我將修改一部分程式碼去除邊緣效應帶來的影響,這裡使用的是相鄰畫素的值方法。
程式碼如下:
[numthreads(32,32,1)]
void Dim_Main (uint3 id : SV_DispatchThreadID)
{
float sumR = 0;
float sumG = 0;
float sumB = 0;
float sumA = 0;
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if((id.x+i)*texWidth[0]+(id.y+j)>texWidth[0]*texWidth[0]-1)
{
sumR += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)-texWidth[0]].r * convolutionTempBuffer[(i+1)*3+(j+1)];
sumG += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)-texWidth[0]].g * convolutionTempBuffer[(i+1)*3+(j+1)];
sumB += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)-texWidth[0]].b * convolutionTempBuffer[(i+1)*3+(j+1)];
sumA += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)-texWidth[0]].a * convolutionTempBuffer[(i+1)*3+(j+1)];
}
sumR += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].r * convolutionTempBuffer[(i+1)*3+(j+1)];
sumG += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].g * convolutionTempBuffer[(i+1)*3+(j+1)];
sumB += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].b * convolutionTempBuffer[(i+1)*3+(j+1)];
sumA += texBuffer[(id.x+i)*texWidth[0]+(id.y+j)].a * convolut