![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZlBnauUGM2E2YhZDO5AzY2MGNwIDOidDZlhTNzQGZhNTYjZDZfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.jpeg)
通過學習,掌握以下幾個問題:
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庫,用于收集這方面的代碼。
雖然這本書很精彩,裡面的代碼對于我來說都是右開創性的;但是不可否認很多地方,他的代碼寫的還是比較繁瑣、備援的,給閱讀移植帶來了不少困難。
使用的情況是這樣的
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);
imshow("dst",smo);
waitKey(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;
// 紋線方向的垂直方向上進行銳化濾波
lpSrc = g_lpTemp + temp + x;
// 紋線方向的垂直方向的索引
d = (DDIndex(*lpDir)+6) % 12;
vsum = 0;
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;
*(g_lpOrgFinger + temp + x) = 255;
了現在的代碼,下一步就可以思考如何對自然環境下的許多圖像進行增強了。
來自為知筆記(Wiz)目前方向:圖像拼接融合、圖像識别
聯系方式:[email protected]