《在纹线方向上进行平滑滤波,在纹线的垂直方向上进行锐化滤波》
--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 [email protected]