diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 46cb6f0c..fd9f8725 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -1,6 +1,7 @@ import numpy as np import collections + def integral_image(img): """Integral image / summed area table. @@ -40,24 +41,19 @@ def integrate(ii, start, end, *args): ---------- ii : ndarray Integral image. - start : tuple of length equal to dimension of `ii` + start : List of tuples, each tuple of length equal to dimension of `ii` Coordinates of top left corner of window(s). - For multiple windows start may be a tuple of lists, each list - containing the starting row, col, ... index i.e - `([row_win1, row_win2, ...], [col_win1, col_win2,...], ...)`, - This convention mirrors the NumPy multi-indexing convention. - end : tuple of length equal to dimension of `ii` + Each tuple in the list contains the starting row, col, ... index + i.e `[(row_win1, col_win1, ...), (row_win2, col_win2,...), ...]`. + end : List of tuples, each tuple of length equal to dimension of `ii` Coordinates of bottom right corner of window(s). - For multiple windows end may be a tuple of lists, each list - containing the end row, col, ... index i.e - ([row_win1, row_win2, ...], [col_win1, col_win2, ...], ...) - This convention mirrors the NumPy multi-indexing convention. + Each tuple in the list containing the end row, col, ... index i.e + `[(row_win1, col_win1, ...), (row_win2, col_win2, ...), ...]`. args: optional - For backward compatibility with versions prior to 0.10. + For backward compatibility with versions prior to 0.11. The earlier function signature was `integrate(ii, r0, c0, r1, c1)`, where `r0`, `c0` are int(lists) specifying start coordinates of window(s) to be integrated and `r1`, `c1` the end coordinates. - Returns ------- @@ -69,20 +65,20 @@ def integrate(ii, start, end, *args): -------- >>> arr = np.ones((5, 6), dtype=np.float) >>> ii = integral_image(arr) - >>> integrate(ii, (1, 0), (1, 2)) # sum from (1,0) -> (1,2) + >>> integrate(ii, [(1, 0)], [(1, 2)]) # sum from (1,0) -> (1,2) array([ 3.]) - >>> integrate(ii, (3, 3), (4, 5)) # sum form (3,3) -> (4,5) + >>> integrate(ii, [(3, 3)], [(4, 5)]) # sum form (3,3) -> (4,5) array([ 6.]) - >>> integrate(ii, ([1, 3], [0, 3]), ([1, 4], [2, 5])) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) + >>> integrate(ii, [(1, 0), (3, 3)], [(1, 2), (4, 5)]) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) array([ 3., 6.]) """ rows = 1 # handle input from new input format if len(args) == 0: - if isinstance(start[0], collections.Iterable): - rows = len(start[0]) - start = np.array(start).T - end = np.array(end).T + if isinstance(start, collections.Iterable): + rows = len(start) + start = np.array(start) + end = np.array(end) # handle deprecated input format else: if isinstance(start, collections.Iterable): @@ -91,59 +87,58 @@ def integrate(ii, start, end, *args): start = np.array(args[:int(len(args)/2)]).T end = np.array(args[int(len(args)/2):]).T - total_shape = ii.shape total_shape = np.tile(total_shape, [rows, 1]) # convert negative indices into equivalent positive indices - start_negatives = start < 0 + start_negatives = start < 0 end_negatives = end < 0 start = (start + total_shape) * start_negatives + \ - start * ~(start_negatives) + start * ~(start_negatives) end = (end + total_shape) * end_negatives + \ - end * ~(end_negatives) + end * ~(end_negatives) - if np.any((end - start) < 0) : + if np.any((end - start) < 0): raise IndexError('end coordinates must be greater or equal to start') # bit_perm is the total number of terms in the expression - # of S. For example, in the case of a 4x4 2D image + # of S. For example, in the case of a 4x4 2D image # sum of image from (1,1) to (2,2) is given by # S = + ii[2, 2] # - ii[0, 2] - ii[2, 0] # + ii[0, 0] # The total terms = 4 = 2 ** 2(dims) - + S = np.zeros(rows) bit_perm = 2 ** ii.ndim width = len(bin(bit_perm - 1)[2:]) - + # Sum of a (hyper)cube, from an integral image is computed using # values at the corners of the cube. The corners of cube are # selected using binary numbers as described in the following example. # In a 3D cube there are 8 corners. The corners are selected using - # binary numbers 000 to 111. Each number is called a permutation, where - # perm(000) means, select end corner where none of the coordinates + # binary numbers 000 to 111. Each number is called a permutation, where + # perm(000) means, select end corner where none of the coordinates # is replaced, i.e ii[end_row, end_col, end_depth]. Similarly, perm(001) - # means replace last coordinate by start - 1, i.e + # means replace last coordinate by start - 1, i.e # ii[end_row, end_col, start_depth - 1], and so on. - # Sign of even permutations is positive, while those of odd is negative. - # If 'start_coord - 1' is -ve it is labeled bad and not considered in + # Sign of even permutations is positive, while those of odd is negative. + # If 'start_coord - 1' is -ve it is labeled bad and not considered in # the final sum. for i in range(bit_perm): # for all permutations # boolean permutation array eg [True, False] for '10' binary = bin(i)[2:].zfill(width) bool_mask = [bit == '1' for bit in binary] - + sign = (-1)**sum(bool_mask) # determine sign of permutation - + bad = [np.any(((start[r] - 1) * bool_mask) < 0) for r in range(rows)] # find out bad start rows corner_points = (end * (np.invert(bool_mask))) + \ ((start - 1) * bool_mask) # find corner for each row - - S += [sign * ii[tuple(corner_points[r])] if(bad[r] == False) else 0 + + S += [sign * ii[tuple(corner_points[r])] if(not bad[r]) else 0 for r in range(rows)] # add only good rows return S diff --git a/skimage/transform/tests/test_integral.py b/skimage/transform/tests/test_integral.py index 1383238b..32038cce 100644 --- a/skimage/transform/tests/test_integral.py +++ b/skimage/transform/tests/test_integral.py @@ -40,8 +40,10 @@ def test_vectorized_integrate(): x[0,0], x[10, 10], x[30:, 31:].sum()]) + start_pts = [(r0[i], c0[i]) for i in range(len(r0))] + end_pts = [(r1[i], c1[i]) for i in range(len(r0))] assert_equal(expected, integrate(s, r0, c0, r1, c1)) # test deprecated - assert_equal(expected, integrate(s, (r0, c0), (r1, c1))) + assert_equal(expected, integrate(s, start_pts, end_pts)) if __name__ == '__main__':