Scikit Image − Gabor Filter



Gabor filter is a type of "linear filter" with a "Gaussian kernel" that is modulated by a sinusoidal plane wave, which is used in image processing and computer vision for various tasks, including texture analysis, edge detection, and feature extraction. It is named after the physicist "Dennis Gabor", who introduced the concept.

The primary purpose of a Gabor filter is to capture features in images with varying spatial frequencies and orientations. This makes them similar to the way the human visual system processes information.

The scikit-image library provides the functions called gabor() and gabor_kernel() in the filters module to apply Gabor filters to an input image and to generate a complex 2D Gabor filter kernel with specific properties, respectively.

Using the skimage.filters.gabor() function

The filters.gabor() function is used to apply a "Gabor filter" to an input image. It applies the real and imaginary parts of the "Gabor filter kernel" and it returns a pair of arrays representing the response of the filter.

Syntax

Following is the syntax of this function −

skimage.filters.gabor(image, frequency, theta=0, bandwidth=1, sigma_x=None, sigma_y=None, n_stds=3, offset=0, mode='reflect', cval=0)

Parameters

Here are the parameters and their explanation −

  • image (2-D array): This is the input image on which the Gabor filter will be applied.
  • Frequency (float): This parameter specifies the spatial frequency of the harmonic function. It's specified in pixels.
  • Theta (float, optional): This parameter defines the orientation of the Gabor filter in radians. If set to 0, the harmonic is oriented in the x-direction.
  • Bandwidth (float, optional): The bandwidth captured by the filter. It controls the width of the Gaussian kernel. For fixed bandwidth, sigma_x and sigma_y will decrease with increasing frequency. This value is ignored if you explicitly set sigma_x and sigma_y.
  • sigma_x and sigma_y (float, optional): These parameters specify the standard deviation in the x- and y-directions of the kernel applied before rotation. If theta is set to pi/2, the kernel is rotated 90 degrees, and sigma_x controls the vertical direction.
  • N_stds (scalar, optional): This parameter defines the linear size of the kernel, which is scaled by standard deviations. The default is 3, meaning the kernel will extend to 3 standard deviations.
  • Offset (float, optional): This is the phase offset of the harmonic function in radians.
  • Mode (str, optional) This parameter specifies the mode used to convolve the image with the kernel and is passed to the convolution function. It can take the following string values {'constant', 'nearest', 'reflect', 'mirror', 'wrap'}.
  • Cval (scalar, optional) If the mode of convolution is set to 'constant', this parameter specifies the value to fill past the edges of the input image.

Return value

The function returns two arrays - real and imag. These arrays represent the filtered images using the real and imaginary parts of the Gabor filter kernel. These filtered images will have the same dimensions as the input image.

Example

The following example demonstrates how to use Gabor filters on an image using the filter.gabor() function −

from skimage.filters import gabor
from skimage import io
from matplotlib import pyplot as plt

# Load an input image 
image = io.imread('Images/tree.jpg', as_gray=True)

# Apply Gabor filter with a frequency of 0.6 
filt_real_1, filt_imag_1 = gabor(image, frequency=0.6)

# Apply Gabor filter with a frequency of 0.1
filt_real_2, filt_imag_2 = gabor(image, frequency=0.1)

# Visualize the input and real part of the filter response
fig, axes = plt.subplots(1, 3, figsize=(10, 5))
ax = axes.ravel()

# Display the input array
ax[0].imshow(image, cmap='gray') 
ax[0].set_title('Input image')  
ax[0].axis('off')

# Display the real part of the first filter response
ax[1].imshow(filt_real_1, cmap='gray')  
ax[1].set_title('Real part of the Filter Response\n (Frequency 0.6)')
ax[1].axis('off')

# Display the real part of the second filter response
ax[2].imshow(filt_real_2, cmap='gray')  
ax[2].set_title('Real part of the Filter Response\n (Frequency 0.1)')
ax[2].axis('off')

plt.tight_layout()
plt.show()

Output

Gabor Filter

Using the skimage.filters.gabor_kernel() function

The filters.gabor_kernel() function is used to generate a complex 2D Gabor filter kernel.

A "Gabor kernel" is a Gaussian kernel modulated by a complex harmonic function. The harmonic function combines an imaginary sine wave and a real cosine wave. The spatial frequency increases as the wavelength of the harmonic decreases, and it decreases as the standard deviation of a Gaussian kernel increases. Similarly, the bandwidth of the function decreases as the standard deviation of the Gaussian kernel increases.

Syntax

Following is the syntax of this function −

skimage.filters.gabor_kernel(frequency, theta=0, bandwidth=1, sigma_x=None, sigma_y=None, n_stds=3, offset=0, dtype=)

Parameters

The function accepts the following parameters −

  • frequency (float): This parameter specifies the spatial frequency of the harmonic function, specified in pixels.
  • theta (float, optional): This parameter defines the orientation of the Gabor filter in radians. If set to 0, the harmonic is oriented in the x-direction.
  • bandwidth (float, optional): This parameter captures the bandwidth controlled by the filter. If the bandwidth is fixed, sigma_x and sigma_y will decrease as the frequency increases. This parameter is ignored if sigma_x and sigma_y are set explicitly by the user.
  • sigma_x and sigma_y (float, optional): The standard deviation in the x- and y-directions. These directions are applied to the kernel before any rotation. If theta is set to pi/2, the kernel is rotated 90 degrees, with sigma_x controlling the vertical direction.
  • n_stds (scalar, optional): Specifies the linear size of the kernel, typically measured in standard deviations. The default is 3 standard deviations.
  • offset (float, optional): The phase offset of the harmonic function, specified in radians.
  • dtype ({np.complex64, np.complex128}): Specifies whether the filter should be single or double precision complex.

