我的问题不是如何使用高斯拉普拉斯过滤图像(基本上使用带有相关内核的filter2D等)。
我想知道的是我如何生成 NxN内核。
我将举例说明如何在openCV中生成[Winsize x WinSize]高斯内核。
在Matlab中:
gaussianKernel = fspecial('gaussian', WinSize, sigma);
在openCV中:
cv::Mat gaussianKernel = cv::getGaussianKernel(WinSize, sigma, CV_64F);
cv::mulTransposed(gaussianKernel,gaussianKernel,false);
预定义sigma和WinSize。
我想为高斯拉普拉斯算法做同样的事。
在Matlab中:
LoGKernel = fspecial('log', WinSize, sigma);
如何在openCV中获得确切的内核(精确到几乎可以忽略不计的数值差异)?
我正在开发一个特定的应用程序,在那里我需要实际的内核值,只是通过近似高斯的差异找到另一种实现LoG过滤的方法并不是我所追求的。
谢谢!
答案 0 :(得分:4)
您可以使用公式
手动生成它LoG(x,y)=(1 /(pi * sigma ^ 4))*(1 - (x ^ 2 + y ^ 2)/(sigma ^ 2))*(e ^( - (x ^ 2 + y ^ 2)/ 2sigma ^ 2)
http://homepages.inf.ed.ac.uk/rbf/HIPR2/log.htm
cv::Mat kernel(WinSize,WinSize,CV_64F);
int rows = kernel.rows;
int cols = kernel.cols;
double halfSize = (double) WinSize / 2.0;
for (size_t i=0; i<rows;i++)
for (size_t j=0; j<cols;j++)
{
double x = (double)j - halfSize;
double y = (double)i - halfSize;
kernel.at<double>(j,i) = (1.0 /(M_PI*pow(sigma,4))) * (1 - (x*x+y*y)/(sigma*sigma))* (pow(2.718281828, - (x*x + y*y) / 2*sigma*sigma));
}
如果上面的函数不正常,你可以简单地重写fspecial的matlab版本:
case 'log' % Laplacian of Gaussian
% first calculate Gaussian
siz = (p2-1)/2;
std2 = p3^2;
[x,y] = meshgrid(-siz(2):siz(2),-siz(1):siz(1));
arg = -(x.*x + y.*y)/(2*std2);
h = exp(arg);
h(h<eps*max(h(:))) = 0;
sumh = sum(h(:));
if sumh ~= 0,
h = h/sumh;
end;
% now calculate Laplacian
h1 = h.*(x.*x + y.*y - 2*std2)/(std2^2);
h = h1 - sum(h1(:))/prod(p2); % make the filter sum to zero
答案 1 :(得分:1)
我要感谢老不明飞行物在正确的方向上推动我。 我希望我不必通过快速的matlab重新发明轮子 - &gt; openCV转换,但我猜这是我用于快速解决方案的最佳解决方案。
注意 - 我这样做仅用于方形内核(否则很容易修改,但我没有必要这样......)。 也许这可以用更优雅的形式写成,但是我做的很快,所以我可以继续处理更紧迫的事情。
来自主要功能:
int WinSize(7); int sigma(1); // can be changed to other odd-sized WinSize and different sigma values
cv::Mat h = fspecialLoG(WinSize,sigma);
实际功能是:
// return NxN (square kernel) of Laplacian of Gaussian as is returned by Matlab's: fspecial(Winsize,sigma)
cv::Mat fspecialLoG(int WinSize, double sigma){
// I wrote this only for square kernels as I have no need for kernels that aren't square
cv::Mat xx (WinSize,WinSize,CV_64F);
for (int i=0;i<WinSize;i++){
for (int j=0;j<WinSize;j++){
xx.at<double>(j,i) = (i-(WinSize-1)/2)*(i-(WinSize-1)/2);
}
}
cv::Mat yy;
cv::transpose(xx,yy);
cv::Mat arg = -(xx+yy)/(2*pow(sigma,2));
cv::Mat h (WinSize,WinSize,CV_64F);
for (int i=0;i<WinSize;i++){
for (int j=0;j<WinSize;j++){
h.at<double>(j,i) = pow(exp(1),(arg.at<double>(j,i)));
}
}
double minimalVal, maximalVal;
minMaxLoc(h, &minimalVal, &maximalVal);
cv::Mat tempMask = (h>DBL_EPSILON*maximalVal)/255;
tempMask.convertTo(tempMask,h.type());
cv::multiply(tempMask,h,h);
if (cv::sum(h)[0]!=0){h=h/cv::sum(h)[0];}
cv::Mat h1 = (xx+yy-2*(pow(sigma,2))/(pow(sigma,4));
cv::multiply(h,h1,h1);
h = h1 - cv::sum(h1)[0]/(WinSize*WinSize);
return h;
}
答案 2 :(得分:0)
您的函数和matlab版本之间存在一些差异: http://br1.einfach.org/tmp/log-matlab-vs-opencv.png。
上面是matlab fspecial('log', 31, 6)
,下面是具有相同参数的函数的结果。不知何故,帽子更加弯曲&#39; - 这是打算在以后的处理中产生什么影响?
我可以创建一个与具有这些函数的matlab非常类似的内核,它直接反映了LoG公式:
float LoG(int x, int y, float sigma) {
float xy = (pow(x, 2) + pow(y, 2)) / (2 * pow(sigma, 2));
return -1.0 / (M_PI * pow(sigma, 4)) * (1.0 - xy) * exp(-xy);
}
static Mat LOGkernel(int size, float sigma) {
Mat kernel(size, size, CV_32F);
int halfsize = size / 2;
for (int x = -halfsize; x <= halfsize; ++x) {
for (int y = -halfsize; y <= halfsize; ++y) {
kernel.at<float>(x+halfsize,y+halfsize) = LoG(x, y, sigma);
}
}
return kernel;
}
答案 3 :(得分:0)
这是一个NumPy版本,直接从MATLAB中的fspecial
函数翻译过来。
import numpy as np
import sys
def get_log_kernel(siz, std):
x = y = np.linspace(-siz, siz, 2*siz+1)
x, y = np.meshgrid(x, y)
arg = -(x**2 + y**2) / (2*std**2)
h = np.exp(arg)
h[h < sys.float_info.epsilon * h.max()] = 0
h = h/h.sum() if h.sum() != 0 else h
h1 = h*(x**2 + y**2 - 2*std**2) / (std**4)
return h1 - h1.mean()
答案 4 :(得分:0)
下面的代码与fspecial('log', p2, p3)
完全等效:
def fspecial_log(p2, std):
siz = int((p2-1)/2)
x = y = np.linspace(-siz, siz, 2*siz+1)
x, y = np.meshgrid(x, y)
arg = -(x**2 + y**2) / (2*std**2)
h = np.exp(arg)
h[h < sys.float_info.epsilon * h.max()] = 0
h = h/h.sum() if h.sum() != 0 else h
h1 = h*(x**2 + y**2 - 2*std**2) / (std**4)
return h1 - h1.mean()
答案 5 :(得分:0)
我知道这个话题很老,但是关于CV_User的答案,我有MATLAB实现:
sigma=3;
kernelSize=3;
lin = round(linspace(-floor(kernelSize/2),floor(kernelSize/2),kernelSize));
[X,Y] = meshgrid(lin,lin);
hg = exp(-(X.^2 + Y.^2)/(2*(sigma^2)));
kernel_t = hg.*(X.^2 + Y.^2-2*sigma^2)/(sigma^4*sum(hg(:)));
% make the filter sum to zero
kernel = kernel_t - sum(kernel_t(:))/kernelSize^2;
提供一个完全不同的结果,只有当sigma为1时,它才是相同的结果,才应该检查实现。
通过这种方式输入错字:
cv::Mat h1 = (xx+yy-2*(pow(sigma,2))/(pow(sigma,4)); and extra parenthesis is required
答案 6 :(得分:0)
我在OpenCV中编写了Matlab fspecial
函数的确切实现
功能:
Mat C_fspecial_LOG(double* kernel_size,double sigma)
{
double size[2]={ (kernel_size[0]-1)/2 , (kernel_size[1]-1)/2};
double std = sigma;
const double eps = 2.2204e-16;
cv::Mat kernel(kernel_size[0],kernel_size[1],CV_64FC1,0.0);
int row=0,col=0;
for (double y = -size[0]; y <= size[0]; ++y,++row)
{
col=0;
for (double x = -size[1]; x <= size[1]; ++x,++col)
{
kernel.at<double>(row,col)=exp( -( pow(x,2) + pow(y,2) ) /(2*pow(std,2)));
}
}
double MaxValue;
cv::minMaxLoc(kernel,nullptr,&MaxValue,nullptr,nullptr);
Mat condition=~(kernel < eps*MaxValue)/255;
condition.convertTo(condition,CV_64FC1);
kernel = kernel.mul(condition);
cv::Scalar SUM = cv::sum(kernel);
if(SUM[0]!=0)
{
kernel /= SUM[0];
}
return kernel;
}
此功能的用法:
double kernel_size[2] = {4,4}; // kernel size set to 4x4
double sigma = 2.1;
Mat kernel = C_fspecial_LOG(kernel_size,sigma);
将OpenCV结果与Matlab进行比较:
opencv结果:
[0.04918466596701741, 0.06170341496034986, 0.06170341496034986, 0.04918466596701741;
0.06170341496034986, 0.07740850411228289, 0.07740850411228289, 0.06170341496034986;
0.06170341496034986, 0.07740850411228289, 0.07740850411228289, 0.06170341496034986;
0.04918466596701741, 0.06170341496034986, 0.06170341496034986, 0.04918466596701741]
fspecial('gaussian', 4, 2.1)
的Matlab结果:
0.0492 0.0617 0.0617 0.0492
0.0617 0.0774 0.0774 0.0617
0.0617 0.0774 0.0774 0.0617
0.0492 0.0617 0.0617 0.0492