Filtering Model tutorial

This tutorial is a part of Model module guide. Here, we explore how you can use the FilteringModel wrapper to use your Python or Matlab filtering functions in the benchmark.

First, being on the project’s root, you need to import the necessary modules,

import numpy as np
import matlab.engine
import matplotlib.pyplot as plt

from functools import partial
from OpenDenoising import data
from OpenDenoising import model

eng = matlab.engine.start_matlab()

The following function will be used throughout this tutorial to display denoising results,

def display_results(clean_imgs, noisy_imgs, rest_images, name):
    """Display denoising results."""
    fig, axes = plt.subplots(5, 3, figsize=(15, 15))

    plt.suptitle("Denoising results using {}".format(name))

    for i in range(5):
        axes[i, 0].imshow(np.squeeze(clean_imgs[i]), cmap="gray")
        axes[i, 0].axis("off")
        axes[i, 0].set_title("Ground-Truth")

        axes[i, 1].imshow(np.squeeze(noisy_imgs[i]), cmap="gray")
        axes[i, 1].axis("off")
        axes[i, 1].set_title("Noised Image")

        axes[i, 2].imshow(np.squeeze(rest_imgs[i]), cmap="gray")
        axes[i, 2].axis("off")
        axes[i, 2].set_title("Restored Images")

Moreover, you may download the data we will use by using the following function,

data.download_BSDS_grayscale(output_dir="./tmp/BSDS500/")

The models will be evaluated using the BSDS dataset,

# Validation images generator
valid_generator = data.DatasetFactory.create(path="./tmp/BSDS500/Valid",
                                             batch_size=8,
                                             n_channels=1,
                                             noise_config={data.utils.gaussian_noise: [25]},
                                             name="BSDS_Valid")

To define a filtering model, you need either a Python or Matlab function that performs the denoising.

Using a Python-based function for Image Denoising.

Python-based functions should agree with the following convention:

def my_filter(image, optional_arguments):
    # Perform the filter algorithm
    return restored_image

where image is a 4D numpy ndarray, and restored_image is a 4D numpy ndarray with same shape as image. As an example, consider the median_filter implemented on model.filtering,

def median_filter(image, filter_size=(3, 3), stride=(1, 1)):
    """Naive implementation of median filter using numpy.

    Parameters
    ----------
    image : :class:`numpy.ndarray`
        4D batch of noised images. It has shape: (batch_size, height, width, channels).
    filter_size : list
        2D list containing the size of filter's kernel.
    stride : list
        2D list containing the horizontal and vertical strides.

    Returns
    -------
    output : :class:`numpy.ndarray`
        4D batch of denoised images. It has shape: (batch_size, height, width, channels).
    """
    if image.ndim == 2:
        image = np.expand_dims(np.expand_dims(image, axis=0), axis=-1)
    p = filter_size[0] // 2
    sh, sw = stride

    _image = np.pad(image, pad_width=((0, 0), (p, p), (p, p), (0, 0)), mode="constant")
    N, h, w, c = _image.shape
    output = np.zeros(image.shape)

    for i in range(p, h - p, sh):
        # Loops over horizontal axis
        for j in range(p, w - p, sw):
            # Loops over vertical axis
            window = _image[:, i - p: i + p, j - p: j + p, :]
            output[:, i - p, j - p, :] = np.median(window, axis=(1, 2))

    return output

To charge the model in a Filtering Model, you can use,

Using a Matlab-based function for Image Denoising.

You can also use Matlab functions to perform image denoising. To do so, you need to wrap your Matlab function with a Python function. As an example, consider the BM3D matlab’s implementation. You can download the author’s code and add it to Matlab’s “pathdef.m” file. As an example of function wrapping matlab’s code, consider the following:

