嘎嘣gabor小波算子各参数直观理解与应用(c++实现)
一小段废话(简介)
gabor算子理论上就是三角函数和高斯函数的一个相乘本文简略介绍了从算子构建到滤波输出图片的整个过程,比较简单
“嘎嘣”算子构建
关于gabor的公式到处都是,我就懒得去扒了,大家想要具体公式可以自行检索相关文章,这里大致知道三角算子乘高斯算子就够了
上代码:
下面是opencv里的gabor算子生成算法,返回Mat形式的算子,有兴趣可以参考公式分析一波
Mat gabor::load_fliter(Size ksize, double sigma, double theta,double lambd, double gamma, double psi, int ktype)
{
double sigma_x = sigma;
double sigma_y = sigma/gamma;
int nstds = 3;
int xmin, xmax, ymin, ymax;
double c = cos(theta), s = sin(theta);
if( ksize.width > 0 )
xmax = ksize.width/2;
else
xmax = cvRound(std::max(fabs(nstds*sigma_x*c), fabs(nstds*sigma_y*s)));
if( ksize.height > 0 )
ymax = ksize.height/2;
else
ymax = cvRound(std::max(fabs(nstds*sigma_x*s), fabs(nstds*sigma_y*c)));
xmin = -xmax;
ymin = -ymax;
CV_Assert( ktype == CV_32F || ktype == CV_64F );
Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
double scale = 1;
double ex = -0.5/(sigma_x*sigma_x);
double ey = -0.5/(sigma_y*sigma_y);
double cscale = CV_PI*2/lambd;
for( int y = ymin; y <= ymax; y++ )
for( int x = xmin; x <= xmax; x++ )
{
double xr = x*c + y*s;
double yr = -x*s + y*c;
double v = scale*exp(ex*xr*xr + ey*yr*yr)*cos(cscale*xr + psi);
if( ktype == CV_32F )
kernel.at<float>(ymax - y, xmax - x) = (float)v;
else
kernel.at<double>(ymax - y, xmax - x) = v;
}
return kernel;
};
算子使用
就滤波呗,但这个操作一定要和卷积分清楚,大致上滤波要比卷积简略很多
另一件事就是注意下算子和图片的大小,显然四个循环,执行a.rowsa.colsb.rowsb.cols这么多次操作可能需要化费很长时间最好把算子控制在55左右
代码
Mat gabor::flter(Mat a,Mat b)
{
float m[20][20];
Mat c(a.rows,a.cols,CV_32F);
for(int tag_a=0;tag_a<a.rows;tag_a++)
for(int tag_b=0;tag_b<a.cols;tag_b++)
c.at<float>(tag_a,tag_b)=0.0;
for(int tag_a=0;tag_a<a.rows;tag_a++)
for(int tag_b=0;tag_b<a.cols;tag_b++)
for(int tag_c=0;tag_c<b.rows;tag_c++)
for(int tag_d=0;tag_d<b.cols;tag_d++)
{
int x=tag_c-b.rows/2;
int y=tag_d-b.cols/2;
if((tag_a+x)>=0&&(tag_b+y)>=0&&(tag_a+x)<a.rows&&(tag_b+y)<a.cols)
for(int tag_e=0;tag_e<3;tag_e++)
c.at<float>(tag_a,tag_b)+=a.at<Vec3b>(tag_a+x,tag_b+y)[tag_e]*b.at<float>(tag_c,tag_d)/3/b.cols/b.rows;
}
return c;
}
运行效果
全部代码(云玩家直接看这里)
cpp部分
#include"gabor.hpp"
int sigma=6;
int theta=0;
int lambd=5;
int gammaa=1;
int psi=0;
int ktype=CV_32F;
int col=5,row=5;
gabor a;
void aa(int,void*)
{
imshow("gabor",a.load_fliter(Size(col,row),sigma,theta,lambd,gammaa, psi, ktype ));
}
bool test=0;//0是滤波,1是算子
void bb(int,void*)
{
imshow("gabor",a.deal(a.load_fliter(Size(col,row), sigma,theta,lambd,gammaa, psi, ktype)));
}
int main()
{
if(test)
{
//算子尺寸在最上面,自己改,滤波函数默认算子CV_32F改那个类型应该会出错
namedWindow("gabor",WINDOW_AUTOSIZE);
createTrackbar("sigma","gabor",&sigma,50,aa);
createTrackbar("theta","gabor",&theta,10,aa);
createTrackbar("lambd","gabor",&lambd,10,aa);
createTrackbar("gammaa","gabor",&gammaa,10,aa);
createTrackbar("psi","gabor",&psi,10,aa);
waitKey(0);
}
else//下面五个数对应上面五个滑动条对应的参数,算子尺寸自己改
{
//载入图片(出现图像后点图像按键继续)
a.load_image();
namedWindow("gabor",WINDOW_AUTOSIZE);
createTrackbar("sigma","gabor",&sigma,50,bb);
createTrackbar("theta","gabor",&theta,10,bb);
createTrackbar("lambd","gabor",&lambd,10,bb);
createTrackbar("gammaa","gabor",&gammaa,10,bb);
createTrackbar("psi","gabor",&psi,10,bb);
waitKey(0);
}
return 0;
}
hpp部分
#include <stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<opencv2/highgui.hpp>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
class gabor
{
private:
Mat image_source;
public:
void load_image();
Mat flter(Mat a,Mat b);
Mat load_fliter(Size ksize, double sigma, double theta,double lambd, double gamma, double psi, int ktype);
Mat deal(Mat a);
};
Mat gabor::deal(Mat a)
{
return flter(image_source,a);
}
Mat gabor::flter(Mat a,Mat b)
{
float m[20][20];
Mat c(a.rows,a.cols,CV_32F);
for(int tag_a=0;tag_a<a.rows;tag_a++)
for(int tag_b=0;tag_b<a.cols;tag_b++)
c.at<float>(tag_a,tag_b)=0.0;
for(int tag_a=0;tag_a<a.rows;tag_a++)
for(int tag_b=0;tag_b<a.cols;tag_b++)
for(int tag_c=0;tag_c<b.rows;tag_c++)
for(int tag_d=0;tag_d<b.cols;tag_d++)
{
int x=tag_c-b.rows/2;
int y=tag_d-b.cols/2;
if((tag_a+x)>=0&&(tag_b+y)>=0&&(tag_a+x)<a.rows&&(tag_b+y)<a.cols)
for(int tag_e=0;tag_e<3;tag_e++)
c.at<float>(tag_a,tag_b)+=a.at<Vec3b>(tag_a+x,tag_b+y)[tag_e]*b.at<float>(tag_c,tag_d)/3/b.cols/b.rows;
}
return c;
}
Mat gabor::load_fliter(Size ksize, double sigma, double theta,double lambd, double gamma, double psi, int ktype)
{
//获取两个数学遍历sigma_x和sigma_y,方便后面的计算,没有特殊含义
double sigma_x = sigma;
double sigma_y = sigma/gamma;
int nstds = 3;//为后面的当ksize出现负数时计算核函数窗口大小提供参数
int xmin, xmax, ymin, ymax;
double c = cos(theta), s = sin(theta);
//当ksize的width和height没有负数时,取其一半给xmax和ymax
if( ksize.width > 0 )
xmax = ksize.width/2;
else//cvround返回最近的int(四舍五入)
xmax = cvRound(std::max(fabs(nstds*sigma_x*c), fabs(nstds*sigma_y*s)));
if( ksize.height > 0 )
ymax = ksize.height/2;
else
ymax = cvRound(std::max(fabs(nstds*sigma_x*s), fabs(nstds*sigma_y*c)));
xmin = -xmax;
ymin = -ymax;
CV_Assert( ktype == CV_32F || ktype == CV_64F );
//核函数矩阵,ymax-ymin+1和xmax-xmin+1是为保证核函数矩阵是长和宽都是奇数
Mat kernel(ymax - ymin + 1, xmax - xmin + 1, ktype);
double scale = 1;
//方便计算,可以自己带入到后面的v的计算公式中会发现Gabor滤波器的实数部分的公式
double ex = -0.5/(sigma_x*sigma_x);
double ey = -0.5/(sigma_y*sigma_y);
double cscale = CV_PI*2/lambd;
for( int y = ymin; y <= ymax; y++ )
for( int x = xmin; x <= xmax; x++ )
{
double xr = x*c + y*s;
double yr = -x*s + y*c;
//计算公式,Opencv这里获取的是Gabor滤波器的实数部分
double v = scale*exp(ex*xr*xr + ey*yr*yr)*cos(cscale*xr + psi);
if( ktype == CV_32F )
kernel.at<float>(ymax - y, xmax - x) = (float)v;
else
kernel.at<double>(ymax - y, xmax - x) = v;
}
return kernel;
};
void gabor::load_image()
{
image_source=imread("z.jpeg",1);
imshow("a",image_source);
//waitKey(0);
}
ps:
算子模式记得把cpp文件上面的col和row抬高,太小了看不到(应该好理解)
滤波模式记得调小,算子太大电脑得运行很久
如有问题欢迎留言