一、圖像數位化
通過傳感器獲得的圖像是平面坐標(x,y)的連續函數f(x,y),它的值圖像對應位置的亮度。為了能夠讓計算機來處理,需要對圖像進行採樣,並且對亮度值進行量化。
1、採樣。對連續函數f(x,y)進行採樣,就是分別對x軸和y軸,按照固定間隔取值,得到平面坐標上的M×N個點,將其函數值作為元素生成M行N列的矩陣。
2、量化亮度值。將f(x,y)的值轉化為等價的整數值的過程稱為量化,量化的級別越高,圖像越細緻。通常將亮度值表示為0-255之間的整數。
這樣,在計算機中通常以矩陣表示數字圖像,矩陣的元素對應圖像的亮度信息。
二、距離
滿足以下三個條件的函數D稱作距離:
(1)同一性:
(2)對稱性:
(3)三角不等式:
數字圖像的距離有多種定義方式,包括歐式距離、城市街區距離、棋盤距離等。以下以兩坐標點a=(i,j)和b=(k,l)的距離為例,來說明各種距離的定義方式。
歐式距離DE就是通常所說的距離,它定義為
歐式距離在事實上比較直觀,但是平方根計算比較費時,且距離可能不是數。
城市街區距離D4,它定義為在只允許橫向和縱向運動的情況下,從起點到終點的移動步數。用公式表示為
符號D4中的4表示在這種定義下,像素點是4鄰接的,即每個點只與它的上、下、左、右相鄰的4個點之間的距離為1。
如果允許橫向、縱向和沿對角線方向移動,則可以得到棋盤距離D8的定義
符號D8中的8表示在這種定義下,像素點是8鄰接的,即每個點只與它的上、下、左、右、四個對角線方向相鄰的8個點之間的距離為1。
顯然,以上三種距離的定義都滿足距離的定義條件。
三、距離變換
距離變換也叫作距離函數或者斜切算法。它是距離概念的一個應用,圖像處理的一些算法以距離變換為基礎。距離變換描述的是圖像中像素點與某個區域塊的距離,區域塊中的像素點值為0,臨近區域塊的像素點有較小的值,離它越遠值越大。
以二值圖像為例,其中區域塊內部的像素值為1,其他像素值為0。距離變換給出每個像素點到最近的區域塊邊界的距離,區域塊內部的距離變換結果為0。輸入圖像如圖1所示,D4距離的距離變換結果如圖2所示。
圖1:輸入圖像
圖2:D4距離下距離變換結果
下面來討論距離變換算法,其核心是利用兩個小的局部掩膜遍歷圖像。第一遍利用掩模1,左上角開始,從左往右,從上往下。第二遍利用第二個掩模,右下角開始,從右往左,從下往上。掩模形狀如下圖所示:
按照某種距離(如:D4距離或D8距離)對大小為M×N的圖像中的區域塊作距離變換,算法過程如下:
1、建立一個大小為M×N的數組F,作如下的初始化:將區域塊中的元素設置為0,其餘元素設置為無窮;
2、利用掩模1(mask1),左上角開始,從左往右,從上往下遍歷數組,將掩模中P點對應的元素的值作如下更新:
3、利用掩模2(mask2),右下角開始,從右往左,從下往上遍歷數組,將掩模中P點對應的元素的值作如下更新:
最終得到的更新後的數組即為距離變換的結果。
這個算法過程在圖像編邊界需要做出調整,因為在邊界處,掩模不能全部覆蓋圖像,這時可以將掩模中沒有對應元素的位置的值當作0來處理。
四、OpenCV代碼實現
這個算法過程經過很多的改進,但基本原理並沒有區別。開源計算機視覺庫OpenCV中,距離變換算法有相應的實現,聲明如下:
CV_EXPORTS_W void distanceTransform( InputArray src, OutputArray dst,
int distanceType, int maskSize, int dstType=CV_32F);
參數詳解:
InputArray src:輸入圖像,一般為二值圖像;
OutputArray dst:輸出的圖像,距離變換結果;
int distanceType:用於距離變換的距離類型(歐氏距離:DIST_L2 = 2;$D_4$距離:DIST_L1 = 1;$D_8$距離:DIST_C = 3等);
int mask_size:距離變換掩模的大小,一般為3或5;
int dstType:輸出圖像的數據類型,可以為CV_8U或CV_32F。
下面我們用一個具體的例子來展示距離變換的效果。將大小為480*480,其中有三個像素點設置為1,其餘都為0的一張圖片作為輸入圖像,分別在歐式距離、$D_4$距離和$D_8$距離下,距離變換的結果。
效果如下圖所示:
歐式距離下的距離變換
D4距離下的距離變換
D8距離下的距離變換
下面是代碼實現:
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat mat(480, 480, CV_8UC1, Scalar(0)), transMatE, transMatD4, transMatD8;
mat.at<uchar>(100, 200) = 1;
mat.at<uchar>(200, 100) = 1;
mat.at<uchar>(300, 300) = 1;
mat = 1 - mat;
imshow("原始圖片", mat);
distanceTransform(mat, transMatE, DIST_L2, 0);
distanceTransform(mat, transMatD4, DIST_L1, 0, CV_8U);
distanceTransform(mat, transMatD8, DIST_C, 0);
transMatE.convertTo(transMatE, CV_8U);
transMatD8.convertTo(transMatD8, CV_8U);
imshow("歐式距離變換後的圖片", transMatE);
imshow("D4距離變換後的圖片", transMatD4);
imshow("D8距離變換後的圖片", transMatD8);
waitKey();
return 0;