From 0b7b225660b952d9c39ce1c88959f130c38d0ca6 Mon Sep 17 00:00:00 2001 From: Tabish Date: Sat, 22 Mar 2014 22:02:01 +0530 Subject: [PATCH 01/11] Modify 'integral_image' to support nD --- skimage/transform/integral.py | 72 ++++++++++++++++-------- skimage/transform/tests/test_integral.py | 27 ++------- 2 files changed, 53 insertions(+), 46 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 3503a652..c58ca9ea 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -18,8 +18,8 @@ def integral_image(x): Returns ------- - S : ndarray - Integral image / summed area table. + S : scalar value + summed area table. References ---------- @@ -27,44 +27,66 @@ def integral_image(x): ACM SIGGRAPH Computer Graphics, vol. 18, 1984, pp. 207-212. """ - return x.cumsum(1).cumsum(0) + dim = len(x.shape) + S = x + for i in range(dim): + S = S.cumsum(i) + return S -def integrate(ii, r0, c0, r1, c1): +def integrate(ii, start, end): """Use an integral image to integrate over a given window. Parameters ---------- ii : ndarray Integral image. - r0, c0 : int or ndarray - Top-left corner(s) of block to be summed. - r1, c1 : int or ndarray - Bottom-right corner(s) of block to be summed. + start : int or ndarray or list + Top-left corner of block to be summed. + end : int or ndarray or list + Bottom-right corner of block to be summed. Returns ------- - S : scalar or ndarray - Integral (sum) over the given window(s). - + S : scalar + Integral (sum) over the given window. + Notes + ----- + Explination: + For a 2D array say(10 x 10) intergral from start=(2,3) to end=(5,6) is + #replace 'zero' elements from end -> permutation('00') + +Intgral_array[5,6] + #replace 'one' elements from end by 'start coorinate - 1' -> permutation('10','01') + -(Integral_array[5,(3 - 1)] + integral_array[(2 - 1), 6]) + #replace 'two' elements from end by 'start coordinate - 1' -> permutation('11') + +(Integral_array[(2-1),(3-1)]) """ - if np.isscalar(r0): - r0, c0, r1, c1 = [np.asarray([x]) for x in (r0, c0, r1, c1)] + #make sure start and end both are arrays + start = np.asarray(start) + end = np.asarray(end) - S = np.zeros(r0.shape, ii.dtype) + if(np.any(start < 0) or np.any(end < 0)): + raise IndexError('cordinates must be non negative') - S += ii[r1, c1] + if(np.any((end - start) < 0)): + raise IndexError('end coordinates must be greater or equal to start') - good = (r0 >= 1) & (c0 >= 1) - S[good] += ii[r0[good] - 1, c0[good] - 1] + dim = len(ii.shape) #No. of dimensions of input nd-array + S = 0 + bit_perm = 2**dim #bit_perm is the total number of elements in expression of S + width = len(bin(bit_perm-1)[2:]) - good = r0 >= 1 - S[good] -= ii[r0[good] - 1, c1[good]] - - good = c0 >= 1 - S[good] -= ii[r1[good], c0[good] - 1] - - if S.size == 1: - return np.asscalar(S) + for i in range(bit_perm): #for all permutations + #generate boolean array corresponding to permutation 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 - 1)*bool_mask) < 0) + if(bad): + continue + corner_point = (end * (np.invert(bool_mask))) + ((start - 1) * bool_mask) + + S += sign*ii[tuple(corner_point)] return S diff --git a/skimage/transform/tests/test_integral.py b/skimage/transform/tests/test_integral.py index 4a641e71..307728f4 100644 --- a/skimage/transform/tests/test_integral.py +++ b/skimage/transform/tests/test_integral.py @@ -16,30 +16,15 @@ def test_validity(): def test_basic(): - assert_equal(x[12:24, 10:20].sum(), integrate(s, 12, 10, 23, 19)) - assert_equal(x[:20, :20].sum(), integrate(s, 0, 0, 19, 19)) - assert_equal(x[:20, 10:20].sum(), integrate(s, 0, 10, 19, 19)) - assert_equal(x[10:20, :20].sum(), integrate(s, 10, 0, 19, 19)) + assert_equal(x[12:24, 10:20].sum(), integrate(s, [12, 10], [23, 19])) + assert_equal(x[:20, :20].sum(), integrate(s, [0, 0], [19, 19])) + assert_equal(x[:20, 10:20].sum(), integrate(s, [0, 10], [19, 19])) + assert_equal(x[10:20, :20].sum(), integrate(s, [10, 0], [19, 19])) def test_single(): - assert_equal(x[0, 0], integrate(s, 0, 0, 0, 0)) - assert_equal(x[10, 10], integrate(s, 10, 10, 10, 10)) - -def test_vectorized_integrate(): - r0 = np.array([12, 0, 0, 10, 0, 10, 30]) - c0 = np.array([10, 0, 10, 0, 0, 10, 31]) - r1 = np.array([23, 19, 19, 19, 0, 10, 49]) - c1 = np.array([19, 19, 19, 19, 0, 10, 49]) - - expected = np.array([x[12:24, 10:20].sum(), - x[:20, :20].sum(), - x[:20, 10:20].sum(), - x[10:20, :20].sum(), - x[0,0], - x[10, 10], - x[30:, 31:].sum()]) - assert_equal(expected, integrate(s, r0, c0, r1, c1)) + assert_equal(x[0, 0], integrate(s, [0, 0], [0, 0])) + assert_equal(x[10, 10], integrate(s, [10, 10], [10, 10])) if __name__ == '__main__': From bd442bd8160c9360de26f6c88d5e22c83f5817aa Mon Sep 17 00:00:00 2001 From: Tabish Date: Sun, 23 Mar 2014 15:46:09 +0530 Subject: [PATCH 02/11] Make the nD integral_image backward compatible --- skimage/transform/integral.py | 103 ++++++++++++++--------- skimage/transform/tests/test_integral.py | 26 ++++-- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index c58ca9ea..a922d403 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -1,7 +1,7 @@ import numpy as np -def integral_image(x): +def integral_image(img): """Integral image / summed area table. The integral image contains the sum of all elements above and to the @@ -13,13 +13,13 @@ def integral_image(x): Parameters ---------- - x : ndarray + img : ndarray Input image. Returns ------- - S : scalar value - summed area table. + S : ndarray + Integral image/summed area table of same shape as input image. References ---------- @@ -27,66 +27,87 @@ def integral_image(x): ACM SIGGRAPH Computer Graphics, vol. 18, 1984, pp. 207-212. """ - dim = len(x.shape) - S = x - for i in range(dim): - S = S.cumsum(i) + S = img + for i in range(img.ndim): + S = S.cumsum(axis=i) return S -def integrate(ii, start, end): +def integrate(ii, *args): """Use an integral image to integrate over a given window. Parameters ---------- ii : ndarray Integral image. - start : int or ndarray or list - Top-left corner of block to be summed. - end : int or ndarray or list - Bottom-right corner of block to be summed. + args: lists of start and end coordinates + (see example for detailed explanation) Returns ------- - S : scalar - Integral (sum) over the given window. + S : scalar or ndarray + Integral (sum) over the given window(s). Notes ----- - Explination: - For a 2D array say(10 x 10) intergral from start=(2,3) to end=(5,6) is - #replace 'zero' elements from end -> permutation('00') - +Intgral_array[5,6] - #replace 'one' elements from end by 'start coorinate - 1' -> permutation('10','01') - -(Integral_array[5,(3 - 1)] + integral_array[(2 - 1), 6]) - #replace 'two' elements from end by 'start coordinate - 1' -> permutation('11') - +(Integral_array[(2-1),(3-1)]) + Example + >>arr = np.asarray([[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1]]) + >>print arr + [[1 1 1 1 1 1] + [1 1 1 1 1 1] + [1 1 1 1 1 1] + [1 1 1 1 1 1] + [1 1 1 1 1 1]] + >>ii = integral_image(arr); + >>print integrate(ii,1,0,1,2) #sum from (1,0) -> (1,2) + [ 3.] + >>print integrate(ii,3,3,4,5) #sum form (3,3) -> (4,5) + [ 6.] + >>print integrate(ii,[1,3],[0,3],[1,4],[2,5]) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) + [3. 6.] """ - #make sure start and end both are arrays - start = np.asarray(start) - end = np.asarray(end) + assert(len(args) == 2 * ii.ndim) + start = args[0] + end = args[ii.ndim] + for i in range(1, ii.ndim): + start = np.vstack((start, args[i])) + end = np.vstack((end, args[i + ii.ndim])) + + # each row of start/end is a starting/ending point + start = start.T + end = end.T + + rows = start.shape[0] + image_shape = ii.shape + total_shape = image_shape + # take care of negative coordinates + for i in range(1, rows): + total_shape = np.vstack((total_shape, image_shape)) + + start_negatives = start < 0 + end_negatives = end < 0 + start = (start + total_shape) * start_negatives + start * np.invert(start_negatives) + end = (end + total_shape) * end_negatives + end * np.invert(end_negatives) - if(np.any(start < 0) or np.any(end < 0)): - raise IndexError('cordinates must be non negative') if(np.any((end - start) < 0)): raise IndexError('end coordinates must be greater or equal to start') - dim = len(ii.shape) #No. of dimensions of input nd-array - S = 0 - bit_perm = 2**dim #bit_perm is the total number of elements in expression of S - width = len(bin(bit_perm-1)[2:]) - for i in range(bit_perm): #for all permutations - #generate boolean array corresponding to permutation eg [True, False] for '10' + S = np.zeros(rows) + bit_perm = 2**(ii.ndim) # bit_perm is the total number of elements in expression of S + width = len(bin(bit_perm - 1)[2:]) + + for i in range(bit_perm): # for all permutations + # generate boolean array corresponding to permutation 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 - 1)*bool_mask) < 0) - if(bad): - continue - - corner_point = (end * (np.invert(bool_mask))) + ((start - 1) * bool_mask) + sign = (-1)**sum(bool_mask) # determine sign of permutation - S += sign*ii[tuple(corner_point)] + 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 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 307728f4..51e52773 100644 --- a/skimage/transform/tests/test_integral.py +++ b/skimage/transform/tests/test_integral.py @@ -16,16 +16,30 @@ def test_validity(): def test_basic(): - assert_equal(x[12:24, 10:20].sum(), integrate(s, [12, 10], [23, 19])) - assert_equal(x[:20, :20].sum(), integrate(s, [0, 0], [19, 19])) - assert_equal(x[:20, 10:20].sum(), integrate(s, [0, 10], [19, 19])) - assert_equal(x[10:20, :20].sum(), integrate(s, [10, 0], [19, 19])) + assert_equal(x[12:24, 10:20].sum(), integrate(s, 12, 10, 23, 19)) + assert_equal(x[:20, :20].sum(), integrate(s, 0, 0, 19, 19)) + assert_equal(x[:20, 10:20].sum(), integrate(s, 0, 10, 19, 19)) + assert_equal(x[10:20, :20].sum(), integrate(s, 10, 0, 19, 19)) def test_single(): - assert_equal(x[0, 0], integrate(s, [0, 0], [0, 0])) - assert_equal(x[10, 10], integrate(s, [10, 10], [10, 10])) + assert_equal(x[0, 0], integrate(s, 0, 0, 0, 0)) + assert_equal(x[10, 10], integrate(s, 10, 10, 10, 10)) +def test_vectorized_integrate(): + r0 = np.array([12, 0, 0, 10, 0, 10, 30]) + c0 = np.array([10, 0, 10, 0, 0, 10, 31]) + r1 = np.array([23, 19, 19, 19, 0, 10, 49]) + c1 = np.array([19, 19, 19, 19, 0, 10, 49]) + + expected = np.array([x[12:24, 10:20].sum(), + x[:20, :20].sum(), + x[:20, 10:20].sum(), + x[10:20, :20].sum(), + x[0,0], + x[10, 10], + x[30:, 31:].sum()]) + assert_equal(expected, integrate(s, r0, c0, r1, c1)) if __name__ == '__main__': from numpy.testing import run_module_suite From 083212b2010d169a13102e02076e1383706dbd61 Mon Sep 17 00:00:00 2001 From: Tabish Date: Sat, 29 Mar 2014 15:58:28 +0530 Subject: [PATCH 03/11] Changed integral_image parameters to new format --- skimage/transform/integral.py | 58 ++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index a922d403..9dda2f89 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -33,15 +33,23 @@ def integral_image(img): return S -def integrate(ii, *args): +def integrate(ii, begining, ending, *args): """Use an integral image to integrate over a given window. Parameters ---------- ii : ndarray Integral image. - args: lists of start and end coordinates - (see example for detailed explanation) + begining : A tuple + Coordinates of top left corner of window + For multiple windows each coordinate should be a list + ending : A tuple + Coordinates of bottom right corner of window + For multiple windows each coordinate should be a list + args: optional + for backward compatibility + used when each coordinate is specified in a seperate list + Returns ------- @@ -50,27 +58,33 @@ def integrate(ii, *args): Notes ----- Example - >>arr = np.asarray([[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1],[1,1,1,1,1,1]]) - >>print arr - [[1 1 1 1 1 1] - [1 1 1 1 1 1] - [1 1 1 1 1 1] - [1 1 1 1 1 1] - [1 1 1 1 1 1]] - >>ii = integral_image(arr); - >>print integrate(ii,1,0,1,2) #sum from (1,0) -> (1,2) + >>> arr = np.ones((5, 6), dtype=np.float) + >>> ii = integral_image(arr) + >>> print(integrate(ii,(1, 0), (1, 2))) # sum from (1,0) -> (1,2) [ 3.] - >>print integrate(ii,3,3,4,5) #sum form (3,3) -> (4,5) + >>> print(integrate(ii,(3, 3), (4, 5))) # sum form (3,3) -> (4,5) [ 6.] - >>print integrate(ii,[1,3],[0,3],[1,4],[2,5]) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) - [3. 6.] + >>> print(integrate(ii,([1, 3], [0, 3]), ([1, 4], [2, 5]))) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) + [3. 6.] + >>> print(integrate(ii, [1, 3], [0, 3], [1, 4], [2, 5])) # deprecated usage + [3. 6.] """ - assert(len(args) == 2 * ii.ndim) - start = args[0] - end = args[ii.ndim] - for i in range(1, ii.ndim): - start = np.vstack((start, args[i])) - end = np.vstack((end, args[i + ii.ndim])) + # handle new input format + if(len(args) == 0): + start = begining[0] + end = ending[0] + for i in range(1, ii.ndim): + start = np.vstack((start, begining[i])) + end = np.vstack((end, ending[i])) + + # handle deprecated input format + else: + args = (begining, ending) + args + start = args[0] + end = args[ii.ndim] + for i in range(1, ii.ndim): + start = np.vstack((start, args[i])) + end = np.vstack((end, args[i + ii.ndim])) # each row of start/end is a starting/ending point start = start.T @@ -79,6 +93,7 @@ def integrate(ii, *args): rows = start.shape[0] image_shape = ii.shape total_shape = image_shape + # take care of negative coordinates for i in range(1, rows): total_shape = np.vstack((total_shape, image_shape)) @@ -110,4 +125,3 @@ def integrate(ii, *args): S += [sign * ii[tuple(corner_points[r])] if(bad[r] == False) else 0 for r in range(rows)] # add only good rows return S - From fa729211e7471b66521a848641697320fc56b3c2 Mon Sep 17 00:00:00 2001 From: Tabish Date: Sat, 29 Mar 2014 15:59:40 +0530 Subject: [PATCH 04/11] Updated test_integral to support new format of parameters --- skimage/transform/integral.py | 6 +++--- skimage/transform/tests/test_integral.py | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 9dda2f89..18f689e7 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -61,13 +61,13 @@ def integrate(ii, begining, ending, *args): >>> arr = np.ones((5, 6), dtype=np.float) >>> ii = integral_image(arr) >>> print(integrate(ii,(1, 0), (1, 2))) # sum from (1,0) -> (1,2) - [ 3.] + [ 3.] >>> print(integrate(ii,(3, 3), (4, 5))) # sum form (3,3) -> (4,5) [ 6.] >>> print(integrate(ii,([1, 3], [0, 3]), ([1, 4], [2, 5]))) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) - [3. 6.] + [ 3. 6.] >>> print(integrate(ii, [1, 3], [0, 3], [1, 4], [2, 5])) # deprecated usage - [3. 6.] + [ 3. 6.] """ # handle new input format if(len(args) == 0): diff --git a/skimage/transform/tests/test_integral.py b/skimage/transform/tests/test_integral.py index 51e52773..1383238b 100644 --- a/skimage/transform/tests/test_integral.py +++ b/skimage/transform/tests/test_integral.py @@ -16,22 +16,23 @@ def test_validity(): def test_basic(): - assert_equal(x[12:24, 10:20].sum(), integrate(s, 12, 10, 23, 19)) - assert_equal(x[:20, :20].sum(), integrate(s, 0, 0, 19, 19)) - assert_equal(x[:20, 10:20].sum(), integrate(s, 0, 10, 19, 19)) - assert_equal(x[10:20, :20].sum(), integrate(s, 10, 0, 19, 19)) + assert_equal(x[12:24, 10:20].sum(), integrate(s, (12, 10), (23, 19))) + assert_equal(x[:20, :20].sum(), integrate(s, (0, 0), (19, 19))) + assert_equal(x[:20, 10:20].sum(), integrate(s, (0, 10), (19, 19))) + assert_equal(x[10:20, :20].sum(), integrate(s, (10, 0), (19, 19))) def test_single(): - assert_equal(x[0, 0], integrate(s, 0, 0, 0, 0)) - assert_equal(x[10, 10], integrate(s, 10, 10, 10, 10)) + assert_equal(x[0, 0], integrate(s, (0, 0), (0, 0))) + assert_equal(x[10, 10], integrate(s, (10, 10), (10, 10))) + def test_vectorized_integrate(): r0 = np.array([12, 0, 0, 10, 0, 10, 30]) c0 = np.array([10, 0, 10, 0, 0, 10, 31]) r1 = np.array([23, 19, 19, 19, 0, 10, 49]) c1 = np.array([19, 19, 19, 19, 0, 10, 49]) - + expected = np.array([x[12:24, 10:20].sum(), x[:20, :20].sum(), x[:20, 10:20].sum(), @@ -39,7 +40,9 @@ def test_vectorized_integrate(): x[0,0], x[10, 10], x[30:, 31:].sum()]) - assert_equal(expected, integrate(s, r0, c0, r1, c1)) + assert_equal(expected, integrate(s, r0, c0, r1, c1)) # test deprecated + assert_equal(expected, integrate(s, (r0, c0), (r1, c1))) + if __name__ == '__main__': from numpy.testing import run_module_suite From ed57d4e54fb612f8237bc839fe39800598bb4ff8 Mon Sep 17 00:00:00 2001 From: Tabish Date: Sun, 30 Mar 2014 20:13:04 +0530 Subject: [PATCH 05/11] Corrected documentation/indentation, changed vstacking --- skimage/transform/integral.py | 91 ++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 18f689e7..cf623da3 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -33,75 +33,73 @@ def integral_image(img): return S -def integrate(ii, begining, ending, *args): +def integrate(ii, start, end, *args): """Use an integral image to integrate over a given window. Parameters ---------- ii : ndarray Integral image. - begining : A tuple - Coordinates of top left corner of window + start : tuple of length equal to dimension of ii + Coordinates of top left corner of window(s). For multiple windows each coordinate should be a list - ending : A tuple - Coordinates of bottom right corner of window + using same format as numpy multi-indexing conventions. + end : tuple of length equal to dimension of ii + Coordinates of bottom right corner of window(s). For multiple windows each coordinate should be a list + using same format as numpy multi-indexing conventions. args: optional - for backward compatibility - used when each coordinate is specified in a seperate list + For backward compatibility with versions prior to 0.10 + 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 ------- S : scalar or ndarray Integral (sum) over the given window(s). - Notes - ----- - Example - >>> arr = np.ones((5, 6), dtype=np.float) + + + Examples + -------- + >>> arr = np.ones((5,6), dtype=np.float) >>> ii = integral_image(arr) - >>> print(integrate(ii,(1, 0), (1, 2))) # sum from (1,0) -> (1,2) + >>> print(integrate(ii,(1,0), (1,2))) # sum from (1,0) -> (1,2) [ 3.] - >>> print(integrate(ii,(3, 3), (4, 5))) # sum form (3,3) -> (4,5) + >>> print(integrate(ii,(3,3), (4,5))) # sum form (3,3) -> (4,5) [ 6.] - >>> print(integrate(ii,([1, 3], [0, 3]), ([1, 4], [2, 5]))) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) + >>> print(integrate(ii,([1,3], [0,3]), ([1,4], [2,5]))) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) [ 3. 6.] - >>> print(integrate(ii, [1, 3], [0, 3], [1, 4], [2, 5])) # deprecated usage + >>> print(integrate(ii, [1,3], [0,3], [1,4], [2,5])) # deprecated usage [ 3. 6.] """ - # handle new input format + rows = 1 + # handle input from new input format if(len(args) == 0): - start = begining[0] - end = ending[0] - for i in range(1, ii.ndim): - start = np.vstack((start, begining[i])) - end = np.vstack((end, ending[i])) - + if(not(isinstance(start[0], int))): + rows = len(start[0]) + start = np.array(start).T + end = np.array(end).T # handle deprecated input format else: - args = (begining, ending) + args - start = args[0] - end = args[ii.ndim] - for i in range(1, ii.ndim): - start = np.vstack((start, args[i])) - end = np.vstack((end, args[i + ii.ndim])) - - # each row of start/end is a starting/ending point - start = start.T - end = end.T - - rows = start.shape[0] - image_shape = ii.shape - total_shape = image_shape + if(not(isinstance(start, int))): + rows = len(start) + args = (start , end) + args + start = np.array(args[:len(args)/2]).T + end = np.array(args[len(args)/2:]).T + + total_shape = ii.shape + total_shape = np.tile(total_shape, [rows, 1]) # take care of negative coordinates - for i in range(1, rows): - total_shape = np.vstack((total_shape, image_shape)) start_negatives = start < 0 end_negatives = end < 0 - start = (start + total_shape) * start_negatives + start * np.invert(start_negatives) - end = (end + total_shape) * end_negatives + end * np.invert(end_negatives) + start = (start + total_shape) * start_negatives + \ + start * np.invert(start_negatives) + end = (end + total_shape) * end_negatives + \ + end * np.invert(end_negatives) if(np.any((end - start) < 0)): @@ -109,19 +107,22 @@ def integrate(ii, begining, ending, *args): S = np.zeros(rows) - bit_perm = 2**(ii.ndim) # bit_perm is the total number of elements in expression of S + bit_perm = 2**(ii.ndim) # total number of elements in expression of S width = len(bin(bit_perm - 1)[2:]) for i in range(bit_perm): # for all permutations - # generate boolean array corresponding to permutation eg [True, False] for '10' + # 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 + 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 + 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 for r in range(rows)] # add only good rows + S += [sign * ii[tuple(corner_points[r])] if(bad[r] == False) else 0 + for r in range(rows)] # add only good rows return S From 2909f6d5dba2fb1b132ba5025ae11c25b23af269 Mon Sep 17 00:00:00 2001 From: Tabish Date: Sun, 30 Mar 2014 22:17:46 +0530 Subject: [PATCH 06/11] Corrected Python3 slice indexing error --- skimage/transform/integral.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index cf623da3..75be64c4 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -86,8 +86,8 @@ def integrate(ii, start, end, *args): if(not(isinstance(start, int))): rows = len(start) args = (start , end) + args - start = np.array(args[:len(args)/2]).T - end = np.array(args[len(args)/2:]).T + start = np.array(args[:int(len(args)/2)]).T + end = np.array(args[int(len(args)/2):]).T total_shape = ii.shape From b7c42acd6510d28bc8ea9a4182498d72afa5d9da Mon Sep 17 00:00:00 2001 From: Tabish Date: Fri, 16 May 2014 12:23:17 +0530 Subject: [PATCH 07/11] Check input for Iterables instead of numeric types, PEP8,indentation,added comments --- skimage/transform/integral.py | 73 ++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 75be64c4..8c5ba755 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -1,5 +1,5 @@ import numpy as np - +import collections def integral_image(img): """Integral image / summed area table. @@ -42,15 +42,19 @@ def integrate(ii, start, end, *args): Integral image. start : tuple of length equal to dimension of ii Coordinates of top left corner of window(s). - For multiple windows each coordinate should be a list - using same format as numpy multi-indexing conventions. + 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,...], ...), + The convention mirrors the NumPy multi-indexing convention. end : tuple of length equal to dimension of ii Coordinates of bottom right corner of window(s). - For multiple windows each coordinate should be a list - using same format as numpy multi-indexing conventions. + 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, ...], ...) + The convention mirrors the NumPy multi-indexing convention. args: optional For backward compatibility with versions prior to 0.10 - The earlier function signature was integrate(ii, r0, c0, r1, c1), + 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. @@ -63,52 +67,69 @@ def integrate(ii, start, end, *args): Examples -------- - >>> arr = np.ones((5,6), dtype=np.float) + >>> arr = np.ones((5, 6), dtype=np.float) >>> ii = integral_image(arr) - >>> print(integrate(ii,(1,0), (1,2))) # sum from (1,0) -> (1,2) + >>> integrate(ii, (1, 0), (1, 2)) # sum from (1,0) -> (1,2) [ 3.] - >>> print(integrate(ii,(3,3), (4,5))) # sum form (3,3) -> (4,5) + >>> integrate(ii, (3, 3), (4, 5)) # sum form (3,3) -> (4,5) [ 6.] - >>> print(integrate(ii,([1,3], [0,3]), ([1,4], [2,5]))) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) - [ 3. 6.] - >>> print(integrate(ii, [1,3], [0,3], [1,4], [2,5])) # deprecated usage + >>> integrate(ii, ([1, 3], [0, 3]), ([1, 4], [2, 5])) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) [ 3. 6.] """ rows = 1 # handle input from new input format - if(len(args) == 0): - if(not(isinstance(start[0], int))): + if len(args) == 0: + if isinstance(start[0], collections.Iterable): rows = len(start[0]) start = np.array(start).T end = np.array(end).T # handle deprecated input format else: - if(not(isinstance(start, int))): + if isinstance(start, collections.Iterable): rows = len(start) - args = (start , end) + args + args = (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]) - # take care of negative coordinates + # convert negative indices into equivalent positive indices start_negatives = start < 0 end_negatives = end < 0 start = (start + total_shape) * start_negatives + \ - start * np.invert(start_negatives) + start * ~(start_negatives) end = (end + total_shape) * end_negatives + \ - end * np.invert(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 + # 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) # total number of elements in expression of S + 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 + # is replaced, i.e ii[end_row, end_col, end_depth]. Similarly, perm(001) + # means replace last coordinated by start - 1, i.e + # ii[end_row, end_col, start_depth - 1],and so on. + # Sign of even permutations is +ve, while those of odd is -ve. + # 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' @@ -118,11 +139,11 @@ def integrate(ii, start, end, *args): 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 + 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 + ((start - 1) * bool_mask) # find corner for each row S += [sign * ii[tuple(corner_points[r])] if(bad[r] == False) else 0 - for r in range(rows)] # add only good rows + for r in range(rows)] # add only good rows return S From 37572bdb06bb3c30b75a441fee16f31ffd90441f Mon Sep 17 00:00:00 2001 From: Tabish Date: Fri, 16 May 2014 13:16:22 +0530 Subject: [PATCH 08/11] Corrected doctest output --- skimage/transform/integral.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 8c5ba755..5a8eb3fb 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -70,11 +70,11 @@ 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) - [ 3.] + array([ 3.]) >>> integrate(ii, (3, 3), (4, 5)) # sum form (3,3) -> (4,5) - [ 6.] + array([ 6.]) >>> integrate(ii, ([1, 3], [0, 3]), ([1, 4], [2, 5])) # sum from (1,0) -> (1,2) and (3,3) -> (4,5) - [ 3. 6.] + array([ 3., 6.]) """ rows = 1 # handle input from new input format From c57d7f2784d1a0d3ccc3ac1dadb9acc3720f9d67 Mon Sep 17 00:00:00 2001 From: Tabish Date: Tue, 20 May 2014 12:05:28 +0530 Subject: [PATCH 09/11] Corrected grammatical mistakes in documentation --- skimage/transform/integral.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 5a8eb3fb..52bf7375 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -40,23 +40,23 @@ def integrate(ii, start, end, *args): ---------- ii : ndarray Integral image. - start : tuple of length equal to dimension of ii + start : 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,...], ...), - The convention mirrors the NumPy multi-indexing convention. - end : tuple of length equal to dimension of ii + `([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` 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, ...], ...) - The convention mirrors the NumPy multi-indexing convention. + This convention mirrors the NumPy multi-indexing convention. args: optional - For backward compatibility with versions prior to 0.10 + For backward compatibility with versions prior to 0.10. 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. + where `r0`, `c0` are int(lists) specifying start coordinates + of window(s) to be integrated and `r1`, `c1` the end coordinates. Returns @@ -125,9 +125,9 @@ def integrate(ii, start, end, *args): # 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 coordinated by start - 1, i.e - # ii[end_row, end_col, start_depth - 1],and so on. - # Sign of even permutations is +ve, while those of odd is -ve. + # 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 # the final sum. From eb4923cb810611f3c40970caacef8ceb6ddce963 Mon Sep 17 00:00:00 2001 From: Tabish Date: Wed, 21 May 2014 15:36:14 +0530 Subject: [PATCH 10/11] Removed extra Tab character --- skimage/transform/integral.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 52bf7375..46cb6f0c 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -43,7 +43,7 @@ def integrate(ii, start, end, *args): start : 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 + 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` From 49213973f3a49c8ac4624421322dccca2d267947 Mon Sep 17 00:00:00 2001 From: Tabish Date: Wed, 25 Mar 2015 01:52:23 +0530 Subject: [PATCH 11/11] Changed input format,updated docstring, PEP8 --- skimage/transform/integral.py | 67 +++++++++++------------- skimage/transform/tests/test_integral.py | 4 +- 2 files changed, 34 insertions(+), 37 deletions(-) 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__':