简体   繁体   English

在Python中重现MATLAB的imgaborfilt

[英]Reproduce MATLAB's imgaborfilt in Python

I'm trying to reproduce the behaviour of the following MATLAB code in Python:我试图在 Python 中重现以下 MATLAB 代码的行为:

% Matlab code
wavelength = 10
orientation = 45
image = imread('filename.tif') % grayscale image
[mag,phase] = imgaborfilt(image, wavelength, orientation)
gabor_im = mag .* sin(phase)

Unfortunately, I don't have a license and cannot run the code.不幸的是,我没有许可证,无法运行代码。 Also, the official Matlab documentation of imgaborfilt does not specify precisely what the functions do.此外,imgaborfilt 的官方 Matlab 文档没有具体说明函数的作用。

For lack of an obvious alternative, I'm trying to use OpenCV in Python (open to other suggestions).由于缺乏明显的替代方案,我试图在 Python 中使用 OpenCV (接受其他建议)。 I have no experience working with OpenCV.我没有使用 OpenCV 的经验。 I'm trying to use cv2.getGaborKernel and cv2.filter2D .我正在尝试使用cv2.getGaborKernelcv2.filter2D I could not find detailed documentation of the behaviour of these functions, either.我也找不到这些函数行为的详细文档。 Afaik there is no official documentation of the Python wrapper for OpenCV. Afaik 没有 OpenCV 的 Python 包装器的官方文档。 The docstrings of the functions provide some information but it is incomplete and imprecise.函数的文档字符串提供了一些信息,但它不完整且不精确。

I found this question , where OpenCV is used in C++ to solve the problem.我发现了这个问题,其中 OpenCV 用于 C++ 来解决问题。 I assume the functions work in a very similar way (also note the official C++ documentation ).我假设这些功能以非常相似的方式工作(另请注意官方 C++ 文档)。 However, they have a number of additional parameters.但是,它们有许多附加参数。 How can I find out what the matlab functions really do to reproduce the behaviour?我怎样才能找出 matlab 函数真正用来重现行为的方法?

# python 3.6
import numpy as np
import cv2

wavelength = 10
orientation = 45
shape = (500, 400)  # arbitrary values to get running example code...
sigma = 100  # what to put for Matlab behaviour?
gamma = 1  # what to put for Matlab behaviour?
gabor_filter = cv2.getGaborKernel(shape, sigma, orientation, wavelength, gamma)
print(gabor_filter.shape)  # =(401, 501). Why flipped?

image = np.random.random(shape)  # create some random data.
out_buffer = np.zeros(shape)

destination_depth = -1  # like dtype for filter2D. Apparantly, -1="same as input".
thing = cv2.filter2D(image, destination_depth, gabor_filter, out_buffer)
print(out_buffer.shape, out_buffer.dtype, out_buffer.max())  # =(500, 400) float64 65.2..
print(thing.shape, thing.dtype, thing.max())  # =(500, 400) float64 65.2..

EDIT:编辑:

After receiving the great answer by Cris Luengo, I used it to make two functions, using OpenCV and scikit-image respectively, to (hopefully) reproduce MATLAB imgaborfit function behaviour.在收到 Cris Luengo 的精彩回答后,我用它制作了两个函数,分别使用 OpenCV 和 scikit-image 来(希望)重现 MATLAB imgaborfit ZC1C425268E18385D1ABZA504 行为。 I include them here.我把它们包括在这里。 Note that the scikit implementation is a lot slower than OpenCV.请注意,scikit 实现比 OpenCV 慢很多。

I still have further questions about these functions:我对这些功能还有其他疑问:

  • To what precision do the results of the OpenCV solution and the MATLAB solution agree? OpenCV 解决方案和 MATLAB 解决方案的结果在什么精度上一致?
  • For people not wanting to use OpenCV, I also include a scikit-image solution here.对于不想使用 OpenCV 的人,我还在这里提供了一个 scikit-image 解决方案。 I found parameters, such that the magnitudes are almost equal.我找到了参数,使得大小几乎相等。 However, it seems the phase of the scikit-image solution is different from the OpenCV solution.但是,scikit-image 解决方案的阶段似乎与 OpenCV 解决方案不同。 Why is this?为什么是这样?
import numpy as np
import math
import cv2

