From 53f489dc51f447f7feb709f1f0769625612f1e72 Mon Sep 17 00:00:00 2001 From: James Booth Date: Wed, 1 Aug 2018 10:14:35 +0200 Subject: [PATCH] remove cython gradient implementation --- menpo/feature/.gitignore | 1 - menpo/feature/_gradient.pyx | 33 ------------------ menpo/feature/features.py | 52 ++++++++++------------------- menpo/feature/test/test_gradient.py | 21 ------------ 4 files changed, 18 insertions(+), 89 deletions(-) delete mode 100644 menpo/feature/_gradient.pyx diff --git a/menpo/feature/.gitignore b/menpo/feature/.gitignore index ff7454c8e..4c6be2399 100644 --- a/menpo/feature/.gitignore +++ b/menpo/feature/.gitignore @@ -1,2 +1 @@ windowiterator.cpp -_gradient.cpp diff --git a/menpo/feature/_gradient.pyx b/menpo/feature/_gradient.pyx deleted file mode 100644 index 80463401a..000000000 --- a/menpo/feature/_gradient.pyx +++ /dev/null @@ -1,33 +0,0 @@ -import numpy as np -cimport numpy as np -cimport cython - - -ctypedef fused DOUBLE_TYPES: - float - double - - -cdef extern from "cpp/central_difference.h": - void central_difference[T](const T* input, const Py_ssize_t rows, - const Py_ssize_t cols, const Py_ssize_t n_channels, - T* output) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef gradient_cython(np.ndarray[DOUBLE_TYPES, ndim=3] input): - - cdef Py_ssize_t n_channels = input.shape[0] - cdef Py_ssize_t rows = input.shape[1] - cdef Py_ssize_t cols = input.shape[2] - # Maintain the dtype that was passed in (float or double) - dtype = input.dtype - cdef np.ndarray[DOUBLE_TYPES, ndim=3] output = np.zeros((n_channels * 2, - rows, cols), - dtype=dtype) - - central_difference(&input[0,0,0], rows, cols, n_channels, - &output[0,0,0]) - - return output diff --git a/menpo/feature/features.py b/menpo/feature/features.py index 063fd233a..25aaf9c7d 100644 --- a/menpo/feature/features.py +++ b/menpo/feature/features.py @@ -5,39 +5,9 @@ scipy_gaussian_filter = None # expensive from .base import ndfeature, winitfeature, imgfeature -from ._gradient import gradient_cython from .windowiterator import WindowIterator, WindowIteratorResult -def _np_gradient(pixels): - """ - This method is used in the case of multi-channel images (not 2D images). - The output ordering is identical to the gradient() method, returning - a 2 * n_channels image with gradients in order of the first axis derivative - over all the channels, then the second etc. For example, in the case of - a 3D image with 2 channels, the ordering would be: - I[:, 0, 0, 0] = [A_0, B_0, A_1, B_1, A_2, B_2] - where A and B are the 'channel' labels (synonymous with RGB for a colour - image) and 0,1,2 are the axis labels (synonymous with y,x for a 2D image). - """ - n_dims = pixels.ndim - 1 - grad_per_dim_per_channel = [np.gradient(g, edge_order=1) - for g in pixels] - # Flatten out the separate dims - grad_per_channel = list(itertools.chain.from_iterable( - grad_per_dim_per_channel)) - # Add a channel axis for broadcasting - grad_per_channel = [g[None, ...] for g in grad_per_channel] - - # Permute the list so it is first axis, second axis, etc - grad_per_channel = [grad_per_channel[i::n_dims] - for i in range(n_dims)] - grad_per_channel = list(itertools.chain.from_iterable(grad_per_channel)) - - # Concatenate gradient list into an array (the new_image) - return np.concatenate(grad_per_channel, axis=0) - - @ndfeature def gradient(pixels): r""" @@ -69,10 +39,24 @@ def gradient(pixels): all the ``y``-gradients are returned over each channel, then all the ``x``-gradients. """ - if (pixels.ndim - 1) == 2: # 2D Image - return gradient_cython(pixels) - else: - return _np_gradient(pixels) + if pixels.dtype == np.uint8: + raise TypeError("Attempting to take the gradient on a uint8 image.") + n_dims = pixels.ndim - 1 + grad_per_dim_per_channel = [np.gradient(g, edge_order=1) + for g in pixels] + # Flatten out the separate dims + grad_per_channel = list(itertools.chain.from_iterable( + grad_per_dim_per_channel)) + # Add a channel axis for broadcasting + grad_per_channel = [g[None, ...] for g in grad_per_channel] + + # Permute the list so it is first axis, second axis, etc + grad_per_channel = [grad_per_channel[i::n_dims] + for i in range(n_dims)] + grad_per_channel = list(itertools.chain.from_iterable(grad_per_channel)) + + # Concatenate gradient list into an array (the new_image) + return np.concatenate(grad_per_channel, axis=0) @ndfeature diff --git a/menpo/feature/test/test_gradient.py b/menpo/feature/test/test_gradient.py index 45416619f..185941c46 100644 --- a/menpo/feature/test/test_gradient.py +++ b/menpo/feature/test/test_gradient.py @@ -4,7 +4,6 @@ from menpo.image import Image from menpo.feature import gradient -from menpo.feature.features import _np_gradient import menpo.io as mio @@ -26,17 +25,6 @@ def test_gradient_float(): assert_allclose(grad_image.pixels[1], np_grad[1]) -def test_gradient_takeo_float32(): - dtype = np.float32 - t = takeo.copy() - t.pixels = t.pixels.astype(dtype) - grad_image = gradient(t) - _check_assertions(grad_image, t.shape, t.n_channels * 2, - dtype) - np_grad = _np_gradient(t.pixels) - assert_allclose(grad_image.pixels, np_grad) - - def test_gradient_double(): dtype = np.float64 p = example_image.astype(dtype) @@ -49,15 +37,6 @@ def test_gradient_double(): assert_allclose(grad_image.pixels[1], np_grad[1]) -def test_gradient_takeo_double(): - t = takeo.copy() - t.pixels = t.pixels.astype(np.float64) - grad_image = gradient(t) - - np_grad = _np_gradient(t.pixels) - assert_allclose(grad_image.pixels, np_grad) - - def test_gradient_uint8_exception(): image = Image(example_image.astype(np.uint8)) with raises(TypeError):