跳轉到內容

理解多點觸控/卷積和反捲積

50% developed
來自華夏公益教科書,開放世界開放書籍

卷積概述

[編輯 | 編輯原始碼]

數學上,卷積被描述為一個運算子,它接收兩個函式 f 和 g,並生成第三個函式,表示 f 和 g 之間的重疊量。卷積在訊號分析中被廣泛使用,因為卷積運算通常非常便宜,可以並行執行,並且易於調整以適應情況。

在我們的案例中,我們將討論二維意義上的卷積,其中影像和已知卷積函式(稱為 **濾波器**)將被組合以生成一個更好地表示我們正在尋找的資料的影像。由於您應該已經熟悉這些概念,因此這將是對卷積工作原理的簡要概述,以及我們感興趣的特定卷積濾波器。

如果您回憶一下,我們的理論感測器模型將資料作為 8x8 的 8 位資料矩陣輸入系統,類似於以下內容

            Horizontal Address 
          0  1  2  3  4  5  6  7   
 V A   0 00 00 00 00 00 00 00 00   
 e d   1 00 00 00 00 00 00 00 00   
 r d   2 00 00 00 00 00 00 00 00   
 t r   3 00 00 00 00 00 00 00 00   
 i e   4 00 00 00 00 00 00 00 00   
 c s   5 00 00 00 00 00 00 00 00   
 a s   6 00 00 00 00 00 00 00 00   
 l     7 00 00 00 00 00 00 00 00 

然而,我們的卷積核尺寸要小得多。對於我們將用於理論模型的演算法,3x3 卷積核就足夠了。

在實現卷積核時,重要的是要記住,我們正在接受兩個函式並生成第三個函式。原始函式可以被丟棄,或者可以返回到系統中用於其他目的,例如降噪。因此,當我們在軟體中實現我們的濾波器時,我們將希望生成一個函式,該函式將返回一個與輸入緩衝區大小相同的填充緩衝區給使用者。

卷積操作應該看起來非常類似於此虛擬碼

function Convolution ( in_buffer[8][8], out_buffer[8][8], divisor )
{
   define kernel[3][3];

   for (x from 0 to 7)
      for (y from 0 to 7)
         out_buffer[x][y]  = 0; //make sure the buffer is zeroed

         if (x-1 >= 0 && y-1 >= 0)
            out_buffer[x][y] += in_buffer[x-1][y-1] * kernel[0][0];
         if (x-1 >= 0)
            out_buffer[x][y] += in_buffer[x-1][y] * kernel[0][1];
         if (x-1 >= 0 && y+1 <= 7)
            out_buffer[x][y] += in_buffer[x-1][y+1] * kernel[0][2];

         if (y-1 >= 0)
            out_buffer[x][y] += in_buffer[x][y-1] * kernel[1][0];
         //this is our "free" operation, the only one that will always work.
            out_buffer[x][y] += in_buffer[x][y] * kernel[1][1];
         if (y+1 <= 7)
            out_buffer[x][y] += in_buffer[x][y+1] * kernel[1][2];

         if (x+1 <= 7 && y-1 >= 0)
            out_buffer[x][y] += in_buffer[x+1][y-1] * kernel[2][0];
         if (x+1 <= 7)
            out_buffer[x][y] += in_buffer[x+1][y] * kernel[2][1];
         if (x+1 <= 7 && y+1 <= 7)
            out_buffer[x][y] += in_buffer[x+1][y+1] * kernel[2][2];

         if (divisor && divisor > 0)
            out_buffer[x][y] /= divisor;
         else
            out_buffer[x][y] /= 9;

      end for
   end for
}

上述內容可能在您的硬體上進行了最佳化,因為您通常可以透過確保您的緩衝區足夠大來避免分支語句,以捕獲可能發生的溢位,而無需進行邊界測試,以記憶體換分支操作。您的演算法通常不需要除了常數核心之外的任何臨時資料,如果您過去處理過訊號處理,您可能已經在工具包中擁有類似的功能。

人們經常問在濾波器邊緣(稱為 **邊緣條件**)應該怎麼做。有許多選擇,例如選擇已知的“背景強度”並將其應用於該元素(在本例中,資料為零,因此不會影響核心操作;這與在本例中截斷濾波器完全相同),但是還有許多其他方法來處理這種情況,包括擴充套件影像邊緣的資料,截斷核心並且不執行操作,或者根本不將核心應用於超出邊緣的點(通常稱為“就地複製”)。

您還會注意到,我們在畫素操作期間沒有指定任何 else 情況;在所有不進行操作的情況下,我們假設該操作的輸出將為零。例如,如果卷積核的中心項為零,就像通常情況下那樣,沒有必要進行乘法並得到零。只需跳過該計算並繼續下一個。

在某些情況下,例如如果我們的資料是整數格式,我們需要應用一個除數來滿足數字格式的約束(在本例中,我們的緩衝區由 8 位資料組成)。除數通常是核心寬度乘以核心高度(3x3 為 9,5x5 為 25 等等),但也有一些情況下可能需要其他除數。

索貝爾運算元

[編輯 | 編輯原始碼]

我們將要看的第一個核心是 **索貝爾運算元**,也稱為 **邊緣檢測** 核心。索貝爾運算元透過降低低頻畫素的強度,同時提高高頻畫素的強度,來強調影像函式中的高頻變化。本質上,索貝爾運算元正在對影像的每個座標處的強度求二階導數,然而,對於那些對數學不太瞭解的人,以及我們中的一些人來說,將其理解為卷積濾波器更容易。

 -1 0 1
 -2 0 2
 -1 0 1

我們可以透過檢視常數函式 f(x) 的一維二階導數來思考這一點:. 透過取常數並將它們儲存在一個矩陣中,我們構建了一維卷積核:. 我們透過將該矩陣與垂直矩陣 相乘,將其轉換為二維卷積核,該矩陣只是我們正在進行卷積的點的絕對差值。

這個相對簡單的濾波器有幾個變體。例如,一個常見的調整是強調的方向

 1 0 -1
 2 0 -2
 1 0 -1

這種調整將濾波器的前沿改為右側,而不是像前一個例子中那樣強調左側。它與上面相同的濾波器“翻轉”了,方法是將函式乘以 -1。

  1  2  1        -1 -2 -1 
  0  0  0   or    0  0  0
 -1 -2 -1         1  2  1

這些是原始濾波器的旋轉,分別強調向上和向下。

反捲積

[編輯 | 編輯原始碼]
華夏公益教科書