def BM3D(z, sigma=25.0, profile="np", channels_first=False):
    """This function wraps MATLAB's BM3D implementation, available on matlab_libs/BM3Dlib. The original code is
    available to the public through the author's page, on http://www.cs.tut.fi/~foi/GCF-BM3D/

    Parameters
    ----------
    z : :class:`numpy.ndarray`
        4D batch of noised images. It has shape: (batch_size, height, width, channels).
    sigma : float
        Level of gaussian noise.
    profile : str
        One between {'np', 'lc', 'high', 'vn', 'vn_old'}. Algorithm's profile.

        Available for grayscale:

        * 'np': Normal profile.
        * 'lc': Fast profile.
        * 'high': High quality profile.
        * 'vn': High noise profile (sigma > 40.0)
        * 'vn_old': old 'vn' profile. Yields inferior results than 'vn'.

        Available for RGB:

        * 'np': Normal profile.
        * 'lc': Fast profile.

    Returns
    -------
    y_est : :class:`numpy.ndarray`
        4D batch of denoised images. It has shape: (batch_size, height, width, channels).
    """
    _z = z.copy()
    rgb = True if _z.shape[-1] == 3 else False
    if rgb:
        assert (profile in ["np", "lc", "high", "vn", "vn_old"]), "Expected profile to be 'np', 'lc', 'high', 'vn' " \
                                                                  "or 'vn_old' but got {}.".format(profile)
    else:
        assert (profile in ["np", "lc"]), "Expected profile to be 'np', 'lc' bug got {}".format(profile)


    # Convert input arrays to matlab
    m_sigma = matlab.double([sigma])
    m_show = matlab.int64([0])

    # Call BM3D function on matlab
    y_est = []
    for i in range(len(_z)):
        m_z = matlab.double(_z[i, :, :, :].tolist())
        if rgb:
            _, y_est = eng.CBM3D(m_z, m_z, m_sigma, profile, m_show, nargout=2)
        else:
            _, y = eng.BM3D(m_z, m_z, m_sigma, profile, m_show, nargout=2)
        y_est.append(np.asarray(y))

    y_est = np.asarray(y_est).reshape(z.shape)
    return y_est

Here, we make a few remarks,

  • You need your engine to be defined in order to call the Matlab function.
  • Inside the Python’s matlab module, you have a series of classes making the bridge between Python and Matlab variables. Moreover, Python does not handle so well operations between these classes (for instance, the addition between two matlab.double variables). A way to get around this, is to do all operations inside of Matlab’s functions. Therefore, your Python wrapper function should prepare the arguments to be passed to your Matlab function, followed by a call “eng.my_matlab_function”.
  • If your matlab function returns more than one argument, you need to specify the “nargout” parameter (even if it is not present in your Matlab code).
  • After Matlab done its computation, it outputs “Matlab arrays”. These can be converted into numpy arrays by calling “numpy.asarray” function.
../../_images/output_13_0.png ../../_images/output_14_0.png

Providing your own Matlab functions

Consider one more example of how you can include Matlab functions into your FilteringModels. We define a script for a function called “kernel_filter” in the folder “./Examples/Jupyter Notebooks/Additional Files”. The Matlab function has the following implementation:

function y_est = kernel_filter(z, kernel)

k = size(kernel);
k = floor(k(1) / 2);
ndims = length(size(z));
y_est = zeros(size(z));

if ndims == 2
    z_ = padarray(z, [k, k], 0, 'both');
    [N, h, w, c] = size(z_);
    for i=k+1:h
        for j=k+1:w
            window = sum(z_(i-k:i+k, j-k:j+k) .* kernel, 'all');
            y_est(n, i - k, j - k) = window;
        end
    end
else
    z_ = padarray(z, [k, k, 0], 0, 'both');
    [N, h, w, c] = size(z_);
    kernel = repmat(kernel, [1, 1, 3]);
    for i=k+1:h-k
        for j=k+1:w-k
            window = sum(sum(z_(i-k:i+k, j-k:j+k, :) .* kernel));
            y_est(i - k, j - k, :) = window;
        end
    end
end

As we can see, this function denoises one single image at time, so the 4D-in 4D-out convention needs to be handled in the Python wrapper function. Then, you can verify that the function is indeed on your Matlab’s path by executing:

/home/efernand/repos/Summer_Internship_2019/Code/examples/Jupyter Notebooks/Additional Files/kernel_filter.m
../../_images/output_19_0.png