obartra/ssim

View on GitHub
src/matlab/fspecial.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import { Matrix } from '../types'
import { sum2d, divide2d } from '../math'

/**
 * Creates a matrix of lenght `2 * length + 1` with values being the sum of the square of the
 * distance for each component from the center. E.g:
 *
 * For a length of 5 it results in a matrix size of 11. Looking at [0, 0] (distance: [-5, -5] from
 * the center), the value at that position becomes `-5^2 + -5^2 = 50`
 *
 * @method rangeSquare2d
 * @param {Number} length - The maxium distance from the matrix center
 * @returns {Matrix} mx - The generated matrix
 * @private
 * @memberOf matlab
 * @since 0.0.2
 */
function rangeSquare2d(length: number): Matrix {
  const size = length * 2 + 1
  const data = new Array(size ** 2)

  for (let x = 0; x < size; x++) {
    for (let y = 0; y < size; y++) {
      data[x * size + y] = (x - length) ** 2 + (y - length) ** 2
    }
  }

  return {
    data,
    width: size,
    height: size,
  }
}

/**
 * Applies a gaussian filter of sigma to a given matrix
 *
 * @method gaussianFilter2d
 * @param {Matrix} A - The input matrix
 * @param {Number} σ - The sigma value
 * @returns {Matrix} B - The matrix with the gaussian filter applied
 * @private
 * @memberOf matlab
 * @since 0.0.2
 */
function gaussianFilter2d(
  { data: ref, width, height }: Matrix,
  σ: number
): Matrix {
  const data = new Array(ref.length)

  for (let x = 0; x < ref.length; x++) {
    data[x] = Math.exp(-ref[x] / (2 * σ ** 2))
  }

  return {
    data,
    width,
    height,
  }
}
/**
 * Create predefined 2-D filter
 *
 * `h = fspecial(type, parameters)` accepts the filter specified by type plus additional modifying
 * parameters particular to the type of filter chosen. If you omit these arguments, fspecial uses
 * default values for the parameters.
 *
 * This method mimics Matlab's `fspecial2` method with `type = 'gaussian'`. `hsize` cannot be a
 * vector (unlike Matlab's implementation), only a Number is accepted.
 *
 * `h = fspecial('gaussian', hsize, sigma)` returns a rotationally symmetric Gaussian lowpass filter
 * of size `hsize` with standard deviation sigma (positive). In this implementation `hsize` will
 * always be a scalar, which will result in `h` being a square matrix.
 *
 * The gaussian logic follows: hg(hsize) = e^(-2*hsize^2 / 2σ^2)
 *
 * @example
 *   fspecial('gaussian', 3, 1.5) === {
 *     data: [
 *       0.094742, 0.118318, 0.094742,
 *       0.118318, 0.147761, 0.118318,
 *       0.094742, 0.118318, 0.094742
 *     ],
 *     width: 3,
 *     height: 3
 *   };
 *
 * @method fspecial
 * @param {String} [type='gaussian'] - The type of 2D filter to create (coerced to 'gaussian')
 * @param {Number} [hsize=3] - The length of the filter
 * @param {Number} [σ=1.5] - The filter sigma value
 * @returns {Matrix} c - Returns the central part of the convolution of the same
 * size as `a`.
 * @public
 * @memberOf matlab
 * @since 0.0.2
 */
export function fspecial(_type?: 'gaussian', hsize = 3, σ = 1.5): Matrix {
  hsize = (hsize - 1) / 2

  const pos = rangeSquare2d(hsize)
  const gauss = gaussianFilter2d(pos, σ)
  const total = sum2d(gauss)

  return divide2d(gauss, total)
}