一种更快的高斯拉普拉斯算子方法

3
我正在优化我的代码,以使图像处理更加高效。我的第一个问题是由于vision.VideoFileReaderstep导致每个帧的打开时间很长。我通过将灰度图像压缩成1个RGB帧中的3个帧来加速代码。这样,我可以使用vid.step()加载1个RGB帧,并准备好处理3个帧。
现在,我的代码在拉普拉斯高斯(LoG)滤波上运行缓慢。我了解到可以使用imfilter函数执行LoG,但它似乎是下一个限制速率的步骤。
进一步阅读后,发现imfilter不是速度最快的选项。显然,MATLAB引入了LoG function,但它是在R2016b中引入的,而我不幸地使用的是R2016a。
是否有一种方法可以加快imfilter的速度,或者有更好的函数用于执行LoG滤波?

我应该调用Python来加速这个过程吗?

enter image description here

代码:

Hei = gh.Video.reader.info.VideoSize(2);
Wid = gh.Video.reader.info.VideoSize(1);

Log_filter = fspecial('log', filterdot, thresh); % fspecial creat predefined filter.Return a filter.
    % 25X25 Gaussian filter with SD =25 is created.

tic
ii = 1;

bkgd = zeros(Hei,Wid,3);
bkgd(:,:,1) = gh.Bkgd;
bkgd(:,:,2) = gh.Bkgd;
bkgd(:,:,3) = gh.Bkgd;

bkgdmod = reshape(bkgd,720,[]);

while ~isDone(gh.Video.reader)
    frame = gh.readFrame();
    img_temp = double(frame);

    img_temp2 = reshape(img_temp,720,[]);
    subbk = img_temp2 - bkgdmod;

    img_LOG = imfilter(subbk, Log_filter, 'symmetric', 'conv');

    img_LOG =  imbinarize(img_LOG,.002);
    [~, centroids, ~] = gh.Video.blobAnalyser.step(img_LOG);

    toc
end
1个回答

5

高斯拉普拉斯算子不能直接分解为两个一维核。因此,imfilter 将进行完整卷积,这是非常昂贵的。但我们可以手动将其分解为更简单的滤波器。


高斯拉普拉斯算子定义为高斯函数的两个二阶导数之和:

LoG = d²/dx² G + d²/dy² G

高斯函数及其导数是可分离的。因此,上述内容可以使用4个一维卷积计算,这比单个二维卷积便宜得多,除非内核非常小(例如,如果内核为7x7,则对于2D内核,每个像素需要49次乘法和加法,对于4个1D内核,每个像素需要4*7=28次乘法和加法;随着内核变大,这种差异会增加)。计算如下:
sigma = 3;
cutoff = ceil(3*sigma);
G = fspecial('gaussian',[1,2*cutoff+1],sigma);
d2G = G .* ((-cutoff:cutoff).^2 - sigma^2)/ (sigma^4);
dxx = conv2(d2G,G,img,'same');
dyy = conv2(G,d2G,img,'same');
LoG = dxx + dyy;

如果你真的时间很紧,且不在意精度,你可以将截止值设置为2*sigma(适用于较小的内核),但这并不理想。


另一种不太精确的替代方法是以不同方式分离操作:

LoG * f = ( d²/dx² G + d²/dy² G ) * f
        = ( d²/dx²  * G + d²/dy²  * G ) * f
        = ( d²/dx^2  + d²/dy²  ) * G * f

在这里,*代表卷积运算,Dirac Delta函数则是卷积运算中乘以1的等价物。而在离散领域中并不存在d²/dx² + d²/dy²算子,但可以采用有限差分逼近方法,从而得到著名的3x3 Laplace核:

[ 1  1  1             [ 0  1  0
  1 -8  1       or:     1 -4  1
  1  1  1 ]             0  1  0 ]

现在我们得到了一个更加粗略的近似,但计算速度更快(2个一维卷积和一个带有简单3x3内核的卷积)。
sigma = 3;
cutoff = ceil(3*sigma);
G = fspecial('gaussian',[1,2*cutoff+1],sigma);
tmp = conv2(G,G,img,'same');
h = fspecial('laplacian',0);
LoG = conv2(tmp,h,'same'); % or use imfilter

感谢您解释拉普拉斯高斯和计算它们的不同方法。在尝试了这些方法后,我发现imfilter似乎仍然是我的代码中最快的方法。话虽如此,我认为我没有在我的代码中积极地计算LoG,这就是为什么它更快的原因。我将尝试使用gpuArray来加速处理速度。虽然上次我这样做时,最终减慢了处理速度。:D 我会看看的。再次感谢。 - Hojo.Timberwolf
2
@Hojo.Timberwolf GPU需要相当长的时间来发送内存并从GPU中收集它。如果您可以堆叠几个图像,将它们发送到GPU以一起处理并收集结果,您将获得速度提升。 - Ander Biguri
1
@Hojo:我不知道您在filterdot, thresh中使用的是什么值,但评论显示您使用了sigma=25的25x25滤波器。这确实不是高斯滤波器!事实上,它更接近于均匀滤波器的特征。而且,均匀滤波器( fspecial('average')计算速度非常快。因此,如果您对自己的滤波效果满意,请尝试将其替换为均匀滤波器。 - Cris Luengo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接