Source code for dippykit.transforms

"""Module of various image transform functions

This module contains an assortment of functions that perform transform
operations on images in various useful manners.

"""

# This library was developed for the Georgia Tech graduate course ECE 6258:
# Digital Image Processing with Professor Ghassan AlRegib.
# For comments and feedback, please email dippykit[at]gmail.com

# Functional imports
import numpy as np
from scipy import fftpack
from skimage.feature import canny
from skimage.filters import sobel, sobel_h, sobel_v, scharr, prewitt, \
    roberts, laplace

__author__ = 'Brighton Ancelin, Motaz Alfarraj, Ghassan AlRegib'

__all__ = ['dct_2d', 'idct_2d', 'edge_detect']


[docs]def dct_2d( im: np.ndarray, ) -> np.ndarray: """Computes the Discrete Cosine Transform of an image Given an input image, this function computes its discrete cosine transform. This result is identical to the result of the dct2() Matlab function. Internally, this function is computed using ``scipy.fftpack.dctn(im, norm='ortho')``. This function is essentially a wrapper for `scipy.fftpack.dctn`_, so more detailed documentation may be found there. :type im: ``numpy.ndarray`` :param im: An image to be processed. :rtype: ``numpy.ndarray`` :return: The discrete cosine transform of the input image. .. note:: This function wraps around functions from other packages. Reading these functions' documentations may be useful. See the **See also** section for more information. .. seealso:: `scipy.fftpack.dctn`_ Documentation of the dctn function from Scipy .. _scipy.fftpack.dctn: https://docs.scipy.org/doc/scipy/reference /generated/scipy.fftpack.dctn.html Examples: >>> import numpy as np >>> freq_1 = 1/4 >>> freq_2 = 1/8 >>> domain_1, domain_2 = np.meshgrid(np.arange(8), np.arange(8)) >>> domain_1 array([[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7]]) >>> domain_2 array([[0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1], [2, 2, 2, 2, 2, 2, 2, 2], [3, 3, 3, 3, 3, 3, 3, 3], [4, 4, 4, 4, 4, 4, 4, 4], [5, 5, 5, 5, 5, 5, 5, 5], [6, 6, 6, 6, 6, 6, 6, 6], [7, 7, 7, 7, 7, 7, 7, 7]]) >>> im_1 = 10*np.cos(2 * np.pi * freq_1 * (domain_1 + 0.5)) >>> im_2 = 10*np.cos(2 * np.pi * freq_2 * (domain_2 + 0.5)) >>> im = im_1 + im_2 >>> np.set_printoptions(precision=2, suppress=True) >>> im array([[ 16.31, 2.17, 2.17, 16.31, 16.31, 2.17, 2.17, 16.31], [ 10.9 , -3.24, -3.24, 10.9 , 10.9 , -3.24, -3.24, 10.9 ], [ 3.24, -10.9 , -10.9 , 3.24, 3.24, -10.9 , -10.9 , 3.24], [ -2.17, -16.31, -16.31, -2.17, -2.17, -16.31, -16.31, -2.17], [ -2.17, -16.31, -16.31, -2.17, -2.17, -16.31, -16.31, -2.17], [ 3.24, -10.9 , -10.9 , 3.24, 3.24, -10.9 , -10.9 , 3.24], [ 10.9 , -3.24, -3.24, 10.9 , 10.9 , -3.24, -3.24, 10.9 ], [ 16.31, 2.17, 2.17, 16.31, 16.31, 2.17, 2.17, 16.31]]) >>> dct_2d(im) array([[-0. , 0. , -0. , 0. , 56.57, 0. , -0. , 0. ], [ 0. , 0. , 0. , -0. , -0. , 0. , -0. , -0. ], [56.57, 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [-0. , 0. , -0. , -0. , 0. , 0. , 0. , -0. ], [-0. , 0. , -0. , 0. , -0. , 0. , -0. , 0. ], [ 0. , 0. , 0. , -0. , 0. , 0. , -0. , -0. ], [-0. , 0. , -0. , 0. , 0. , 0. , -0. , -0. ], [ 0. , 0. , 0. , -0. , -0. , 0. , -0. , -0. ]]) """ return fftpack.dctn(im, norm='ortho')
[docs]def idct_2d( im: np.ndarray, ) -> np.ndarray: """Computes the Inverse Discrete Cosine Transform of an image Given an input image, this function computes its inverse discrete cosine transform. This result is identical to the result of the idct2() Matlab function. Internally, this function is computed using ``scipy.fftpack.idctn(im, norm='ortho')``. This function is essentially a wrapper for `scipy.fftpack.idctn`_, so more detailed documentation may be found there. :type im: ``numpy.ndarray`` :param im: An image to be processed. :rtype: ``numpy.ndarray`` :return: The inverse discrete cosine transform of the input image. .. note:: This function wraps around functions from other packages. Reading these functions' documentations may be useful. See the **See also** section for more information. .. seealso:: `scipy.fftpack.idctn`_ Documentation of the dctn function from Scipy .. _scipy.fftpack.idctn: https://docs.scipy.org/doc/scipy/reference /generated/scipy.fftpack.idctn.html Examples: >>> import numpy as np >>> im_DCT = np.zeros((8, 8)) >>> im_DCT[0, 2] = 1 >>> np.set_printoptions(precision=2, suppress=True) >>> idct_2d(im_DCT) array([[ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16], [ 0.16, 0.07, -0.07, -0.16, -0.16, -0.07, 0.07, 0.16]]) """ return fftpack.idctn(im, norm='ortho')
[docs]def edge_detect( im: np.ndarray, mode: str='sobel', as_bool: bool=False, **kwargs ) -> np.ndarray: """Performs an edge detection operation on an image Given an input image and an edge detection mode, this function applies an edge detection operation to the image and returns the result. Optionally, this result can be returned as a binary/boolean image. This function is essentially a wrapper for several external functions, so more detailed documentation may be found there. :type im: ``numpy.ndarray`` :param im: The input image. :type mode: ``str`` :param mode: The edge detection mode/algorithm to be used. Acceptable values are: 'sobel', 'sobel_h', 'sobel_v', 'canny', 'scharr', 'prewitt', 'roberts', and 'laplace'. :type as_bool: ``bool`` :param as_bool: (default=False) If set to True, this function will return the result as a binary/boolean image. This binary image is built by taking the original edge image and substituting any values below *threshold* as False, and all other values as True. The value of *threshold* is defined as the 95th percentile of values in the original edge image. :param kwargs: The keyword arguments to be passed to the edge detecting functions. If mode='canny', and there is no keyword argument 'sigma', then this function will set the 'sigma' keyword argument to the square root of 2 by default. :rtype: ``numpy.ndarray`` :return: The edge image. .. note:: This function wraps around functions from other packages. Reading these functions' documentations may be useful. See the **See also** section for more information. .. seealso:: `skimage.feature.canny`_ Documentation of the canny function from Scikit Image `skimage.filters.sobel`_ Documentation of the sobel function from Scikit Image `skimage.filters.sobel_h`_ Documentation of the sobel_h function from Scikit Image `skimage.filters.sobel_v`_ Documentation of the sobel_v function from Scikit Image `skimage.filters.scharr`_ Documentation of the scharr function from Scikit Image `skimage.filters.prewitt`_ Documentation of the prewitt function from Scikit Image `skimage.filters.roberts`_ Documentation of the roberts function from Scikit Image `skimage.filters.laplace`_ Documentation of the laplace function from Scikit Image .. _skimage.feature.canny: http://scikit-image.org/docs/dev/api /skimage.feature.html#skimage.feature.canny .. _skimage.filters.sobel: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.sobel .. _skimage.filters.sobel_h: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.sobel_h .. _skimage.filters.sobel_v: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.sobel_v .. _skimage.filters.scharr: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.scharr .. _skimage.filters.prewitt: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.prewitt .. _skimage.filters.roberts: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.roberts .. _skimage.filters.laplace: http://scikit-image.org/docs/dev/api /skimage.filters.html#skimage.filters.laplace Examples: >>> import numpy as np >>> im = np.array(array([[0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], ... [0. , 0. , 0. , 0.5, 0.5, 0. , 0. , 0. ], ... [0. , 0. , 1. , 1. , 1. , 1. , 0. , 0. ], ... [0. , 0.5, 1. , 1. , 1. , 1. , 0. , 0. ], ... [0. , 0.5, 1. , 1. , 1. , 1. , 0. , 0. ], ... [0. , 0. , 1. , 1. , 1. , 1. , 0. , 0. ], ... [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], ... [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) >>> np.round(edge_detect(im), 2) array([[0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [0. , 0.25, 0.64, 0.73, 0.73, 0.64, 0.25, 0. ], [0. , 0.64, 0.75, 0.45, 0.45, 0.76, 0.56, 0. ], [0. , 0.73, 0.45, 0. , 0. , 0.71, 0.71, 0. ], [0. , 0.73, 0.45, 0. , 0. , 0.71, 0.71, 0. ], [0. , 0.64, 0.76, 0.71, 0.71, 0.75, 0.56, 0. ], [0. , 0.25, 0.56, 0.71, 0.71, 0.56, 0.25, 0. ], [0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) >>> edge_detect(im, as_bool=True) array([[False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False], [False, False, True, False, False, True, False, False], [False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False], [False, False, True, False, False, True, False, False], [False, False, False, False, False, False, False, False], [False, False, False, False, False, False, False, False]]) >>> np.round(dip.edge_detect(im, 'sobel_h'), 2) array([[ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 0.25, 0.75, 1. , 1. , 0.75, 0.25, 0. ], [ 0. , 0.5 , 0.75, 0.62, 0.62, 0.62, 0.25, 0. ], [ 0. , 0.25, 0.12, 0. , 0. , 0. , 0. , 0. ], [ 0. , -0.25, -0.12, 0. , 0. , 0. , 0. , 0. ], [ 0. , -0.5 , -0.88, -1. , -1. , -0.75, -0.25, 0. ], [ 0. , -0.25, -0.75, -1. , -1. , -0.75, -0.25, 0. ], [ 0. , 0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) """ if 'sobel' == mode: edges = sobel(im, **kwargs) elif 'sobel_h' == mode: edges = sobel_h(im, **kwargs) elif 'sobel_v' == mode: edges = sobel_v(im, **kwargs) elif 'canny' == mode: if 'sigma' not in kwargs: kwargs['sigma'] = np.sqrt(2) edges = canny(im, **kwargs) elif 'scharr' == mode: edges = scharr(im, **kwargs) elif 'prewitt' == mode: edges = prewitt(im, **kwargs) elif 'roberts' == mode: edges = roberts(im, **kwargs) elif 'laplace' == mode: edges = laplace(im, **kwargs) else: raise ValueError("Unrecognized mode '{}'".format(mode)) if as_bool: if edges.dtype.kind == 'b': return edges else: sorted_edges_ravel = np.sort(edges.ravel()) # Set threshold to 95th percentile threshold = sorted_edges_ravel[(edges.size * 19) // 20] return np.where(edges < threshold, False, True) return edges