《在紋線方向上進行平滑濾波,在紋線的垂直方向上進行銳化濾波》
--Gabor增強的具體
實踐
一、問題提出
一般認為“Gabor小波感受野模擬線性濾波器,能對圖像進行較好的智能收斂,從而智能增強圖像。Gabor小波是智能收斂增強的物理模型”
那么,問題是在實際過程中,如何實現“Gabor小波的智能收斂”,達到“智能增強效果”?
二、解題思路
使用工具,能夠簡單地得到Gabor增強的核;而對於想要增強效果的物體,首先要得到它的梯度數據,並且按照“在紋線方向上進行平滑濾波,在垂直方向上進行銳化濾波”的方式,按照不同方向選擇不同Gabor增強核的方法進行圖像增強。這樣就能夠得到“智能增強”。
三、問題關鍵
1、實現不同方向不同尺度的Gabor增強核,這個可以直接通過函數得到。在實際過程中,使用核心要經過量化的過程,否則將無法使用;
2、得到原始圖片的梯度數據。而這里的梯度數據,可以用方向場
的方式保存下來;
3、使用量化的Gabor核,依據梯度數據,對原始圖像進行增強。
四、解析過程
1
、Gabor核的生成(只描述數學結果,不做推導)
Gabor函數是由高斯函數和三角(傅里葉)函數構成的,周期振盪函數。
我們常見的一維Gabor函數為(基於正定傅里葉公式和高斯變換在實軸上投影):

同時,二維的Gabor函數為:

那么,根據矩陣定義 :

全部帶回Gabor函數二維表達式,得到

2、固定方向的Gabor增強,就是用構建好的卷積核去做卷積。這樣可以對核主要方向上體現出增強效果,對於垂直方向上,體現小波振盪效果。
(
圖片為核心、原始圖片和效果)

從結果圖片可以看到,在豎直方向上的紋理大多得到了一定的增強;但是在水平方向結果就非常差。
(核心生成函數)
// ks 核的大小
// sig σ:高斯函數的標准差
// th θ:Gabor核函數的方向
// lm λ:正弦函數波長;
// ps ψ:相位偏移
// 本例中寬高比為1,也就是原始定義中的γ=1,這樣得到的Gabor核的寬高是一致的
cv::Mat mkKernel(int ks, double sig, double th, double lm, double ps)
{
int hks = (ks-1)/2;
double theta = th*CV_PI/180;
double psi = ps*CV_PI/180;
double del = 2.0/(ks-1);
double lmbd = lm;
double sigma = sig/ks;
double x_theta;
double y_theta;
cv::Mat kernel(ks,ks, CV_32F);
for (int y=-hks; y<=hks; y++)
{
for (int x=-hks; x<=hks; x++)
{
x_theta = x*del*cos(theta)+y*del*sin(theta);
y_theta = -x*del*sin(theta)+y*del*cos(theta);
kernel.at<float>(hks+y,hks+x) = (float)exp(-0.5*(pow(x_theta,2)+pow(y_theta,2))/pow(sigma,2))* cos(2*CV_PI*x_theta/lmbd + psi);
}
}
return kernel;
}
這個函數基本上是依據wikipad上面對於Gabor的定義編寫的。注意里面的
γ=1,
這是沒有實際說明的。但是這樣得到的結果存在一個最主要的問題就是“只能對一個方向進行濾波”,而且考慮到之后可能會在原始圖像的不同像素上面使用不同的核進行濾波,所以要采用其他的計算方式。
“為了加速度,將Gabor函數做成模板,用模板來擬合Gabor函數”(為什么,沒找到依據)

3、判定得到原始圖片的方向場
;
這是另一個問題,所謂方向場指的主要圖像數據和法線之間的夾角。具體運算可以參考《精通visual c++指紋模式識別系統算法及實現》110頁附近。
4、依據方向場
,在不同方向選擇不同的Gabor核,對原始圖像進行增強。
利用Gabor小波函數,可以在方向場上對圖像進行增強,以彌補圖像中紋線的斷裂等不足,在垂直的方向上,進行振盪增強。