def gaborfilt_OpenCV_likeMATLAB(image, wavelength, orientation, SpatialFrequencyBandwidth=1, SpatialAspectRatio=0.5):
    """Reproduces (to what accuracy in what MATLAB version??? todo TEST THIS!) the behaviour of MATLAB imgaborfilt function using OpenCV."""

    orientation = -orientation / 180 * math.pi # for OpenCV need radian, and runs in opposite direction
    sigma = 0.5 * wavelength * SpatialFrequencyBandwidth
    gamma = SpatialAspectRatio
    shape = 1 + 2 * math.ceil(4 * sigma)  # smaller cutoff is possible for speed
    shape = (shape, shape)
    gabor_filter_real = cv2.getGaborKernel(shape, sigma, orientation, wavelength, gamma, psi=0)
    gabor_filter_imag = cv2.getGaborKernel(shape, sigma, orientation, wavelength, gamma, psi=math.pi / 2)
    filtered_image = cv2.filter2D(image, -1, gabor_filter_real) + 1j * cv2.filter2D(image, -1, gabor_filter_imag)
    mag = np.abs(filtered_image)
    phase = np.angle(filtered_image)
    return mag, phase
import numpy as np
import math
from skimage.filters import gabor

def gaborfilt_skimage_likeMATLAB(image, wavelength, orientation, SpatialFrequencyBandwidth=1, SpatialAspectRatio=0.5):
    """TODO (does not quite) reproduce the behaviour of MATLAB imgaborfilt function using skimage."""
    sigma = 0.5 * wavelength * SpatialFrequencyBandwidth
    filtered_image_re, filtered_image_im = gabor(
        image, frequency=1 / wavelength, theta=-orientation / 180 * math.pi,
        sigma_x=sigma, sigma_y=sigma/SpatialAspectRatio, n_stds=5,
    )
    full_image = filtered_image_re + 1j * filtered_image_im
    mag = np.abs(full_image)
    phase = np.angle(full_image)
    return mag, phase

Code to test above functions:测试上述功能的代码:

from matplotlib import pyplot as plt
import numpy as np

def show(im, title=""):
    plt.figure()
    plt.imshow(im)
    plt.title(f"{title}: dtype={im.dtype}, shape={im.shape},\n max={im.max():.3e}, min= {im.min():.3e}")
    plt.colorbar()

image = np.zeros((400, 400))
image[200, 200] = 1  # a delta impulse image to visualize the filtering kernel
wavelength = 10
orientation = 33  # in degrees (for MATLAB)

mag_cv, phase_cv = gaborfilt_OpenCV_likeMATLAB(image, wavelength, orientation)
show(mag_cv, "mag")  # normalized by maximum, non-zero noise even outside filter window region
show(phase_cv, "phase")  # all over the place

mag_sk, phase_sk = gaborfilt_skimage_likeMATLAB(image, wavelength, orientation)
show(mag_sk, "mag skimage")  # small values, zero outside filter region
show(phase_sk, "phase skimage")  # and hence non-zero only inside filter window region

show(mag_cv - mag_sk/mag_sk.max(), "cv - normalized(sk)")  # approximately zero-image.
show(phase_sk - phase_cv, "phase_sk - phase_cv") # phases do not agree at all! Not even in the window region!
plt.show()

The documentation for both MATLAB's imgaborfilt and OpenCV's getGaborKernel are almost complete enough to be able to do a 1:1 translation. MATLAB 的imgaborfilt和 OpenCV 的getGaborKernel的文档几乎都足够完整,可以进行 1:1 的翻译。 Only a little bit of experimentation is needed to figure out how to translate MATLAB's " SpatialFrequencyBandwidth " to a sigma for the Gaussian envelope.只需进行一点实验即可弄清楚如何将 MATLAB 的“ SpatialFrequencyBandwidth ”转换为高斯包络的 sigma。

One thing that I've noticed here is that OpenCV's implementation of Gabor filtering seems to indicate that Gabor filters are not well understood.我在这里注意到的一件事是 OpenCV 的 Gabor 过滤器实现似乎表明 Gabor 过滤器还没有被很好地理解。 A quick Google exercise demonstrates that the most popular tutorials for Gabor filtering in OpenCV do not properly understand Gabor filters.一个快速的 Google 练习表明 OpenCV 中最流行的 Gabor 过滤教程不能正确理解 Gabor 过滤器。

