From f81fbf5b25bc44af191981115f218fd70b0d141a Mon Sep 17 00:00:00 2001 From: Luis Pedro Coelho Date: Tue, 1 Oct 2013 13:07:17 +0200 Subject: [PATCH 1/2] ENH Faster perimeter computation Measured a 30% speedup on a 640x640 image. Fundamentally, this makes fewer passes over the image; so it should never be worse that the previous implementation. --- skimage/measure/_regionprops.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/skimage/measure/_regionprops.py b/skimage/measure/_regionprops.py index 0cc56635..d383faf1 100644 --- a/skimage/measure/_regionprops.py +++ b/skimage/measure/_regionprops.py @@ -513,28 +513,40 @@ def perimeter(image, neighbourhood=4): a Perimeter Estimator. The Queen's University of Belfast. http://www.cs.qub.ac.uk/~d.crookes/webpubs/papers/perimeter.doc """ + from skimage.exposure import histogram if neighbourhood == 4: strel = STREL_4 else: strel = STREL_8 + image = image.astype(np.uint8) eroded_image = ndimage.binary_erosion(image, strel, border_value=0) border_image = image - eroded_image # perimeter contribution: corresponding values in convolved image - perimeter_weights = { + perimeter_weights_matrix = { 1: (5, 7, 15, 17, 25, 27), sqrt(2): (21, 33), (1 + sqrt(2)) / 2: (13, 23) } + + # specifying the perimeter_weights array inline did not make a measurable + # difference in execution time (for a 640x640 image), but made the code + # harder to follow, so we build it everytime: + perimeter_weights = np.zeros(34, float) + for v,indices in perimeter_weights_matrix.items(): + for i in indices: perimeter_weights[i] = v + perimeter_image = ndimage.convolve(border_image, np.array([[10, 2, 10], [ 2, 1, 2], [10, 2, 10]]), mode='constant', cval=0) - total_perimeter = 0 - for weight, values in perimeter_weights.items(): - num_values = 0 - for value in values: - num_values += np.sum(perimeter_image == value) - total_perimeter += num_values * weight + # You can also write + # return perimeter_weights[perimeter_image].sum() + # but that was measured as taking much longer than histogram + np.dot (5x + # as much time) + + perimeter_histogram,_ = histogram(perimeter_image, nbins=50) + size = min(len(perimeter_histogram), len(perimeter_weights)) + total_perimeter = np.dot(perimeter_histogram[:size], perimeter_weights[:size]) return total_perimeter From ff0b315110f232df78777bd84ac78642bc18f264 Mon Sep 17 00:00:00 2001 From: Luis Pedro Coelho Date: Tue, 1 Oct 2013 15:19:42 +0200 Subject: [PATCH 2/2] RFCT Simpler code for initializing array Also, replaced skimage.measures.histogram by np.bincount. Both changes were suggested in the pull request discussion by Juan Nunez-Iglesias (jni): https://github.com/scikit-image/scikit-image/pull/746 --- skimage/measure/_regionprops.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/skimage/measure/_regionprops.py b/skimage/measure/_regionprops.py index d383faf1..0156a6bc 100644 --- a/skimage/measure/_regionprops.py +++ b/skimage/measure/_regionprops.py @@ -513,7 +513,6 @@ def perimeter(image, neighbourhood=4): a Perimeter Estimator. The Queen's University of Belfast. http://www.cs.qub.ac.uk/~d.crookes/webpubs/papers/perimeter.doc """ - from skimage.exposure import histogram if neighbourhood == 4: strel = STREL_4 else: @@ -522,19 +521,11 @@ def perimeter(image, neighbourhood=4): eroded_image = ndimage.binary_erosion(image, strel, border_value=0) border_image = image - eroded_image - # perimeter contribution: corresponding values in convolved image - perimeter_weights_matrix = { - 1: (5, 7, 15, 17, 25, 27), - sqrt(2): (21, 33), - (1 + sqrt(2)) / 2: (13, 23) - } + perimeter_weights = np.zeros(50, float) + perimeter_weights[[5, 7, 15, 17, 25, 27]] = 1 + perimeter_weights[[21, 33]] = sqrt(2) + perimeter_weights[[13, 23]] = (1 + sqrt(2)) / 2 - # specifying the perimeter_weights array inline did not make a measurable - # difference in execution time (for a 640x640 image), but made the code - # harder to follow, so we build it everytime: - perimeter_weights = np.zeros(34, float) - for v,indices in perimeter_weights_matrix.items(): - for i in indices: perimeter_weights[i] = v perimeter_image = ndimage.convolve(border_image, np.array([[10, 2, 10], [ 2, 1, 2], @@ -543,10 +534,8 @@ def perimeter(image, neighbourhood=4): # You can also write # return perimeter_weights[perimeter_image].sum() - # but that was measured as taking much longer than histogram + np.dot (5x + # but that was measured as taking much longer than bincount + np.dot (5x # as much time) - - perimeter_histogram,_ = histogram(perimeter_image, nbins=50) - size = min(len(perimeter_histogram), len(perimeter_weights)) - total_perimeter = np.dot(perimeter_histogram[:size], perimeter_weights[:size]) + perimeter_histogram = np.bincount(perimeter_image.ravel(), minlength=50) + total_perimeter = np.dot(perimeter_histogram, perimeter_weights) return total_perimeter