int g_DDSite[12][7][2] = {
-3, 0, -2, 0, -1, 0, 0, 0, 1, 0, 2, 0, 3, 0,
-3,-1, -2,-1, -1, 0, 0, 0, 1, 0, 2, 1, 3, 1,
-3,-2, -2,-1, -1,-1, 0, 0, 1, 1, 2, 1, 3, 2,
-3,-3, -2,-2, -1,-1, 0, 0, 1, 1, 2, 2, 3, 3,
-2,-3, -1,-2, -1,-1, 0, 0, 1, 1, 1, 2, 2, 3,
-1,-3, -1,-2, 0,-1, 0, 0, 0, 1, 1, 2, 1, 3,
0,-3, 0,-2, 0,-1, 0, 0, 0, 1, 0, 2, 0, 3,
-1, 3, -1, 2, 0, 1, 0, 0, 0,-1, 1,-2, 1,-3,
-2, 3, -1, 2, -1, 1, 0, 0, 1,-1, 1,-2, 2,-3,
-3, 3, -2, 2, -1, 1, 0, 0, 1,-1, 2,-2, 3,-3,
-3, 2, -2, 1, -1, 1, 0, 0, 1,-1, 2,-1, 3,-2,
-3, 1, -2, 1, -1, 0, 0, 0, 1, 0, 2,-1, 3,-1
};
//將 173- 8 = 165分為11等分
int DDIndex(int angle)
{
/////////////////////////////////////////////////////////////////////////
// angle: [in] 角度 (0 - 180)
/////////////////////////////////////////////////////////////////////////
if(angle >= 173 || angle < 8)
{
return 0;
}
else
{
return ((angle-8)/15 + 1);
}
}
//org是方向場 dst是輸入輸出結果
void orientEnhance(Mat org,Mat& dst)
{
int x, y;
int i;
int d = 0;
int sum = 0;
// 紋線方向上進行平滑濾波的平滑濾波器
int Hw[7] = {1, 1, 1, 1, 1, 1, 1};
// 紋線方向的垂直方向上進行銳化濾波的銳化濾波器
int Vw[7] = {-3, -1, 3, 9, 3, -1, -3};
int hsum = 0;
int vsum = 0;
int temp = 0;
int IMGW = org.cols;
int IMGH = org.rows;
BYTE *lpSrc = NULL;
BYTE *lpDir = NULL;
BYTE *g_lpOrient = org.ptr<uchar>(0);
BYTE *g_lpOrgFinger = dst.ptr<uchar>(0);
BYTE *g_lpTemp = dst.ptr<uchar>(0);
//BYTE *g_lpTemp = new BYTE[IMGW * IMGH];
// 紋線方向上進行平滑濾波
temp = 0;
for(y = 0; y < IMGH; y++)
{
for(x = 0; x < IMGW; x++)
{
lpDir = g_lpOrient + temp + x;
lpSrc = g_lpOrgFinger + temp + x;
// 紋線方向的索引
// 找到划分的等分
d = DDIndex(*lpDir); //求的方向
sum = 0;
hsum = 0;
for(i = 0; i < 7; i++)
{
//x對應0 y對應1
if(y+g_DDSite[d][i][1] < 0 || y+g_DDSite[d][i][1] >= IMGH ||
x+g_DDSite[d][i][0] < 0 || x+g_DDSite[d][i][0] >= IMGW)
{
continue;
}
sum += Hw[i]*(*(lpSrc + g_DDSite[d][i][1]*IMGW + g_DDSite[d][i][0]));
hsum += Hw[i];
}
if(hsum != 0)
{
*(g_lpTemp + temp + x) = (BYTE)(sum/hsum);
}
else
{
*(g_lpTemp + temp + x) = 255;
}
}
temp += IMGW;
}
// 紋線方向的垂直方向上進行銳化濾波
temp = 0;
for(y = 0; y < IMGH; y++)
{
for(x = 0; x < IMGW; x++)
{
lpDir = g_lpOrient + temp + x;
lpSrc = g_lpTemp + temp + x;
// 紋線方向的垂直方向的索引
// 橫過來計算
d = (DDIndex(*lpDir)+6) % 12;
sum = 0;
vsum = 0;
for(i = 0; i < 7; i++)
{
if(y+g_DDSite[d][i][1] < 0 || y+g_DDSite[d][i][1] >= IMGH ||
x+g_DDSite[d][i][0] < 0 || x+g_DDSite[d][i][0] >= IMGW)
{
continue;
}
sum += Vw[i]*(*(lpSrc + g_DDSite[d][i][1]*IMGW + g_DDSite[d][i][0]));
vsum += Vw[i];
}
if(vsum > 0)
{
sum /= vsum;
if(sum > 255)
{
*(g_lpOrgFinger + temp + x) = 255;
}
else if(sum < 0)
{
*(g_lpOrgFinger + temp + x) = 0;
}
else
{
*(g_lpOrgFinger + temp + x) = (BYTE)sum;
}
}
else
{
*(g_lpOrgFinger + temp + x) = 255;
}
}
temp += IMGW;
}
結果

但是,圖像的大小應該是有限制的,如果圖像過大,現在選擇的參數就應該不正確,得到類似下面的結果

如果想在其它圖像上使用,關鍵是要得到和本例中同樣的方向場
所以,如果用在其它地方,首先是要把圖像的尺度縮放正確,可以得到以下結果
自然指紋

血管


五、結果小結
1、善用資源。很多實現,在wikipad上面就已經有很好的結果了,略加修改就可以得到目的;
2、不斷讀書,書本中有很多靈感;多動筆墨,在書寫中思考;
3、攻堅克難,一定要把經典吃透,能夠獲得的遠遠比表面的問題多得多。
感謝閱讀至此,如果需要繼續交流請Email 1755311380@qq.com