The Gabor filter, as can be learned for example from the same Wikipedia page that OpenCV's documentation links to, is a complex-valued filter. Gabor 过滤器,例如可以从 OpenCV 的文档链接到的同一个Wikipedia 页面中了解到,是一个复值过滤器。 The result of applying it to an image is therefore also complex-valued.因此,将其应用于图像的结果也是复值。 MATLAB correctly returns the magnitude and phase of the complex result, rather than the complex-valued image itself, since it is mostly the magnitude that is of interest. MATLAB 正确返回复数结果的幅度和相位,而不是复数值图像本身,因为它主要是感兴趣的幅度。 The magnitude of the Gabor filter indicates which parts of an image have frequencies of the given wavelength and orientation. Gabor 滤波器的大小指示图像的哪些部分具有给定波长和方向的频率。

For example, one can apply a Gabor filter to this image (left) to yield this result (right) (this is the magnitude of the complex-valued output):例如,可以将 Gabor 滤波器应用于该图像(左)以产生该结果(右)(这是复值输出的幅度):

Gabor 滤波器的图像和结果

However, OpenCV's filtering seems to be strictly real-valued.然而,OpenCV 的过滤似乎是严格实值的。 It is possible to build a real-valued component of the Gabor filter kernel with an arbitrary phase.可以构建具有任意相位的 Gabor 滤波器 kernel 的实值分量。 Gabor's filter has a real component with 0 phase and an imaginary component with π/2 phase (that is, the real component is even and the imaginary component is odd). Gabor 滤波器有一个相位为 0 的实分量和一个相位为 π/2 的虚分量(即实分量为偶数,虚分量为奇数)。 Combining the even and the odd filters allows one to analyze a signal with arbitrary phase, creating filters with other phases is unnecessary.结合偶数和奇数滤波器可以分析具有任意相位的信号,无需创建具有其他相位的滤波器。


To replicate the following MATLAB code:要复制以下 MATLAB 代码:

image = zeros(64,64); 
image(33,33) = 1;     % a delta impulse image to visualize the filtering kernel

wavelength = 10;
orientation = 30; # in degrees
[mag,phase] = imgaborfilt(image, wavelength, orientation);
% defaults: 'SpatialFrequencyBandwidth'=1; 'SpatialAspectRatio'=0.5

in Python with OpenCV one would need to do:在 Python 和 OpenCV 中,需要做:

import cv2
import numpy as np
import math

image = np.zeros((64, 64))
image[32, 32] = 1          # a delta impulse image to visualize the filtering kernel

wavelength = 10
orientation = -30 / 180 * math.pi    # in radian, and seems to run in opposite direction
sigma = 0.5 * wavelength * 1         # 1 == SpatialFrequencyBandwidth
gamma = 0.5                          # SpatialAspectRatio
shape = 1 + 2 * math.ceil(4 * sigma) # smaller cutoff is possible for speed
shape = (shape, shape)
gabor_filter_real = cv2.getGaborKernel(shape, sigma, orientation, wavelength, gamma, psi=0)
gabor_filter_imag = cv2.getGaborKernel(shape, sigma, orientation, wavelength, gamma, psi=math.pi/2)

gabor = cv2.filter2D(image, -1, gabor_filter_real) + 1j * cv2.filter2D(image, -1, gabor_filter_imag)
mag = np.abs(gabor)
phase = np.angle(gabor)

Do note that it is important for the input image to be of a floating-point type, otherwise the computation result will be cast to a type that cannot represent all the values needed to represent the result of the Gabor filter.请注意,输入图像为浮点类型很重要,否则计算结果将被转换为无法表示表示 Gabor 滤波器结果所需的所有值的类型。


The last line of the code in the OP is OP 中代码的最后一行是

gabor_im = mag .* sin(phase)

This is, to me, very strange and I wonder what this code was used for.对我来说,这很奇怪,我想知道这段代码是用来做什么的。 What it accomplishes is obtaining the result of the imaginary component of the Gabor filter:它完成的是获得 Gabor 滤波器的虚部的结果:

gabor_im = cv2.filter2D(image, -1, gabor_filter_imag)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM