reference: http://blog.csdn.net/jorg_zhao?viewmode=contents
第一個項目根據公司那邊提供的學習資料,需要用到Gabor濾波器對圖像進行處理,公司那邊關於項目的說法比較商業化,叫X-Ray Image Auto Judging System,之前找了很久論文都沒有思路,用這個英文查找論文,也是不對路,這讓我在前期浪費不少時間,後來查閱大量論文之後,確定關於目前的項目的學術說法應該是“輪胎X射線圖缺陷檢測”,英文是“X-Ray Defect
Detection”,目前放暑假,不在學校檢索論文比較麻煩,只能根據各路大神網友的博客在做這個項目。
這個項目涉及的主要學術方向是“複雜多紋理圖像提取”,能查到的最普遍的做法是採用Gabor濾波器對圖像進行處理,可能閱讀論文量不到,也許有更好的圖像處理方法和思路,這裏記錄下我目前的做法吧,有效果,但是不是特別好。
看到的大神寫到的博客,非常詳細,大部分信息整理自以下博客:
1. Gabor濾波器詳細介紹:http://mplab.ucsd.edu/tutorials/gabor.pdf
2. Gabor濾波器學習:http://blog.csdn.net/jinshengtao/article/details/17797641
3. 國外大學對Gabor濾波器的參數的講解:http://matlabserver.cs.rug.nl/edgedetectionweb/web/edgedetection_params.html
4. 備份論文,在校外沒法下載:http://www.researchgate.net/publication/252703921_Designing_multiple_Gabor_filters_for_multitexture_image_segmentation
5. 德國某公司的宣傳網頁,看樣子做的非常棒:http://www.cyxplus.fr/produit/our-industy-activities/cyxpert-automatic-defect-detection
一、Gabor Filter
一維Gabor濾波器
Gabor濾波器是處理一維信號(比如音頻)最佳的帶通濾波器,一個複雜的Gabor濾波器是一個高斯核函數乘以一個複雜的sin函數(A complex Gabor filter is defined as the product of a Gaussian kernel times a complex
sinusoid),比如:
![]()
k theta fo是濾波器的參數。我們可以把這個複雜的Gabor濾波器想象成兩個相位濾波器分成了一個複雜函數的實部和虛部,
![]()
二維Gabor濾波器
但是我要用到的是二維Gabor濾波器,也叫空間Gabor濾波器(The Spatial (2-D) Gabor Filter),但是第一條參考中給出的二維Gabor濾波器講解太過於。。怎麼說。
按照普通的解釋來吧,二維Gabor函數的數學表達式:
![]()
如何得到的這個公式呢?大神博客(http://blog.csdn.net/yanmy2012/article/details/8090400)中給出的詳細求解過程,截圖如下:
![]()
![]()
![]()
根據第三條參考文獻的解釋,關於實部和虛部的說明,只有一句話,實部可以對圖像進行平滑濾波,虛部可以用來邊緣檢測,具體的用法可以可以參考相關論文,不在學校下論文不方便,暫且寫這點吧。
下面具體看一下Gabor濾波器的參數說明:
![]()
![]()
![]()
二、Gabor濾波器的應用和適應性
根據第二條參考文獻給出的說明,可以總結以下幾點:
1. Gabor濾波器可以很好的近似單細胞的感受野細胞(光強刺激下的傳遞函數),在提取目標的局部空間和頻率域信息方面具有良好的特性。
2. 雖然Gabor小波本身不能構成正交基,但在特定參數下可構成緊框架。Gabor小波對於圖像的邊緣敏感,能夠提供良好的方向選擇和尺度選擇特性,而且對於光照變化不敏感,能夠提供對光照變化良好的適應性。-------Gabor小波被廣泛應用於視覺信息理解
3. 二維Gabor小波變換是在時頻域進行信號分析處理的重要工具,其變換系數有着良好的視覺特性和生物學背景。------因此被廣泛應用於圖像處理、模式識別等領域。
4. 與傳統的傅立葉變換相比,Gabor小波變換具有良好的時頻局部化特性。即非常容易地調整Gabor濾波器的方向、基頻帶寬及中心頻率從而能夠最好的兼顧信號在時空域和頻域中的分辨能力.
5. Gabor小波變換具有多分辨率特性即變焦能力。即採用多通道濾波技術,將一組具有不同時頻域特性的Gabor小波應用於圖像變換,每個通道都能夠得到輸入圖像的某種局部特性,這樣可以根據需要在不同粗細粒度上分析圖像。
6. 在特徵提取方面,Gabor小波變換與其它方法相比:一方面其處理的數據量較少,能滿足系統的實時性要求;另一方面,小波變換對光照變化不敏感,且能容忍一定程度的圖像旋轉和變形,當採用基於歐氏距離進行識別時,特徵模式與待測特徵不需要嚴格的對應,故能提高系統的魯棒性。
爲什麼能夠有這些應用呢?我們需要深入的理解下Gabor的特性。根據大神博客(http://blog.csdn.net/yanmy2012/article/details/8090400)中的詳細解釋。在基本視覺皮層裏的簡單細胞的感受野侷限在很小的空域範圍內,並且高度結構化。
1. Gabor變換所採用的核(Kernels)與哺乳動物視覺皮層簡單細胞2D感受野剖面(Profile)非常相似,具有優良的空間局部性和方向選擇性,能夠抓住圖像局部區域內多個方向的空間頻率(尺度)和局部性結構特徵。這樣,Gabor分解可以看作一個對方向和尺度敏感的有方向性的顯微鏡。
2. 二維Gabor函數也類似於增強邊緣以及峯、谷、脊輪廓等底層圖像特徵,這相當於增強了被認爲是面部關鍵部件的眼睛、鼻子、嘴巴等信息,同時也增強了諸於黑痣、酒窩、傷疤等局部特徵,從而使得在保留總體人臉信息的同時增強局部特性成爲可能。它的小波特性說明了Gabor濾波結果是描述圖像局部灰度分佈的有力工具,因此,可以使用Gabor濾波來抽取圖像的紋理信息。
3. 由於Gabor特徵具有良好的空間局部性和方向選擇性,而且對光照、姿態具有一定的魯棒性,因此在人臉識別中獲得了成功的應用。然而,大部分基於Gabor特徵的人臉識別算法中,只應用了Gabor幅值信息,而沒有應用相位信息,主要原因是Gabor相位信息隨着空間位置呈週期性變化,而幅值的變化相對平滑而穩定,幅值反映了圖像的能量譜,Gabor幅值特徵通常稱爲Gabor
能量特徵(Gabor Energy Features)。Gabor小波可像放大鏡一樣放大灰度的變化,人臉的一些關鍵功能區域(眼睛、鼻子、嘴、眉毛等)的局部特徵被強化,從而有利於區分不同的人臉圖像。
4. Gabor小波核函數具有與哺育動物大腦皮層簡單細胞的二維反射區相同的特性,即具有較強的空間位置和方向選擇性,並且能夠捕捉對應於空間和頻率的局部結構信息;Gabor濾波器對於圖像的亮度和對比度變化以及人臉姿態變化具有較強的健壯性,並且它表達的是對人臉識別最爲有用的局部特徵。Gabor
小波是對高級脊椎動物視覺皮層中的神經元的良好逼近,是時域和頻域精確度的一種折中。
三、Gabor濾波器的編程實現
這裏我用opencv做的,因爲對matlab不是很熟,二是工業應用一般都是C或C++做的,代碼給出吧,我用mfc做的界面,之前有好幾個版本,驗證了幾個算法,最後這個就是爲了看效果的,比較簡單。因爲用的opencv3.0,所以顯示圖像時遇到了麻煩,查找相關資料,解決辦法是自己建立CvvImage.h
CvvImage.cpp文件,添加到工程中即可。還有一個要說明的是,Gabor濾波器的實現,是用到的網上搜到的Gabor.h Gabor.cpp實現的,只是進行了簡單的註釋和文檔整理。原作者。。。額,sorry,找的資料太多,不知道出處了,見諒啊!
Gabor.h頭文件
-
#ifndef _GABOR_H
-
#define _GABOR_H
-
#include <stdio.h>
-
#include <iostream>
-
#include <cv.h>
-
-
#define PI 3.14159
-
#define GAMMA 0.5 //The default value of γ,which is the spatial aspect ratio (sigma_x/sigma_y)
-
#define RATIO_S2L 0.56 //The default value of σ/λ
-
#define THETA 45
-
class Gabor
-
{
-
public:
-
-
Gabor(float dLambda, float dTheta, float dRatio_S2L = RATIO_S2L, float dGamma = GAMMA, float dPhi = 0);
-
-
~Gabor();
-
-
-
void init(float dLambda, float dTheta, float dPhi, float dGamma = GAMMA);
-
-
void init(float dSigma, float dTheta, float dPhi);
-
-
void init();
-
-
-
-
bool is_kernel(){ return bKernel; }
-
-
bool is_init() { return bInit; }
-
-
bool is_param() { return bParam; }
-
-
CvMat* get_Mat() { return pGaborfilter; }
-
-
IplImage* get_NormImage();
-
-
IplImage* do_Filter(const IplImage *src);
-
-
protected:
-
bool bParam;
-
bool bKernel;
-
bool bInit;
-
float Lambda;
-
float Theta;
-
float Sigma;
-
float Gamma;
-
float Phi;
-
CvSize GaborWindow;
-
CvMat *pGaborfilter;
-
-
private:
-
void create_kernel();
-
};
-
#endif
Gabor.cpp源文件
-
#include "stdafx.h"
-
#include <iostream>
-
#include <cv.h>
-
#include <highgui.h>
-
#include <cstdlib>
-
#include "Gabor.h"
-
-
-
Gabor::Gabor(float dLambda, float dTheta, float dRatio_S2L, float dGamma, float dPhi)
-
{
-
Lambda = dLambda;
-
Theta = dTheta;
-
Sigma = dLambda*dRatio_S2L;
-
Gamma = dGamma;
-
Phi = dPhi;
-
pGaborfilter = NULL;
-
bParam = 1;
-
}
-
-
Gabor::~Gabor()
-
{
-
cvReleaseMat(&pGaborfilter);
-
}
-
void Gabor::init()
-
{
-
float dtmp;
-
int itmp;
-
if (is_param() == 0)
-
{
-
AfxMessageBox("The parameters are not enough!");
-
return;
-
}
-
-
-
dtmp = sqrt(48 * pow(Sigma, 2) + 1);
-
itmp = cvRound(dtmp);
-
if (itmp % 2 == 0)
-
itmp++;
-
GaborWindow.height = GaborWindow.width = itmp;
-
bInit = 1;
-
-
create_kernel();
-
}
-
-
void Gabor::init(float dSigma, float dTheta, float dPhi)
-
{
-
float dtmp;
-
int itmp;
-
-
Sigma = dSigma;
-
Theta = dTheta;
-
Phi = dPhi;
-
Gamma = GAMMA;
-
Lambda = Sigma / RATIO_S2L;
-
bParam = 1;
-
-
dtmp = sqrt(24 * pow(Sigma, 2));
-
itmp = cvRound(dtmp);
-
if (itmp % 2 == 0)
-
itmp++;
-
GaborWindow.height = GaborWindow.width = itmp;
-
bInit = 1;
-
-
create_kernel();
-
}
-
-
void Gabor::init(float dLambda, float dTheta, float dPhi, float dGamma)
-
{
-
-
float dtmp;
-
int itmp;
-
-
Lambda = dLambda;
-
Theta = dTheta;
-
Phi = dPhi;
-
Gamma = dGamma;
-
Sigma = Lambda * RATIO_S2L;
-
bParam = 1;
-
-
dtmp = sqrt(24 * pow(Sigma, 2));
-
itmp = cvRound(dtmp);
-
if (itmp % 2 == 0)
-
itmp++;
-
GaborWindow.height = GaborWindow.width = itmp;
-
bInit = 1;
-
-
create_kernel();
-
}
-
-
-
void Gabor::create_kernel()
-
{
-
float tmp1, tmp2, xtmp, ytmp, re;
-
int i, j, x, y;
-
-
if (is_init() == 0)
-
{
-
AfxMessageBox("The paremeters haven't been initialed!");
-
}
-
-
pGaborfilter = cvCreateMat(GaborWindow.height, GaborWindow.width, CV_32FC1);
-
for (i = 0; i < GaborWindow.height; i++)
-
{
-
for (j = 0; j < GaborWindow.width; j++)
-
{
-
x = j - GaborWindow.width / 2;
-
y = i - GaborWindow.height / 2;
-
-
-
-
-
xtmp = (float)x*cos(Theta) + (float)y*sin(Theta);
-
ytmp = -(float)x*sin(Theta) + (float)y*cos(Theta);
-
-
tmp1 = exp(-(pow(xtmp, 2) + pow(ytmp*Gamma, 2)) / (2 * pow(Sigma, 2)));
-
tmp2 = cos(2 * PI*xtmp / Lambda + Phi);
-
-
re = tmp1*tmp2;
-
cvSetReal2D((CvMat*)pGaborfilter, i, j, re);
-
}
-
}
-
-
bKernel = 1;
-
}
-
-
IplImage* Gabor::get_NormImage()
-
{
-
if (is_kernel() == 0)
-
{
-
AfxMessageBox("The filter hasn't bee created!");
-
return NULL;
-
}
-
-
IplImage *pImg = cvCreateImage(GaborWindow, IPL_DEPTH_32F, 1);
-
IplImage *pImgU8 = cvCreateImage(GaborWindow, IPL_DEPTH_8U, 1);
-
CvMat * pMat = cvCreateMat(GaborWindow.height, GaborWindow.width, CV_32FC1);
-
-
cvCopy(pGaborfilter, pImg);
-
-
-
cvNormalize((IplImage*)pImg, (IplImage*)pImg, 0, 255, CV_MINMAX, NULL);
-
-
cvConvertScaleAbs(pImg, pImgU8, 1, 0);
-
-
return pImgU8;
-
-
}
-
-
IplImage * Gabor::do_Filter(const IplImage *src)
-
{
-
-
if (is_kernel() == false)
-
{
-
printf("The Gabor Kernel has not been created!");
-
return NULL;
-
}
-
-
IplImage *pDestImage = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
-
IplImage *tmpImg = cvCloneImage(src);
-
IplImage *tmpGrayImg = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 1);
-
-
-
if (tmpImg->nChannels != 1)
-
{
-
cvCvtColor(tmpImg, tmpGrayImg, CV_BGR2GRAY);
-
}
-
else
-
{
-
cvReleaseImage(&tmpGrayImg);
-
tmpGrayImg = tmpImg;
-
}
-
-
CvMat *pGaborKernel = get_Mat();
-
-
cvFilter2D(tmpGrayImg, pDestImage, pGaborKernel, cvPoint((GaborWindow.width - 1) / 2, (GaborWindow.height - 1) / 2));
-
-
cvReleaseImage(&tmpImg);
-
cvReleaseImage(&tmpGrayImg);
-
-
return pDestImage;
-
}
對話框Dlg.cpp,這裏只給出了實現按鈕的實現函數代碼,完整的工程代碼,看文章最後的附錄:
-
void CTyreXDlg::OnBnClickedBtndstimg()
-
{
-
-
UpdateData(TRUE);
-
-
CSliderCtrl *pSlidCtrlTheta = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_THETA);
-
int tmptheta = pSlidCtrlTheta->GetPos();
-
-
CString sValueTheta = "";
-
sValueTheta.Format("%d", tmptheta);
-
SetDlgItemText(IDC_StaticTheta, sValueTheta);
-
-
-
CSliderCtrl *pSlidCtrlLambda = (CSliderCtrl*)GetDlgItem(IDC_SLIDER_LAMBDA);
-
int tmplambda = pSlidCtrlLambda->GetPos();
-
-
CString sValueLambda = "";
-
sValueLambda.Format("%d", tmplambda);
-
SetDlgItemText(IDC_StaticLambda, sValueLambda);
-
-
-
if (SrcImg == NULL)
-
{
-
AfxMessageBox("沒有可處理圖像!!!");
-
return;
-
}
-
-
-
-
-
-
Gabor GaborFilter(tmplambda *0.1, PI*tmptheta / 180, RATIO_S2L, GAMMA, 0);
-
-
GaborFilter.init();
-
-
IplImage* pGaborNormImg = GaborFilter.get_NormImage();
-
-
IplImage* poutGaborimg;
-
poutGaborimg = GaborFilter.do_Filter(SrcImg);
-
-
-
-
IplImage *pGrayImage;
-
-
pGrayImage = cvCreateImage(cvGetSize(poutGaborimg), IPL_DEPTH_8U, 1);
-
-
if (poutGaborimg->nChannels != 1)
-
cvCvtColor(poutGaborimg, pGrayImage, CV_BGR2GRAY);
-
else
-
{
-
cvReleaseImage(&pGrayImage);
-
pGrayImage = poutGaborimg;
-
}
-
-
-
IplImage *pBinaryImage;
-
pBinaryImage = cvCreateImage(cvGetSize(pGrayImage), IPL_DEPTH_8U, 1);
-
cvThreshold(pGrayImage, pBinaryImage, 0, 255, CV_THRESH_BINARY);
-
-
-
ShowImgFunc(pBinaryImage, IDC_ShowDstImg);
-
-
-
-
cvReleaseImage(&DstImg);
-
cvReleaseImage(&DstGaborImg);
-
cvReleaseImage(&DstBinaryImg);
-
}
附錄:
1. 上面代碼的完整的工程文件,可以到這裏下載,不需要積分。我運行成功,但是出現錯誤,或者有不合理的地方,希望各位看到能告訴我一聲,共同進步。
工程文件完整下載地址:http://download.csdn.net/detail/jorg_zhao/8949247