Return value

The function returns a complex array g, which represents the complex filter kernel.

Example

Here's an example of using the filters.gabor_kernel() function to create a complex 2D Gabor filter kernel −

from skimage.filters import gabor_kernel
import numpy as np
import matplotlib.pyplot as plt

# Define Gabor kernel parameters
frequency = 0.2  
theta = 0.0      
bandwidth = 1.0 

# Create a Gabor kernel with the specified parameters
gk = gabor_kernel(frequency=frequency, theta=theta, bandwidth=bandwidth)

# Display the real and imaginary parts of the Gabor kernel
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
ax = axes.ravel()

# Display the Real part
ax[0].imshow(np.real(gk))
ax[0].axis('off')
ax[0].set_title('Real Part of Gabor Kernel')

# Display the Imaginary part
ax[1].imshow(np.imag(gk))
ax[1].axis('off')
ax[1].set_title('Imaginary Part of Gabor Kerne')

plt.tight_layout()
plt.show()

Output

Gabor Filter

Gabor filter banks for texture classification

In the below example, we'll explore texture classification using "Gabor filter banks". The images undergo filtering using the real components of multiple Gabor filter kernels. Subsequently, the mean and variance of the resulting filtered images serve as classification features, relying on the least squared error for simplicity.

Example

Here is an example for texture classification using "Gabor filter banks" −

import matplotlib.pyplot as plt
import numpy as np
from scipy import ndimage as ndi
from skimage import data
from skimage.util import img_as_float
from skimage.filters import gabor_kernel

# Function to compute features using Gabor filter banks
def compute_feats(image, kernels):
    feats = np.zeros((len(kernels), 2), dtype=np.double)
    for k, kernel in enumerate(kernels):
        filtered = ndi.convolve(image, kernel, mode='wrap')
        feats[k, 0] = filtered.mean()
        feats[k, 1] = filtered.var()
    return feats

# Function to match features to reference features
def match(feats, ref_feats):
    min_error = np.inf
    min_i = None
    for i in range(ref_feats.shape[0]):
        error = np.sum((feats - ref_feats[i, :])**2)
        if error < min_error:
            min_error = error
            min_i = i
    return min_i

# Prepare filter bank kernels
kernels = []
for theta in range(4):
    theta = theta / 4. * np.pi
    for sigma in (1, 3):
        for frequency in (0.05, 0.25):
            kernel = np.real(gabor_kernel(frequency, theta=theta,
                                          sigma_x=sigma, sigma_y=sigma))
            kernels.append(kernel)

shrink = (slice(0, None, 3), slice(0, None, 3))
brick = img_as_float(data.brick())[shrink]
grass = img_as_float(data.grass())[shrink]
gravel = img_as_float(data.gravel())[shrink]
image_names = ('brick', 'grass', 'gravel')
images = (brick, grass, gravel)

# Prepare reference features
ref_feats = np.zeros((3, len(kernels), 2), dtype=np.double)
ref_feats[0, :, :] = compute_feats(brick, kernels)
ref_feats[1, :, :] = compute_feats(grass, kernels)
ref_feats[2, :, :] = compute_feats(gravel, kernels)

print('Rotated images matched against references using Gabor filter banks:')

# Example matches
rotate_degrees = [190, 70, 145]
for i, degree in enumerate(rotate_degrees):
    original_name = image_names[i]
    rotated_name = f'rotated: {degree}deg'
    feats = compute_feats(ndi.rotate(images[i], angle=degree, reshape=False), kernels)
    result_name = image_names[match(feats, ref_feats)]
    print(f'original: {original_name}, {rotated_name}, match result: {result_name}')

# Function to compute the power of an image using a Gabor kernel
def power(image, kernel):
    # Normalize images for better comparison.
    image = (image - image.mean()) / image.std()
    return np.sqrt(ndi.convolve(image, np.real(kernel), mode='wrap')**2 +
                   ndi.convolve(image, np.imag(kernel), mode='wrap')**2)

# Plot a selection of the filter bank kernels and their responses.
results = []
kernel_params = []

for theta in (0, 1):
    theta = theta / 4. * np.pi
    for frequency in (0.1, 0.4):
        kernel = gabor_kernel(frequency, theta=theta)
        params = f"theta={theta * 180 / np.pi},\nfrequency={frequency:.2f}"
        kernel_params.append(params)
        # Save kernel and the power image for each image
        results.append((kernel, [power(img, kernel) for img in images]))

fig, axes = plt.subplots(nrows=5, ncols=4, figsize=(5, 6))
plt.gray()

fig.suptitle('Image responses for Gabor filter kernels', fontsize=12)

axes[0][0].axis('off')

# Plot original images
for label, img, ax in zip(image_names, images, axes[0][1:]):
    ax.imshow(img)
    ax.set_title(label, fontsize=9)
    ax.axis('off')

for label, (kernel, powers), ax_row in zip(kernel_params, results, axes[1:]):
    # Plot Gabor kernel
    ax = ax_row[0]
    ax.imshow(np.real(kernel))
    ax.set_ylabel(label, fontsize=7)
    ax.set_xticks([])
    ax.set_yticks([])

    # Plot Gabor responses with the contrast normalized for each filter
    vmin = np.min(powers)
    vmax = np.max(powers)
    for patch, ax in zip(powers, ax_row[1:]):
        ax.imshow(patch, vmin=vmin, vmax=vmax)
        ax.axis('off')

plt.show()

Output

Rotated images matched against references using Gabor filter banks:
original: brick, rotated: 190deg, match result: brick
original: grass, rotated: 70deg, match result: brick
original: gravel, rotated: 145deg, match result: brick
Gabor Filter
Advertisements