
通過學習,掌握以下幾個問題:
1、核心算法,並且向GVF衍生;
2、核心庫封裝的方法
2016年11月16日06:52:51
昨日實現了梯度場和頻率場的計算。最大的感覺就是建立基礎代碼庫的重要性。
如果使用opencv或者別的代碼庫,可能它也能實現一些功能,特別對於建立在感官上的效果,差別不大。但是,如果是用於數學計算的,特別是對於我現在還不是很清楚過程,也不是很清楚結果的算法來說,精確的、容易比對的代碼更重要。在這種時候,我更願意采取原始的、按照定義實現的計算方法。
在昨天的頻度場計算中,我突破好幾天的困擾,直接按照定義修改代碼,比如計算頻度場
int main( int argc, char** argv )
{
Mat src = imread2gray("E:\\template\\1.bmp");
src.convertTo(src,CV_8U);//255的運算
pyrDown(src,src);
Mat dst;//結果
dst.create(src.size(),src.type());
int IMGH =src.rows;
int IMGW =src.cols;
int gradSum;
int grad;
long vx, vy, lvx, lvy;
unsigned char *lpSrc = NULL;
unsigned char *lpOri = NULL;
long angle, num;
double fAngle;
int r = 6;
int i;int j;
for (int y = 0;y<IMGH-1;y++)
{
for (int x=0;x<IMGW-1;x++)
{
lpOri = dst.ptr<uchar>(0) + y*IMGW + x;
lvx = 0;
lvy = 0;
num = 0;
for(i = -r; i <= r; i++) // 為提高速度,步長為
{
if(y+i<1 || y+i>=IMGH-1) continue;
for(j = -r; j <= r; j++) // 為提高速度,步長為
{
if(x+j<1 || x+j>=IMGW-1) continue;
lpSrc = src.ptr<uchar>(0) + (y+i)*(IMGW) + x+j;
//求x方向偏導
vx = *(lpSrc + IMGW + 1) - *(lpSrc + IMGW - 1) +
*(lpSrc + 1)*2 - *(lpSrc - 1)*2 +
*(lpSrc - IMGW + 1) - *(lpSrc - IMGW - 1);
//求y方向偏導
vy = *(lpSrc + IMGW - 1) - *(lpSrc - IMGW - 1) +
*(lpSrc + IMGW)*2 - *(lpSrc - IMGW)*2 +
*(lpSrc + IMGW + 1) - *(lpSrc - IMGW + 1);
lvx += vx * vy * 2;//sin(2sita)
lvy += vx*vx - vy*vy;//cos(2sita)
num++;
}
}
if(num == 0) num = 1;
// 求弧度
fAngle = atan2((float)lvy, (float)lvx);
// 變換到(0 - 2*pi)
if(fAngle < 0) fAngle += 2*PI;
// 求紋線角度
fAngle = (fAngle*EPI*0.5 + 0.5);
angle = (long)fAngle;
// 因為采用sobel算子,所以角度偏轉了度,所以要旋轉求得的角度
angle -= 135;
// 角度變換到(-180)
if(angle <= 0) angle += 180;
angle = 180-angle;
// 最終紋線角度
*lpOri = (unsigned char)angle;
*(lpOri + 1) = (unsigned char)angle;
*(lpOri + IMGW) = (unsigned char)angle;
*(lpOri + IMGW + 1) = (unsigned char)angle;
}
}
pyrUp(dst,dst);
imwrite("e:/sandbox/n1dst.bmp",dst);
return 0;
}
這樣從結果的面上來看,已經是非常接近書中給出的效果了。
下一步,專門成立GOGVF項目作為GOCVHelper的一個部分,逐步地改造現有代碼庫,實現書中的效果。並且向GOGVF的按照定義實現做出努力。
2016年11月16日06:52:51 已經逐步移植代碼,從梯度一直做到了增強。雖然現在的代碼還有一些問題,但是基本不影響使用。並且生成了專門的GOGVF庫,用於收集這方面的代碼。
雖然這本書很精彩,里面的代碼對於我來說都是右開創性的;但是不可否認很多地方,他的代碼寫的還是比較繁瑣、冗余的,給閱讀移植帶來了不少困難。
使用的情況是這樣的
int main( int argc, char** argv )
{
Mat src = imread2gray("E:\\template\\2.bmp");
Mat grad = getGrads(src); //梯度場
Mat org = getOrientMap(src); //方向場
Mat seg;
segment(grad,seg); //對梯度場進行閾值,seg為分割結果
segment_clearEdge(src,org,seg);//反饋到src和org中了,這種方法倒也是方便
Mat equ = src.clone();
//cv::equalizeHist(src,equ);
equalize(src,equ);
Mat gauss = src.clone();
GaussSmooth(equ,gauss,0.4);
Mat smo = src.clone();
smooth(gauss,smo,1,1);
orientEnhance(org,smo);
orientEnhance(org,smo);
imshow("dst",smo);
waitKey(0);
return 0;
}
原始圖像
梯度圖像,可以看到,在指紋比較密集的地方,梯度很強,而在背景區域,比較干凈。
通過梯度場,可以背景前景分離。
方向場。基本上是表示了指紋線段角度的變化。特別觀察中間的位置,由255跳躍至0,是因為在中間的部分,指紋幾乎是水平的。
gaobor增強,現在在細節部分還有一點問題,但是已經基本體現出來特點了。
這是我第一次自己寫代碼實現gabor的效果,也是深入理解gabor的一次。回頭思考,指紋識別其實是很好的算法平台,因為采集到的圖片,本身背景前景分割還是比較干凈的;在以前,如果處理這樣的圖片,我可能會選擇閾值分割這種直觀的方法;在實現了frangi算法之后,很多時候我會拿frangi來實驗一下,看看效果。但是這次試用gabor增強,應該說是給我增加了一種新的思路,以后的眼界會更寬闊。。
gaobor增強的核心,是對前面計算出來的梯度場中的“紋線方向進行平滑濾波,紋線
的豎直方向進行銳化濾波
”
。那么首先就是要計算處正確的梯度場來。在本例中,圖片質量比較好,能夠通過幾乎是定義計算的方法計算出正確穩定的梯度場(但是在其他很多地方,可能不能這樣使用?用什么計算出正確的梯度場,作為一個專門的話題)。然后就是通過對梯度進行增強。這里才是實現gaobor的地方。這里貼出的是實現的代碼,推導過程分帖說明。關鍵就是“量化“。
int DDIndex(int angle)
{
/////////////////////////////////////////////////////////////////////////
// angle: [in] 角度 (0 - 180)
/////////////////////////////////////////////////////////////////////////
if(angle >= 173 || angle < 8)
{
return 0;
}
else
{
return ((angle-8)/15 + 1);
}
}
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++)
{
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;
}
了現在的代碼,下一步就可以思考如何對自然環境下的許多圖像進行增強了。
