簡體   English   中英

在Python中重現MATLAB的imgaborfilt

[英]Reproduce MATLAB's imgaborfilt 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)

不幸的是,我沒有許可證,無法運行代碼。 此外,imgaborfilt 的官方 Matlab 文檔沒有具體說明函數的作用。

由於缺乏明顯的替代方案,我試圖在 Python 中使用 OpenCV (接受其他建議)。 我沒有使用 OpenCV 的經驗。 我正在嘗試使用cv2.getGaborKernelcv2.filter2D 我也找不到這些函數行為的詳細文檔。 Afaik 沒有 OpenCV 的 Python 包裝器的官方文檔。 函數的文檔字符串提供了一些信息,但它不完整且不精確。

我發現了這個問題,其中 OpenCV 用於 C++ 來解決問題。 我假設這些功能以非常相似的方式工作(另請注意官方 C++ 文檔)。 但是,它們有許多附加參數。 我怎樣才能找出 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..

編輯:

在收到 Cris Luengo 的精彩回答后,我用它制作了兩個函數,分別使用 OpenCV 和 scikit-image 來(希望)重現 MATLAB imgaborfit ZC1C425268E18385D1ABZA504 行為。 我把它們包括在這里。 請注意,scikit 實現比 OpenCV 慢很多。

我對這些功能還有其他疑問:

  • OpenCV 解決方案和 MATLAB 解決方案的結果在什么精度上一致?
  • 對於不想使用 OpenCV 的人,我還在這里提供了一個 scikit-image 解決方案。 我找到了參數,使得大小幾乎相等。 但是,scikit-image 解決方案的階段似乎與 OpenCV 解決方案不同。 為什么是這樣?
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

測試上述功能的代碼:

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()

MATLAB 的imgaborfilt和 OpenCV 的getGaborKernel的文檔幾乎都足夠完整,可以進行 1:1 的翻譯。 只需進行一點實驗即可弄清楚如何將 MATLAB 的“ SpatialFrequencyBandwidth ”轉換為高斯包絡的 sigma。

我在這里注意到的一件事是 OpenCV 的 Gabor 過濾器實現似乎表明 Gabor 過濾器還沒有被很好地理解。 一個快速的 Google 練習表明 OpenCV 中最流行的 Gabor 過濾教程不能正確理解 Gabor 過濾器。

Gabor 過濾器,例如可以從 OpenCV 的文檔鏈接到的同一個Wikipedia 頁面中了解到,是一個復值過濾器。 因此,將其應用於圖像的結果也是復值。 MATLAB 正確返回復數結果的幅度和相位,而不是復數值圖像本身,因為它主要是感興趣的幅度。 Gabor 濾波器的大小指示圖像的哪些部分具有給定波長和方向的頻率。

例如,可以將 Gabor 濾波器應用於該圖像(左)以產生該結果(右)(這是復值輸出的幅度):

Gabor 濾波器的圖像和結果

然而,OpenCV 的過濾似乎是嚴格實值的。 可以構建具有任意相位的 Gabor 濾波器 kernel 的實值分量。 Gabor 濾波器有一個相位為 0 的實分量和一個相位為 π/2 的虛分量(即實分量為偶數,虛分量為奇數)。 結合偶數和奇數濾波器可以分析具有任意相位的信號,無需創建具有其他相位的濾波器。


要復制以下 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

在 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)

請注意,輸入圖像為浮點類型很重要,否則計算結果將被轉換為無法表示表示 Gabor 濾波器結果所需的所有值的類型。


OP 中代碼的最后一行是

gabor_im = mag .* sin(phase)

對我來說,這很奇怪,我想知道這段代碼是用來做什么的。 它完成的是獲得 Gabor 濾波器的虛部的結果:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM