From 646c2102d26eff70424f2d088ffa7a18d33bab87 Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 16:15:08 +0100 Subject: [PATCH 01/14] Added active contour model --- skimage/segmentation/__init__.py | 2 + skimage/segmentation/active_contour_model.py | 199 ++++++++++++++++++ .../tests/test_active_contour_model.py | 109 ++++++++++ 3 files changed, 310 insertions(+) create mode 100644 skimage/segmentation/active_contour_model.py create mode 100644 skimage/segmentation/tests/test_active_contour_model.py diff --git a/skimage/segmentation/__init__.py b/skimage/segmentation/__init__.py index f79fb482..a1a316f4 100644 --- a/skimage/segmentation/__init__.py +++ b/skimage/segmentation/__init__.py @@ -1,4 +1,5 @@ from .random_walker_segmentation import random_walker +from .active_contour_model import active_contour_model from ._felzenszwalb import felzenszwalb from .slic_superpixels import slic from ._quickshift import quickshift @@ -8,6 +9,7 @@ from ._join import join_segmentations, relabel_from_one, relabel_sequential __all__ = ['random_walker', + 'active_contour_model', 'felzenszwalb', 'slic', 'quickshift', diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py new file mode 100644 index 00000000..c673316d --- /dev/null +++ b/skimage/segmentation/active_contour_model.py @@ -0,0 +1,199 @@ +import numpy as np +from skimage import img_as_float +import scipy.linalg +from scipy.interpolate import RectBivariateSpline +from skimage.filters import gaussian_filter, sobel + +def active_contour_model(image, snake, alpha=0.01, beta=0.1, + w_line=0, w_edge=1, gamma=0.01, + bc='periodic', max_px_move=1.0, + max_iterations=2500, convergence=0.1): + """Active contour model + + Active contours by fitting snakes to features of images. Supports single + and multichannel 2D images. Snakes can be periodic (for segmentation) or + have fixed and/or free ends. + + Parameters + ---------- + image: (N, M) or (N, M, 3) ndarray + Input image + snake: (N, 2) ndarray + Initialisation of snake. + alpha: float, optional + Snake length shape parameter + beta: float, optional + Snake smoothness shape parameter + w_line: float, optional + Controls attraction to brightness. Use negative values to attract to + dark regions + w_edge: float, optional + Controls attraction to edges. Use negative values to repel snake from + edges. + gamma: flota, optional + Excpliti time stepping parameter. + bc: {'periodic', 'free', 'fixed'}, optional + Boundary conditions for worm. 'periodic' attaches the two ends of the + snake, 'fixed' holds the end-points in place, and'free' allows free + movement of the ends. 'fixed' and 'free' can be combined by parsing + 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' + yields same behaviour as 'fixed' and 'free', respectively. + max_px_move: float, optional + Maximum pixel distance to move per iteration. + max_iterations: int, optional + Maximum iterations to optimize snake shape. + convergence: float, optional + Convergence criteria. + + Returns + ------- + snake: (N, 2) ndarray + Optimised snake, same shape as input parameter. + + References + ---------- + .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour models". International Journal of Computer Vision 1 (4): 321 (1988). + + Examples + -------- + >>> #from skimage.segmentation import active_contour_model + >>> from skimage.draw import circle_perimeter + >>> img = np.zeros((100, 100)) + >>> rr, cc = circle_perimeter(35, 45, 25) + >>> img[rr, cc] = 1 + >>> img = gaussian_filter(img,2) + >>> s = np.linspace(0,2*np.pi,100) + >>> init = 50*np.array([np.cos(s),np.sin(s)]).T+50 + >>> snake = active_contour_model(img, init, w_edge=0, w_line=1) + >>> int(np.mean(np.sqrt((45-snake[:,0])**2 + (35-snake[:,1])**2))) + 25 + + """ + + max_iterations = int(max_iterations) + if max_iterations<=0: + raise ValueError("max_iterations should be >0.") + convergence_order = 10 + valid_bcs = ['periodic', 'free', 'fixed', 'free-fixed', + 'fixed-free', 'fixed-fixed', 'free-free'] + if bc not in valid_bcs: + raise ValueError("Invalid boundary condition.\n"+ + "Should be one of: "+", ".join(valid_bcs)+'.') + img = img_as_float(image) + RGB = len(img.shape)==3 + + # Find edges using sobel: + if w_edge!=0: + if RGB: + edge = [sobel(img[:,:,0]),sobel(img[:,:,1]),sobel(img[:,:,2])] + else: + edge = [sobel(img)] + for i in xrange(3 if RGB else 1): + edge[i][0,:] = edge[i][1,:] + edge[i][-1,:] = edge[i][-2,:] + edge[i][:,0] = edge[i][:,1] + edge[i][:,-1] = edge[i][:,-2] + else: + edge = [0] + + # Superimpose intensity and edge images: + if RGB: + img = w_line*np.sum(img,axis=2) \ + + w_edge*sum(edge) + else: + img = w_line*img + w_edge*edge[0] + + # Interpolate for smoothness: + intp = RectBivariateSpline(np.arange(img.shape[1]), + np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) + + x, y = snake[:, 0].copy(), snake[:, 1].copy() + xsave = np.empty((convergence_order,len(x))) + ysave = np.empty((convergence_order,len(x))) + + # Build snake shape matrix + n = len(x) + a = np.roll(np.eye(n), -1, axis=0) \ + + np.roll(np.eye(n), -1, axis=1) \ + - 2*np.eye(n) + b = np.roll(np.eye(n), -2, axis=0) \ + + np.roll(np.eye(n), -2, axis=1) \ + - 4*np.roll(np.eye(n), -1, axis=0) \ + - 4*np.roll(np.eye(n), -1, axis=1) \ + + 6*np.eye(n) + A = -alpha*a + beta*b + + # Impose boundary conditions different from periodic: + sfixed = False + if bc.startswith('fixed'): + A[0, :] = 0 + A[1, :] = 0 + A[1, :3] = [1, -2, 1] + sfixed = True + efixed = False + if bc.endswith('fixed'): + A[-1, :] = 0 + A[-2, :] = 0 + A[-2, -3:] = [1, -2, 1] + efixed = True + sfree = False + if bc.startswith('free'): + A[0, :] = 0 + A[0, :3] = [1, -2, 1] + A[1, :] = 0 + A[1, :4] = [-1, 3, -3, 1] + sfree = True + efree = False + if bc.endswith('free'): + A[-1, :] = 0 + A[-1, -3:] = [1, -2, 1] + A[-2, :] = 0 + A[-2, -4:] = [-1, 3, -3, 1] + efree = True + + # Only one inversion is needed: + inv = scipy.linalg.inv(A+gamma*np.eye(n)) + + # Explcit time stepping for image energy minimization: + for i in xrange(max_iterations): + fx = intp(x, y, dx=1, grid=False) + fy = intp(x, y, dy=1, grid=False) + if sfixed: + fx[0] = 0 + fy[0] = 0 + if efixed: + fx[-1] = 0 + fy[-1] = 0 + if sfree: + fx[0] *= 2 + fy[0] *= 2 + if efree: + fx[-1] *= 2 + fy[-1] *= 2 + xn = np.dot(inv, gamma*x + fx) + yn = np.dot(inv, gamma*y + fy) + + # Movements are capped to max_px_move per iteration: + dx = max_px_move*np.tanh(xn-x) + dy = max_px_move*np.tanh(yn-y) + if sfixed: + dx[0] = 0 + dy[0] = 0 + if efixed: + dx[-1] = 0 + dy[-1] = 0 + x[:] += dx + y[:] += dy + + # Convergence criteria: + j = i%(convergence_order+1) + if j 2 + snake = active_contour_model(gaussian_filter(img,3), init, + bc='fixed', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, + max_iterations=100) + assert_allclose(snake[0,:], [x[0], y[0]], atol=1e-5) + + +def bad_input_tests(): + img = np.zeros((10, 10)) + x = np.linspace(5, 424, 100) + y = np.linspace(136, 50, 100) + init = np.array([x, y]).T + np.testing.assert_raises(ValueError, active_contour_model, img, init, + bc='wrong') + np.testing.assert_raises(ValueError, active_contour_model, img, init, + max_iterations=-15) + + +if __name__ == "__main__": + np.testing.run_module_suite() From ad4948a609d843fdf8002d453287311771f97d40 Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 16:37:21 +0100 Subject: [PATCH 02/14] pep8 compliance --- skimage/segmentation/active_contour_model.py | 32 ++++---- .../tests/test_active_contour_model.py | 78 +++++++++---------- 2 files changed, 56 insertions(+), 54 deletions(-) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index c673316d..c13f56e2 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -2,7 +2,7 @@ import numpy as np from skimage import img_as_float import scipy.linalg from scipy.interpolate import RectBivariateSpline -from skimage.filters import gaussian_filter, sobel +from skimage.filters import sobel def active_contour_model(image, snake, alpha=0.01, beta=0.1, w_line=0, w_edge=1, gamma=0.01, @@ -58,6 +58,7 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, -------- >>> #from skimage.segmentation import active_contour_model >>> from skimage.draw import circle_perimeter + >>> from skimage.filters import gaussian_filter >>> img = np.zeros((100, 100)) >>> rr, cc = circle_perimeter(35, 45, 25) >>> img[rr, cc] = 1 @@ -71,7 +72,7 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, """ max_iterations = int(max_iterations) - if max_iterations<=0: + if max_iterations <= 0: raise ValueError("max_iterations should be >0.") convergence_order = 10 valid_bcs = ['periodic', 'free', 'fixed', 'free-fixed', @@ -80,25 +81,26 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, raise ValueError("Invalid boundary condition.\n"+ "Should be one of: "+", ".join(valid_bcs)+'.') img = img_as_float(image) - RGB = len(img.shape)==3 + RGB = len(img.shape) == 3 # Find edges using sobel: - if w_edge!=0: + if w_edge != 0: if RGB: - edge = [sobel(img[:,:,0]),sobel(img[:,:,1]),sobel(img[:,:,2])] + edge = [sobel(img[:, :, 0]), sobel(img[:, :, 1]), + sobel(img[:, :, 2])] else: edge = [sobel(img)] for i in xrange(3 if RGB else 1): - edge[i][0,:] = edge[i][1,:] - edge[i][-1,:] = edge[i][-2,:] - edge[i][:,0] = edge[i][:,1] - edge[i][:,-1] = edge[i][:,-2] + edge[i][0, :] = edge[i][1, :] + edge[i][-1, :] = edge[i][-2, :] + edge[i][:, 0] = edge[i][:, 1] + edge[i][:, -1] = edge[i][:, -2] else: edge = [0] # Superimpose intensity and edge images: if RGB: - img = w_line*np.sum(img,axis=2) \ + img = w_line*np.sum(img, axis=2) \ + w_edge*sum(edge) else: img = w_line*img + w_edge*edge[0] @@ -108,8 +110,8 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) x, y = snake[:, 0].copy(), snake[:, 1].copy() - xsave = np.empty((convergence_order,len(x))) - ysave = np.empty((convergence_order,len(x))) + xsave = np.empty((convergence_order, len(x))) + ysave = np.empty((convergence_order, len(x))) # Build snake shape matrix n = len(x) @@ -187,9 +189,9 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, # Convergence criteria: j = i%(convergence_order+1) - if j 2 - snake = active_contour_model(gaussian_filter(img,3), init, + assert np.sum(np.abs(snake[0, :]-snake[-1, :])) > 2 + snake = active_contour_model(gaussian_filter(img, 3), init, bc='fixed', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, max_iterations=100) - assert_allclose(snake[0,:], [x[0], y[0]], atol=1e-5) + assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) def bad_input_tests(): @@ -99,9 +99,9 @@ def bad_input_tests(): x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T - np.testing.assert_raises(ValueError, active_contour_model, img, init, + assert_raises(ValueError, active_contour_model, img, init, bc='wrong') - np.testing.assert_raises(ValueError, active_contour_model, img, init, + assert_raises(ValueError, active_contour_model, img, init, max_iterations=-15) From 7c30f36d8557633b19c62901be1dad5479d2606a Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 19:12:35 +0100 Subject: [PATCH 03/14] Active contour example added --- doc/examples/plot_active_contours.py | 83 ++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 doc/examples/plot_active_contours.py diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py new file mode 100644 index 00000000..fb003526 --- /dev/null +++ b/doc/examples/plot_active_contours.py @@ -0,0 +1,83 @@ +""" +==================================================== +Active Contour Model +==================================================== +The active contour model is a method to fit open or closed splines to lines or +edges in an image. It works by minimising an energy that is in part defined by +the image and part by the spline's shape: length and smoothness. The +minimization is done implicitly in the shape energy and explicitly in the +image energy. + +In the following two examples the active contour model is used (1) to segment +the face of a person from the rest of an image by fitting a closed curve +to the edges of the face and (2) to find the darkest curve between two fixed +points while obeying smoothness considerations. + +.. [1] *Snakes: Active contour models*. Kass, M.; Witkin, A.; Terzopoulos, D. + International Journal of Computer Vision 1 (4): 321 (1988). + +We initialize a circle around the astronaut's face and use the defualt boundary +condition `bc='periodic'` to fit a closed curve. The default parameters +`w_line=0, w_edge=1` will make the curve search towards edges, such as the +boundaries of the face. +""" + +import numpy as np +import matplotlib.pyplot as plt +from skimage.color import rgb2gray +from skimage import data +from skimage.filters import gaussian_filter +from skimage.segmentation import active_contour_model + +img = data.astronaut() +img = rgb2gray(img) + +s = np.linspace(0, 2*np.pi, 400) +x = 220 + 100*np.cos(s) +y = 100 + 100*np.sin(s) +init = np.array([x, y]).T + +snake = active_contour_model(gaussian_filter(img, 3), + init, alpha=0.015, beta=10, gamma=0.001) + +fig = plt.figure(figsize=(7, 7)) +ax = fig.add_subplot(111) +plt.gray() +ax.imshow(img) +ax.plot(init[:, 0], init[:, 1], '--r') +ax.plot(snake[:, 0], snake[:, 1], '-b') +ax.set_xticks([]), ax.set_yticks([]) +ax.axis([0, img.shape[1], img.shape[0], 0]) + +""" +.. image:: PLOT2RST.current_figure + +Here we initialize a straight line between two points, `(5, 136)` and +`(424, 50)`, and require that the spline has its end points there by giving +the boundary condition `bc='fixed'`. We furthermore make the algorithm search +for dark lines by giving a negative `w_line` value. +""" + +img = data.text() + +x = np.linspace(5, 424, 100) +y = np.linspace(136, 50, 100) +init = np.array([x, y]).T + +snake = active_contour_model(gaussian_filter(img, 1), init, bc='fixed', + alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) + +fig = plt.figure(figsize=(9, 5)) +ax = fig.add_subplot(111) +plt.gray() +ax.imshow(img) +ax.plot(init[:, 0], init[:, 1], '--r') +ax.plot(snake[:, 0], snake[:, 1], '-b') +ax.set_xticks([]), ax.set_yticks([]) +ax.axis([0, img.shape[1], img.shape[0], 0]) + +plt.show() + +""" +.. image:: PLOT2RST.current_figure +""" From 96847f26526f64e29a790d6766164bd62b97b39f Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 20:28:28 +0100 Subject: [PATCH 04/14] pep8 and other small changes --- doc/examples/plot_active_contours.py | 3 ++- skimage/segmentation/active_contour_model.py | 17 ++++++++++------- .../tests/test_active_contour_model.py | 12 ++++++------ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index fb003526..0ad7ebb4 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -11,7 +11,8 @@ image energy. In the following two examples the active contour model is used (1) to segment the face of a person from the rest of an image by fitting a closed curve to the edges of the face and (2) to find the darkest curve between two fixed -points while obeying smoothness considerations. +points while obeying smoothness considerations. Typically it is a good idea to +smooth images a bit before analyzing, as done in the following examples. .. [1] *Snakes: Active contour models*. Kass, M.; Witkin, A.; Terzopoulos, D. International Journal of Computer Vision 1 (4): 321 (1988). diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index c13f56e2..ece8847d 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -8,7 +8,7 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, w_line=0, w_edge=1, gamma=0.01, bc='periodic', max_px_move=1.0, max_iterations=2500, convergence=0.1): - """Active contour model + """Active contour model. Active contours by fitting snakes to features of images. Supports single and multichannel 2D images. Snakes can be periodic (for segmentation) or @@ -52,21 +52,24 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, References ---------- - .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour models". International Journal of Computer Vision 1 (4): 321 (1988). + .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour + models". International Journal of Computer Vision 1 (4): 321 (1988). Examples -------- - >>> #from skimage.segmentation import active_contour_model >>> from skimage.draw import circle_perimeter >>> from skimage.filters import gaussian_filter + Create and smooth image: >>> img = np.zeros((100, 100)) >>> rr, cc = circle_perimeter(35, 45, 25) >>> img[rr, cc] = 1 - >>> img = gaussian_filter(img,2) - >>> s = np.linspace(0,2*np.pi,100) - >>> init = 50*np.array([np.cos(s),np.sin(s)]).T+50 + >>> img = gaussian_filter(img, 2) + Initiliaze spline: + >>> s = np.linspace(0, 2*np.pi,100) + >>> init = 50*np.array([np.cos(s), np.sin(s)]).T+50 + Fit spline to image: >>> snake = active_contour_model(img, init, w_edge=0, w_line=1) - >>> int(np.mean(np.sqrt((45-snake[:,0])**2 + (35-snake[:,1])**2))) + >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + (35-snake[:, 1])**2))) 25 """ diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index 73289fd7..3e866aef 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -5,7 +5,7 @@ from skimage.filters import gaussian_filter from skimage.segmentation import active_contour_model from numpy.testing import assert_equal, assert_allclose, assert_raises -def periodic_reference_test(): +def test_periodic_reference(): img = data.astronaut() img = rgb2gray(img) s = np.linspace(0, 2*np.pi, 400) @@ -20,7 +20,7 @@ def periodic_reference_test(): assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) -def fixed_reference_test(): +def test_fixed_reference(): img = data.text() x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) @@ -33,7 +33,7 @@ def fixed_reference_test(): assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) -def free_reference_test(): +def test_free_reference(): img = data.text() x = np.linspace(5, 424, 100) y = np.linspace(70, 40, 100) @@ -46,7 +46,7 @@ def free_reference_test(): assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) -def RGB_test(): +def test_RGB(): img = gaussian_filter(data.text(), 1) imgR = np.zeros((img.shape[0], img.shape[1], 3)) imgG = np.zeros((img.shape[0], img.shape[1], 3)) @@ -73,7 +73,7 @@ def RGB_test(): assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) -def end_points_tests(): +def test_end_points_tests(): img = data.astronaut() img = rgb2gray(img) s = np.linspace(0, 2*np.pi, 400) @@ -94,7 +94,7 @@ def end_points_tests(): assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) -def bad_input_tests(): +def test_bad_input_tests(): img = np.zeros((10, 10)) x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) From fa6815404f0255dddbcc779019c3d9388c73ba2f Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 21:48:32 +0100 Subject: [PATCH 05/14] spelling corrections and misc. --- doc/examples/plot_active_contours.py | 2 +- skimage/segmentation/active_contour_model.py | 24 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index 0ad7ebb4..32689adc 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -17,7 +17,7 @@ smooth images a bit before analyzing, as done in the following examples. .. [1] *Snakes: Active contour models*. Kass, M.; Witkin, A.; Terzopoulos, D. International Journal of Computer Vision 1 (4): 321 (1988). -We initialize a circle around the astronaut's face and use the defualt boundary +We initialize a circle around the astronaut's face and use the default boundary condition `bc='periodic'` to fit a closed curve. The default parameters `w_line=0, w_edge=1` will make the curve search towards edges, such as the boundaries of the face. diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index ece8847d..882ab559 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -16,38 +16,38 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, Parameters ---------- - image: (N, M) or (N, M, 3) ndarray + image : (N, M) or (N, M, 3) ndarray Input image - snake: (N, 2) ndarray + snake : (N, 2) ndarray Initialisation of snake. - alpha: float, optional + alpha : float, optional Snake length shape parameter - beta: float, optional + beta : float, optional Snake smoothness shape parameter - w_line: float, optional + w_line : float, optional Controls attraction to brightness. Use negative values to attract to dark regions - w_edge: float, optional + w_edge : float, optional Controls attraction to edges. Use negative values to repel snake from edges. - gamma: flota, optional + gamma : float, optional Excpliti time stepping parameter. - bc: {'periodic', 'free', 'fixed'}, optional + bc : {'periodic', 'free', 'fixed'}, optional Boundary conditions for worm. 'periodic' attaches the two ends of the snake, 'fixed' holds the end-points in place, and'free' allows free movement of the ends. 'fixed' and 'free' can be combined by parsing 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' yields same behaviour as 'fixed' and 'free', respectively. - max_px_move: float, optional + max_px_move : float, optional Maximum pixel distance to move per iteration. - max_iterations: int, optional + max_iterations : int, optional Maximum iterations to optimize snake shape. convergence: float, optional Convergence criteria. Returns ------- - snake: (N, 2) ndarray + snake : (N, 2) ndarray Optimised snake, same shape as input parameter. References @@ -84,7 +84,7 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, raise ValueError("Invalid boundary condition.\n"+ "Should be one of: "+", ".join(valid_bcs)+'.') img = img_as_float(image) - RGB = len(img.shape) == 3 + RGB = img.ndim == 3 # Find edges using sobel: if w_edge != 0: From 94b335fb2e90f28e2fc7667244cb05f738abbe7f Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Mon, 31 Aug 2015 21:52:47 +0100 Subject: [PATCH 06/14] change test names --- skimage/segmentation/tests/test_active_contour_model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index 3e866aef..fa30ad3a 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -73,7 +73,7 @@ def test_RGB(): assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) -def test_end_points_tests(): +def test_end_points(): img = data.astronaut() img = rgb2gray(img) s = np.linspace(0, 2*np.pi, 400) @@ -94,7 +94,7 @@ def test_end_points_tests(): assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) -def test_bad_input_tests(): +def test_bad_input(): img = np.zeros((10, 10)) x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) From 8d344d090f2bc4c0a9643b435767c4b3b9a3d793 Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Sat, 5 Sep 2015 13:29:59 +0100 Subject: [PATCH 07/14] Active contour updates. Now works with scipy<0.14 --- doc/examples/plot_active_contours.py | 6 +-- skimage/segmentation/__init__.py | 4 +- skimage/segmentation/active_contour_model.py | 49 +++++++++++++------ .../tests/test_active_contour_model.py | 24 ++++----- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index 32689adc..911bc174 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -28,7 +28,7 @@ import matplotlib.pyplot as plt from skimage.color import rgb2gray from skimage import data from skimage.filters import gaussian_filter -from skimage.segmentation import active_contour_model +from skimage.segmentation import active_contour img = data.astronaut() img = rgb2gray(img) @@ -38,7 +38,7 @@ x = 220 + 100*np.cos(s) y = 100 + 100*np.sin(s) init = np.array([x, y]).T -snake = active_contour_model(gaussian_filter(img, 3), +snake = active_contour(gaussian_filter(img, 3), init, alpha=0.015, beta=10, gamma=0.001) fig = plt.figure(figsize=(7, 7)) @@ -65,7 +65,7 @@ x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T -snake = active_contour_model(gaussian_filter(img, 1), init, bc='fixed', +snake = active_contour(gaussian_filter(img, 1), init, bc='fixed', alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) fig = plt.figure(figsize=(9, 5)) diff --git a/skimage/segmentation/__init__.py b/skimage/segmentation/__init__.py index a1a316f4..103ca5a4 100644 --- a/skimage/segmentation/__init__.py +++ b/skimage/segmentation/__init__.py @@ -1,5 +1,5 @@ from .random_walker_segmentation import random_walker -from .active_contour_model import active_contour_model +from .active_contour_model import active_contour from ._felzenszwalb import felzenszwalb from .slic_superpixels import slic from ._quickshift import quickshift @@ -9,7 +9,7 @@ from ._join import join_segmentations, relabel_from_one, relabel_sequential __all__ = ['random_walker', - 'active_contour_model', + 'active_contour', 'felzenszwalb', 'slic', 'quickshift', diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index 882ab559..c6ee27f2 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -1,13 +1,14 @@ +import warnings import numpy as np from skimage import img_as_float import scipy.linalg -from scipy.interpolate import RectBivariateSpline +from scipy.interpolate import RectBivariateSpline, interp2d from skimage.filters import sobel -def active_contour_model(image, snake, alpha=0.01, beta=0.1, - w_line=0, w_edge=1, gamma=0.01, - bc='periodic', max_px_move=1.0, - max_iterations=2500, convergence=0.1): +def active_contour(image, snake, alpha=0.01, beta=0.1, + w_line=0, w_edge=1, gamma=0.01, + bc='periodic', max_px_move=1.0, + max_iterations=2500, convergence=0.1): """Active contour model. Active contours by fitting snakes to features of images. Supports single @@ -59,20 +60,28 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, -------- >>> from skimage.draw import circle_perimeter >>> from skimage.filters import gaussian_filter - Create and smooth image: + >>> # Create and smooth image: >>> img = np.zeros((100, 100)) >>> rr, cc = circle_perimeter(35, 45, 25) >>> img[rr, cc] = 1 >>> img = gaussian_filter(img, 2) - Initiliaze spline: + >>>> # Initiliaze spline: >>> s = np.linspace(0, 2*np.pi,100) >>> init = 50*np.array([np.cos(s), np.sin(s)]).T+50 - Fit spline to image: + >>> # Fit spline to image: >>> snake = active_contour_model(img, init, w_edge=0, w_line=1) >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + (35-snake[:, 1])**2))) 25 """ + scipy_version = map(int, scipy.__version__.split('.')) + new_scipy = scipy_version[0]>0 or \ + (scipy_version[0]==0 and scipy_version[1]>=14) + if not new_scipy: + warnings.warn('You are using an old version of scipy. ' + 'Upgrading to a version newer than 0.14.0 ' + 'will signifcantly improve the performance ' + 'of this algorithm.') max_iterations = int(max_iterations) if max_iterations <= 0: @@ -109,8 +118,13 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, img = w_line*img + w_edge*edge[0] # Interpolate for smoothness: - intp = RectBivariateSpline(np.arange(img.shape[1]), - np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) + if new_scipy: + intp = RectBivariateSpline(np.arange(img.shape[1]), + np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) + else: + intp = np.vectorize(interp2d(np.arange(img.shape[1]), + np.arange(img.shape[0]), img, kind='cubic', copy=False, + bounds_error=False, fill_value=0)) x, y = snake[:, 0].copy(), snake[:, 1].copy() xsave = np.empty((convergence_order, len(x))) @@ -156,13 +170,17 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, A[-2, -4:] = [-1, 3, -3, 1] efree = True - # Only one inversion is needed: + # Only one inversion is needed for implicit spline energy minimization: inv = scipy.linalg.inv(A+gamma*np.eye(n)) - # Explcit time stepping for image energy minimization: + # Explicit time stepping for image energy minimization: for i in xrange(max_iterations): - fx = intp(x, y, dx=1, grid=False) - fy = intp(x, y, dy=1, grid=False) + if new_scipy: + fx = intp(x, y, dx=1, grid=False) + fy = intp(x, y, dy=1, grid=False) + else: + fx = intp(x, y, dx=1) + fy = intp(x, y, dy=1) if sfixed: fx[0] = 0 fy[0] = 0 @@ -190,7 +208,8 @@ def active_contour_model(image, snake, alpha=0.01, beta=0.1, x[:] += dx y[:] += dy - # Convergence criteria: + # Convergence criteria needs to compare to a number of previous + # configurations since oscillations can occur. j = i%(convergence_order+1) if j < convergence_order: xsave[j, :] = x diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index fa30ad3a..b53995bd 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -2,7 +2,7 @@ import numpy as np from skimage import data from skimage.color import rgb2gray from skimage.filters import gaussian_filter -from skimage.segmentation import active_contour_model +from skimage.segmentation import active_contour from numpy.testing import assert_equal, assert_allclose, assert_raises def test_periodic_reference(): @@ -12,7 +12,7 @@ def test_periodic_reference(): x = 220 + 100*np.cos(s) y = 100 + 100*np.sin(s) init = np.array([x, y]).T - snake = active_contour_model(gaussian_filter(img, 3), init, + snake = active_contour(gaussian_filter(img, 3), init, alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001) refx = [299, 298, 298, 298, 298, 297, 297, 296, 296, 295] refy = [98, 99, 100, 101, 102, 103, 104, 105, 106, 108] @@ -25,7 +25,7 @@ def test_fixed_reference(): x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T - snake = active_contour_model(gaussian_filter(img, 1), init, bc='fixed', + snake = active_contour(gaussian_filter(img, 1), init, bc='fixed', alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) refx = [5, 9, 13, 17, 21, 25, 30, 34, 38, 42] refy = [136, 135, 134, 133, 132, 131, 129, 128, 127, 125] @@ -38,7 +38,7 @@ def test_free_reference(): x = np.linspace(5, 424, 100) y = np.linspace(70, 40, 100) init = np.array([x, y]).T - snake = active_contour_model(gaussian_filter(img, 3), init, bc='free', + snake = active_contour(gaussian_filter(img, 3), init, bc='free', alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) refx = [10, 13, 16, 19, 23, 26, 29, 32, 36, 39] refy = [76, 76, 75, 74, 73, 72, 71, 70, 69, 69] @@ -57,17 +57,17 @@ def test_RGB(): x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T - snake = active_contour_model(imgR, init, bc='fixed', + snake = active_contour(imgR, init, bc='fixed', alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) refx = [5, 9, 13, 17, 21, 25, 30, 34, 38, 42] refy = [136, 135, 134, 133, 132, 131, 129, 128, 127, 125] assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - snake = active_contour_model(imgG, init, bc='fixed', + snake = active_contour(imgG, init, bc='fixed', alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - snake = active_contour_model(imgRGB, init, bc='fixed', + snake = active_contour(imgRGB, init, bc='fixed', alpha=0.1, beta=1.0, w_line=-5/3., w_edge=0, gamma=0.1) assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) @@ -80,15 +80,15 @@ def test_end_points(): x = 220 + 100*np.cos(s) y = 100 + 100*np.sin(s) init = np.array([x, y]).T - snake = active_contour_model(gaussian_filter(img, 3), init, + snake = active_contour(gaussian_filter(img, 3), init, bc='periodic', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, max_iterations=100) assert np.sum(np.abs(snake[0, :]-snake[-1, :])) < 2 - snake = active_contour_model(gaussian_filter(img, 3), init, + snake = active_contour(gaussian_filter(img, 3), init, bc='free', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, max_iterations=100) assert np.sum(np.abs(snake[0, :]-snake[-1, :])) > 2 - snake = active_contour_model(gaussian_filter(img, 3), init, + snake = active_contour(gaussian_filter(img, 3), init, bc='fixed', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, max_iterations=100) assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) @@ -99,9 +99,9 @@ def test_bad_input(): x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T - assert_raises(ValueError, active_contour_model, img, init, + assert_raises(ValueError, active_contour, img, init, bc='wrong') - assert_raises(ValueError, active_contour_model, img, init, + assert_raises(ValueError, active_contour, img, init, max_iterations=-15) From 5c20ef721861bb60c6e3c12ff236ca4c9293274a Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Wed, 9 Sep 2015 15:44:35 +0100 Subject: [PATCH 08/14] pep8 and py3 compliance. more comments --- doc/examples/plot_active_contours.py | 11 ++-- skimage/segmentation/__init__.py | 2 +- skimage/segmentation/active_contour_model.py | 52 +++++++++++-------- .../tests/test_active_contour_model.py | 15 +++--- 4 files changed, 46 insertions(+), 34 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index 911bc174..c4f30c04 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -1,7 +1,8 @@ """ -==================================================== +==================== Active Contour Model -==================================================== +==================== + The active contour model is a method to fit open or closed splines to lines or edges in an image. It works by minimising an energy that is in part defined by the image and part by the spline's shape: length and smoothness. The @@ -18,8 +19,8 @@ smooth images a bit before analyzing, as done in the following examples. International Journal of Computer Vision 1 (4): 321 (1988). We initialize a circle around the astronaut's face and use the default boundary -condition `bc='periodic'` to fit a closed curve. The default parameters -`w_line=0, w_edge=1` will make the curve search towards edges, such as the +condition ``bc='periodic'`` to fit a closed curve. The default parameters +``w_line=0, w_edge=1`` will make the curve search towards edges, such as the boundaries of the face. """ @@ -39,7 +40,7 @@ y = 100 + 100*np.sin(s) init = np.array([x, y]).T snake = active_contour(gaussian_filter(img, 3), - init, alpha=0.015, beta=10, gamma=0.001) + init, alpha=0.015, beta=10, gamma=0.001) fig = plt.figure(figsize=(7, 7)) ax = fig.add_subplot(111) diff --git a/skimage/segmentation/__init__.py b/skimage/segmentation/__init__.py index 103ca5a4..63f7d66d 100644 --- a/skimage/segmentation/__init__.py +++ b/skimage/segmentation/__init__.py @@ -9,7 +9,7 @@ from ._join import join_segmentations, relabel_from_one, relabel_sequential __all__ = ['random_walker', - 'active_contour', + 'active_contour', 'felzenszwalb', 'slic', 'quickshift', diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index c6ee27f2..b4d61904 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -5,6 +5,7 @@ import scipy.linalg from scipy.interpolate import RectBivariateSpline, interp2d from skimage.filters import sobel + def active_contour(image, snake, alpha=0.01, beta=0.1, w_line=0, w_edge=1, gamma=0.01, bc='periodic', max_px_move=1.0, @@ -18,27 +19,29 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, Parameters ---------- image : (N, M) or (N, M, 3) ndarray - Input image + Input image. snake : (N, 2) ndarray - Initialisation of snake. + Initialisation coordinates of snake. For periodic snakes, it should + not include duplicate endpoints. alpha : float, optional - Snake length shape parameter + Snake length shape parameter. Higher values makes snake contract + faster. beta : float, optional - Snake smoothness shape parameter + Snake smoothness shape parameter. Higher values makes snake smoother. w_line : float, optional Controls attraction to brightness. Use negative values to attract to - dark regions + dark regions. w_edge : float, optional Controls attraction to edges. Use negative values to repel snake from edges. gamma : float, optional - Excpliti time stepping parameter. + Explicit time stepping parameter. bc : {'periodic', 'free', 'fixed'}, optional Boundary conditions for worm. 'periodic' attaches the two ends of the - snake, 'fixed' holds the end-points in place, and'free' allows free - movement of the ends. 'fixed' and 'free' can be combined by parsing - 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' - yields same behaviour as 'fixed' and 'free', respectively. + snake, 'fixed' holds the end-points in place, and'free' allows free + movement of the ends. 'fixed' and 'free' can be combined by parsing + 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' + yields same behaviour as 'fixed' and 'free', respectively. max_px_move : float, optional Maximum pixel distance to move per iteration. max_iterations : int, optional @@ -54,29 +57,36 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, References ---------- .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour - models". International Journal of Computer Vision 1 (4): 321 (1988). + models". International Journal of Computer Vision 1 (4): 321 + (1988). Examples -------- >>> from skimage.draw import circle_perimeter >>> from skimage.filters import gaussian_filter - >>> # Create and smooth image: + + Create and smooth image: + >>> img = np.zeros((100, 100)) >>> rr, cc = circle_perimeter(35, 45, 25) >>> img[rr, cc] = 1 >>> img = gaussian_filter(img, 2) - >>>> # Initiliaze spline: + + Initiliaze spline: + >>> s = np.linspace(0, 2*np.pi,100) >>> init = 50*np.array([np.cos(s), np.sin(s)]).T+50 - >>> # Fit spline to image: + + Fit spline to image: + >>> snake = active_contour_model(img, init, w_edge=0, w_line=1) >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + (35-snake[:, 1])**2))) 25 """ - scipy_version = map(int, scipy.__version__.split('.')) - new_scipy = scipy_version[0]>0 or \ - (scipy_version[0]==0 and scipy_version[1]>=14) + scipy_version = list(map(int, scipy.__version__.split('.'))) + new_scipy = scipy_version[0] > 0 or \ + (scipy_version[0] == 0 and scipy_version[1] >= 14) if not new_scipy: warnings.warn('You are using an old version of scipy. ' 'Upgrading to a version newer than 0.14.0 ' @@ -130,16 +140,16 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, xsave = np.empty((convergence_order, len(x))) ysave = np.empty((convergence_order, len(x))) - # Build snake shape matrix + # Build snake shape matrix for Euler equation n = len(x) a = np.roll(np.eye(n), -1, axis=0) \ + np.roll(np.eye(n), -1, axis=1) \ - - 2*np.eye(n) + - 2*np.eye(n) # second order derivative, central difference b = np.roll(np.eye(n), -2, axis=0) \ + np.roll(np.eye(n), -2, axis=1) \ - 4*np.roll(np.eye(n), -1, axis=0) \ - 4*np.roll(np.eye(n), -1, axis=1) \ - + 6*np.eye(n) + + 6*np.eye(n) # fourth order derivative, central difference A = -alpha*a + beta*b # Impose boundary conditions different from periodic: @@ -216,7 +226,7 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, ysave[j, :] = y else: dist = np.min(np.max(np.abs(xsave-x[None, :]) - + np.abs(ysave-y[None, :]), 1)) + + np.abs(ysave-y[None, :]), 1)) if dist < convergence: break diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index b53995bd..b95f87d7 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -5,6 +5,7 @@ from skimage.filters import gaussian_filter from skimage.segmentation import active_contour from numpy.testing import assert_equal, assert_allclose, assert_raises + def test_periodic_reference(): img = data.astronaut() img = rgb2gray(img) @@ -13,7 +14,7 @@ def test_periodic_reference(): y = 100 + 100*np.sin(s) init = np.array([x, y]).T snake = active_contour(gaussian_filter(img, 3), init, - alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001) + alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001) refx = [299, 298, 298, 298, 298, 297, 297, 296, 296, 295] refy = [98, 99, 100, 101, 102, 103, 104, 105, 106, 108] assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) @@ -81,16 +82,16 @@ def test_end_points(): y = 100 + 100*np.sin(s) init = np.array([x, y]).T snake = active_contour(gaussian_filter(img, 3), init, - bc='periodic', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, - max_iterations=100) + bc='periodic', alpha=0.015, beta=10, w_line=0, w_edge=1, + gamma=0.001, max_iterations=100) assert np.sum(np.abs(snake[0, :]-snake[-1, :])) < 2 snake = active_contour(gaussian_filter(img, 3), init, - bc='free', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, - max_iterations=100) + bc='free', alpha=0.015, beta=10, w_line=0, w_edge=1, + gamma=0.001, max_iterations=100) assert np.sum(np.abs(snake[0, :]-snake[-1, :])) > 2 snake = active_contour(gaussian_filter(img, 3), init, - bc='fixed', alpha=0.015, beta=10, w_line=0, w_edge=1, gamma=0.001, - max_iterations=100) + bc='fixed', alpha=0.015, beta=10, w_line=0, w_edge=1, + gamma=0.001, max_iterations=100) assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) From 55e12b63522c05622f22ace85d2b7030afd4f09f Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Wed, 28 Oct 2015 19:02:59 +0000 Subject: [PATCH 09/14] py3 fixes and skip test for old scipy. --- skimage/segmentation/active_contour_model.py | 13 ++++++------- .../tests/test_active_contour_model.py | 15 ++++++++++----- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index b4d61904..d5aeb0c3 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -79,7 +79,7 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, Fit spline to image: - >>> snake = active_contour_model(img, init, w_edge=0, w_line=1) + >>> snake = active_contour(img, init, w_edge=0, w_line=1) >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + (35-snake[:, 1])**2))) 25 @@ -88,10 +88,9 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, new_scipy = scipy_version[0] > 0 or \ (scipy_version[0] == 0 and scipy_version[1] >= 14) if not new_scipy: - warnings.warn('You are using an old version of scipy. ' - 'Upgrading to a version newer than 0.14.0 ' - 'will signifcantly improve the performance ' - 'of this algorithm.') + raise NotImplementedError('You are using an old version of scipy. ' + 'Active contours is implemented for scipy versions ' + '0.14.0 and above.') max_iterations = int(max_iterations) if max_iterations <= 0: @@ -112,7 +111,7 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, sobel(img[:, :, 2])] else: edge = [sobel(img)] - for i in xrange(3 if RGB else 1): + for i in range(3 if RGB else 1): edge[i][0, :] = edge[i][1, :] edge[i][-1, :] = edge[i][-2, :] edge[i][:, 0] = edge[i][:, 1] @@ -184,7 +183,7 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, inv = scipy.linalg.inv(A+gamma*np.eye(n)) # Explicit time stepping for image energy minimization: - for i in xrange(max_iterations): + for i in range(max_iterations): if new_scipy: fx = intp(x, y, dx=1, grid=False) fy = intp(x, y, dy=1, grid=False) diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index b95f87d7..3b529f67 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -4,8 +4,13 @@ from skimage.color import rgb2gray from skimage.filters import gaussian_filter from skimage.segmentation import active_contour from numpy.testing import assert_equal, assert_allclose, assert_raises +from numpy.testing.decorators import skipif +scipy_version = list(map(int, scipy.__version__.split('.'))) +new_scipy = scipy_version[0] > 0 or \ + (scipy_version[0] == 0 and scipy_version[1] >= 14) +@skipif(not new_scipy) def test_periodic_reference(): img = data.astronaut() img = rgb2gray(img) @@ -20,7 +25,7 @@ def test_periodic_reference(): assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - +@skipif(not new_scipy) def test_fixed_reference(): img = data.text() x = np.linspace(5, 424, 100) @@ -33,7 +38,7 @@ def test_fixed_reference(): assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - +@skipif(not new_scipy) def test_free_reference(): img = data.text() x = np.linspace(5, 424, 100) @@ -46,7 +51,7 @@ def test_free_reference(): assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - +@skipif(not new_scipy) def test_RGB(): img = gaussian_filter(data.text(), 1) imgR = np.zeros((img.shape[0], img.shape[1], 3)) @@ -73,7 +78,7 @@ def test_RGB(): assert_equal(np.array(snake[:10, 0], dtype=np.int32), refx) assert_equal(np.array(snake[:10, 1], dtype=np.int32), refy) - +@skipif(not new_scipy) def test_end_points(): img = data.astronaut() img = rgb2gray(img) @@ -94,7 +99,7 @@ def test_end_points(): gamma=0.001, max_iterations=100) assert_allclose(snake[0, :], [x[0], y[0]], atol=1e-5) - +@skipif(not new_scipy) def test_bad_input(): img = np.zeros((10, 10)) x = np.linspace(5, 424, 100) From 6f1ec347f4f3b7262adea6bc9b84c4dd20ea40c4 Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Sun, 1 Nov 2015 02:03:27 +0000 Subject: [PATCH 10/14] Fixed missing scipy import --- skimage/segmentation/active_contour_model.py | 2 +- skimage/segmentation/tests/test_active_contour_model.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index d5aeb0c3..12395c14 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -1,11 +1,11 @@ import warnings import numpy as np from skimage import img_as_float +import scipy import scipy.linalg from scipy.interpolate import RectBivariateSpline, interp2d from skimage.filters import sobel - def active_contour(image, snake, alpha=0.01, beta=0.1, w_line=0, w_edge=1, gamma=0.01, bc='periodic', max_px_move=1.0, diff --git a/skimage/segmentation/tests/test_active_contour_model.py b/skimage/segmentation/tests/test_active_contour_model.py index 3b529f67..48a86d45 100644 --- a/skimage/segmentation/tests/test_active_contour_model.py +++ b/skimage/segmentation/tests/test_active_contour_model.py @@ -5,6 +5,7 @@ from skimage.filters import gaussian_filter from skimage.segmentation import active_contour from numpy.testing import assert_equal, assert_allclose, assert_raises from numpy.testing.decorators import skipif +import scipy scipy_version = list(map(int, scipy.__version__.split('.'))) new_scipy = scipy_version[0] > 0 or \ From ea33891c6097c25020488b99abe77e39addc370f Mon Sep 17 00:00:00 2001 From: Julius Bier Kirekgaard Date: Sun, 29 Nov 2015 14:15:10 +0000 Subject: [PATCH 11/14] Skip doctest --- skimage/segmentation/active_contour_model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index 12395c14..1f5d88ce 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -79,8 +79,9 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, Fit spline to image: - >>> snake = active_contour(img, init, w_edge=0, w_line=1) - >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + (35-snake[:, 1])**2))) + >>> snake = active_contour(img, init, w_edge=0, w_line=1) #doctest: +SKIP + >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + + (35-snake[:, 1])**2))) #doctest: +SKIP 25 """ From 320977c8b9d8d1d78852a6f8f2986e06fb0acf68 Mon Sep 17 00:00:00 2001 From: Julius Bier Kirkegaard Date: Tue, 1 Dec 2015 12:42:12 +0000 Subject: [PATCH 12/14] Fixed doctest problem --- skimage/segmentation/active_contour_model.py | 466 +++++++++---------- 1 file changed, 233 insertions(+), 233 deletions(-) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index 1f5d88ce..43904306 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -1,233 +1,233 @@ -import warnings -import numpy as np -from skimage import img_as_float -import scipy -import scipy.linalg -from scipy.interpolate import RectBivariateSpline, interp2d -from skimage.filters import sobel - -def active_contour(image, snake, alpha=0.01, beta=0.1, - w_line=0, w_edge=1, gamma=0.01, - bc='periodic', max_px_move=1.0, - max_iterations=2500, convergence=0.1): - """Active contour model. - - Active contours by fitting snakes to features of images. Supports single - and multichannel 2D images. Snakes can be periodic (for segmentation) or - have fixed and/or free ends. - - Parameters - ---------- - image : (N, M) or (N, M, 3) ndarray - Input image. - snake : (N, 2) ndarray - Initialisation coordinates of snake. For periodic snakes, it should - not include duplicate endpoints. - alpha : float, optional - Snake length shape parameter. Higher values makes snake contract - faster. - beta : float, optional - Snake smoothness shape parameter. Higher values makes snake smoother. - w_line : float, optional - Controls attraction to brightness. Use negative values to attract to - dark regions. - w_edge : float, optional - Controls attraction to edges. Use negative values to repel snake from - edges. - gamma : float, optional - Explicit time stepping parameter. - bc : {'periodic', 'free', 'fixed'}, optional - Boundary conditions for worm. 'periodic' attaches the two ends of the - snake, 'fixed' holds the end-points in place, and'free' allows free - movement of the ends. 'fixed' and 'free' can be combined by parsing - 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' - yields same behaviour as 'fixed' and 'free', respectively. - max_px_move : float, optional - Maximum pixel distance to move per iteration. - max_iterations : int, optional - Maximum iterations to optimize snake shape. - convergence: float, optional - Convergence criteria. - - Returns - ------- - snake : (N, 2) ndarray - Optimised snake, same shape as input parameter. - - References - ---------- - .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour - models". International Journal of Computer Vision 1 (4): 321 - (1988). - - Examples - -------- - >>> from skimage.draw import circle_perimeter - >>> from skimage.filters import gaussian_filter - - Create and smooth image: - - >>> img = np.zeros((100, 100)) - >>> rr, cc = circle_perimeter(35, 45, 25) - >>> img[rr, cc] = 1 - >>> img = gaussian_filter(img, 2) - - Initiliaze spline: - - >>> s = np.linspace(0, 2*np.pi,100) - >>> init = 50*np.array([np.cos(s), np.sin(s)]).T+50 - - Fit spline to image: - - >>> snake = active_contour(img, init, w_edge=0, w_line=1) #doctest: +SKIP - >>> int(np.mean(np.sqrt((45-snake[:, 0])**2 + - (35-snake[:, 1])**2))) #doctest: +SKIP - 25 - - """ - scipy_version = list(map(int, scipy.__version__.split('.'))) - new_scipy = scipy_version[0] > 0 or \ - (scipy_version[0] == 0 and scipy_version[1] >= 14) - if not new_scipy: - raise NotImplementedError('You are using an old version of scipy. ' - 'Active contours is implemented for scipy versions ' - '0.14.0 and above.') - - max_iterations = int(max_iterations) - if max_iterations <= 0: - raise ValueError("max_iterations should be >0.") - convergence_order = 10 - valid_bcs = ['periodic', 'free', 'fixed', 'free-fixed', - 'fixed-free', 'fixed-fixed', 'free-free'] - if bc not in valid_bcs: - raise ValueError("Invalid boundary condition.\n"+ - "Should be one of: "+", ".join(valid_bcs)+'.') - img = img_as_float(image) - RGB = img.ndim == 3 - - # Find edges using sobel: - if w_edge != 0: - if RGB: - edge = [sobel(img[:, :, 0]), sobel(img[:, :, 1]), - sobel(img[:, :, 2])] - else: - edge = [sobel(img)] - for i in range(3 if RGB else 1): - edge[i][0, :] = edge[i][1, :] - edge[i][-1, :] = edge[i][-2, :] - edge[i][:, 0] = edge[i][:, 1] - edge[i][:, -1] = edge[i][:, -2] - else: - edge = [0] - - # Superimpose intensity and edge images: - if RGB: - img = w_line*np.sum(img, axis=2) \ - + w_edge*sum(edge) - else: - img = w_line*img + w_edge*edge[0] - - # Interpolate for smoothness: - if new_scipy: - intp = RectBivariateSpline(np.arange(img.shape[1]), - np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) - else: - intp = np.vectorize(interp2d(np.arange(img.shape[1]), - np.arange(img.shape[0]), img, kind='cubic', copy=False, - bounds_error=False, fill_value=0)) - - x, y = snake[:, 0].copy(), snake[:, 1].copy() - xsave = np.empty((convergence_order, len(x))) - ysave = np.empty((convergence_order, len(x))) - - # Build snake shape matrix for Euler equation - n = len(x) - a = np.roll(np.eye(n), -1, axis=0) \ - + np.roll(np.eye(n), -1, axis=1) \ - - 2*np.eye(n) # second order derivative, central difference - b = np.roll(np.eye(n), -2, axis=0) \ - + np.roll(np.eye(n), -2, axis=1) \ - - 4*np.roll(np.eye(n), -1, axis=0) \ - - 4*np.roll(np.eye(n), -1, axis=1) \ - + 6*np.eye(n) # fourth order derivative, central difference - A = -alpha*a + beta*b - - # Impose boundary conditions different from periodic: - sfixed = False - if bc.startswith('fixed'): - A[0, :] = 0 - A[1, :] = 0 - A[1, :3] = [1, -2, 1] - sfixed = True - efixed = False - if bc.endswith('fixed'): - A[-1, :] = 0 - A[-2, :] = 0 - A[-2, -3:] = [1, -2, 1] - efixed = True - sfree = False - if bc.startswith('free'): - A[0, :] = 0 - A[0, :3] = [1, -2, 1] - A[1, :] = 0 - A[1, :4] = [-1, 3, -3, 1] - sfree = True - efree = False - if bc.endswith('free'): - A[-1, :] = 0 - A[-1, -3:] = [1, -2, 1] - A[-2, :] = 0 - A[-2, -4:] = [-1, 3, -3, 1] - efree = True - - # Only one inversion is needed for implicit spline energy minimization: - inv = scipy.linalg.inv(A+gamma*np.eye(n)) - - # Explicit time stepping for image energy minimization: - for i in range(max_iterations): - if new_scipy: - fx = intp(x, y, dx=1, grid=False) - fy = intp(x, y, dy=1, grid=False) - else: - fx = intp(x, y, dx=1) - fy = intp(x, y, dy=1) - if sfixed: - fx[0] = 0 - fy[0] = 0 - if efixed: - fx[-1] = 0 - fy[-1] = 0 - if sfree: - fx[0] *= 2 - fy[0] *= 2 - if efree: - fx[-1] *= 2 - fy[-1] *= 2 - xn = np.dot(inv, gamma*x + fx) - yn = np.dot(inv, gamma*y + fy) - - # Movements are capped to max_px_move per iteration: - dx = max_px_move*np.tanh(xn-x) - dy = max_px_move*np.tanh(yn-y) - if sfixed: - dx[0] = 0 - dy[0] = 0 - if efixed: - dx[-1] = 0 - dy[-1] = 0 - x[:] += dx - y[:] += dy - - # Convergence criteria needs to compare to a number of previous - # configurations since oscillations can occur. - j = i%(convergence_order+1) - if j < convergence_order: - xsave[j, :] = x - ysave[j, :] = y - else: - dist = np.min(np.max(np.abs(xsave-x[None, :]) - + np.abs(ysave-y[None, :]), 1)) - if dist < convergence: - break - - return np.array([x, y]).T +import warnings +import numpy as np +from skimage import img_as_float +import scipy +import scipy.linalg +from scipy.interpolate import RectBivariateSpline, interp2d +from skimage.filters import sobel + +def active_contour(image, snake, alpha=0.01, beta=0.1, + w_line=0, w_edge=1, gamma=0.01, + bc='periodic', max_px_move=1.0, + max_iterations=2500, convergence=0.1): + """Active contour model. + + Active contours by fitting snakes to features of images. Supports single + and multichannel 2D images. Snakes can be periodic (for segmentation) or + have fixed and/or free ends. + + Parameters + ---------- + image : (N, M) or (N, M, 3) ndarray + Input image. + snake : (N, 2) ndarray + Initialisation coordinates of snake. For periodic snakes, it should + not include duplicate endpoints. + alpha : float, optional + Snake length shape parameter. Higher values makes snake contract + faster. + beta : float, optional + Snake smoothness shape parameter. Higher values makes snake smoother. + w_line : float, optional + Controls attraction to brightness. Use negative values to attract to + dark regions. + w_edge : float, optional + Controls attraction to edges. Use negative values to repel snake from + edges. + gamma : float, optional + Explicit time stepping parameter. + bc : {'periodic', 'free', 'fixed'}, optional + Boundary conditions for worm. 'periodic' attaches the two ends of the + snake, 'fixed' holds the end-points in place, and'free' allows free + movement of the ends. 'fixed' and 'free' can be combined by parsing + 'fixed-free', 'free-fixed'. Parsing 'fixed-fixed' or 'free-free' + yields same behaviour as 'fixed' and 'free', respectively. + max_px_move : float, optional + Maximum pixel distance to move per iteration. + max_iterations : int, optional + Maximum iterations to optimize snake shape. + convergence: float, optional + Convergence criteria. + + Returns + ------- + snake : (N, 2) ndarray + Optimised snake, same shape as input parameter. + + References + ---------- + .. [1] Kass, M.; Witkin, A.; Terzopoulos, D. "Snakes: Active contour + models". International Journal of Computer Vision 1 (4): 321 + (1988). + + Examples + -------- + >>> from skimage.draw import circle_perimeter + >>> from skimage.filters import gaussian_filter + + Create and smooth image: + + >>> img = np.zeros((100, 100)) + >>> rr, cc = circle_perimeter(35, 45, 25) + >>> img[rr, cc] = 1 + >>> img = gaussian_filter(img, 2) + + Initiliaze spline: + + >>> s = np.linspace(0, 2*np.pi,100) + >>> init = 50*np.array([np.cos(s), np.sin(s)]).T+50 + + Fit spline to image: + + >>> snake = active_contour(img, init, w_edge=0, w_line=1) #doctest: +SKIP + >>> dist = np.sqrt((45-snake[:, 0])**2 +(35-snake[:, 1])**2) #doctest: +SKIP + >>> int(np.mean(dist)) #doctest: +SKIP + 25 + + """ + scipy_version = list(map(int, scipy.__version__.split('.'))) + new_scipy = scipy_version[0] > 0 or \ + (scipy_version[0] == 0 and scipy_version[1] >= 14) + if not new_scipy: + raise NotImplementedError('You are using an old version of scipy. ' + 'Active contours is implemented for scipy versions ' + '0.14.0 and above.') + + max_iterations = int(max_iterations) + if max_iterations <= 0: + raise ValueError("max_iterations should be >0.") + convergence_order = 10 + valid_bcs = ['periodic', 'free', 'fixed', 'free-fixed', + 'fixed-free', 'fixed-fixed', 'free-free'] + if bc not in valid_bcs: + raise ValueError("Invalid boundary condition.\n"+ + "Should be one of: "+", ".join(valid_bcs)+'.') + img = img_as_float(image) + RGB = img.ndim == 3 + + # Find edges using sobel: + if w_edge != 0: + if RGB: + edge = [sobel(img[:, :, 0]), sobel(img[:, :, 1]), + sobel(img[:, :, 2])] + else: + edge = [sobel(img)] + for i in range(3 if RGB else 1): + edge[i][0, :] = edge[i][1, :] + edge[i][-1, :] = edge[i][-2, :] + edge[i][:, 0] = edge[i][:, 1] + edge[i][:, -1] = edge[i][:, -2] + else: + edge = [0] + + # Superimpose intensity and edge images: + if RGB: + img = w_line*np.sum(img, axis=2) \ + + w_edge*sum(edge) + else: + img = w_line*img + w_edge*edge[0] + + # Interpolate for smoothness: + if new_scipy: + intp = RectBivariateSpline(np.arange(img.shape[1]), + np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) + else: + intp = np.vectorize(interp2d(np.arange(img.shape[1]), + np.arange(img.shape[0]), img, kind='cubic', copy=False, + bounds_error=False, fill_value=0)) + + x, y = snake[:, 0].copy(), snake[:, 1].copy() + xsave = np.empty((convergence_order, len(x))) + ysave = np.empty((convergence_order, len(x))) + + # Build snake shape matrix for Euler equation + n = len(x) + a = np.roll(np.eye(n), -1, axis=0) \ + + np.roll(np.eye(n), -1, axis=1) \ + - 2*np.eye(n) # second order derivative, central difference + b = np.roll(np.eye(n), -2, axis=0) \ + + np.roll(np.eye(n), -2, axis=1) \ + - 4*np.roll(np.eye(n), -1, axis=0) \ + - 4*np.roll(np.eye(n), -1, axis=1) \ + + 6*np.eye(n) # fourth order derivative, central difference + A = -alpha*a + beta*b + + # Impose boundary conditions different from periodic: + sfixed = False + if bc.startswith('fixed'): + A[0, :] = 0 + A[1, :] = 0 + A[1, :3] = [1, -2, 1] + sfixed = True + efixed = False + if bc.endswith('fixed'): + A[-1, :] = 0 + A[-2, :] = 0 + A[-2, -3:] = [1, -2, 1] + efixed = True + sfree = False + if bc.startswith('free'): + A[0, :] = 0 + A[0, :3] = [1, -2, 1] + A[1, :] = 0 + A[1, :4] = [-1, 3, -3, 1] + sfree = True + efree = False + if bc.endswith('free'): + A[-1, :] = 0 + A[-1, -3:] = [1, -2, 1] + A[-2, :] = 0 + A[-2, -4:] = [-1, 3, -3, 1] + efree = True + + # Only one inversion is needed for implicit spline energy minimization: + inv = scipy.linalg.inv(A+gamma*np.eye(n)) + + # Explicit time stepping for image energy minimization: + for i in range(max_iterations): + if new_scipy: + fx = intp(x, y, dx=1, grid=False) + fy = intp(x, y, dy=1, grid=False) + else: + fx = intp(x, y, dx=1) + fy = intp(x, y, dy=1) + if sfixed: + fx[0] = 0 + fy[0] = 0 + if efixed: + fx[-1] = 0 + fy[-1] = 0 + if sfree: + fx[0] *= 2 + fy[0] *= 2 + if efree: + fx[-1] *= 2 + fy[-1] *= 2 + xn = np.dot(inv, gamma*x + fx) + yn = np.dot(inv, gamma*y + fy) + + # Movements are capped to max_px_move per iteration: + dx = max_px_move*np.tanh(xn-x) + dy = max_px_move*np.tanh(yn-y) + if sfixed: + dx[0] = 0 + dy[0] = 0 + if efixed: + dx[-1] = 0 + dy[-1] = 0 + x[:] += dx + y[:] += dy + + # Convergence criteria needs to compare to a number of previous + # configurations since oscillations can occur. + j = i%(convergence_order+1) + if j < convergence_order: + xsave[j, :] = x + ysave[j, :] = y + else: + dist = np.min(np.max(np.abs(xsave-x[None, :]) + + np.abs(ysave-y[None, :]), 1)) + if dist < convergence: + break + + return np.array([x, y]).T From a16c530322576f328d79d5b4681c16d1343ce3c4 Mon Sep 17 00:00:00 2001 From: emmanuelle Date: Fri, 11 Dec 2015 17:21:17 +0100 Subject: [PATCH 13/14] Some minor PEP8 issues --- doc/examples/plot_active_contours.py | 4 +-- skimage/segmentation/active_contour_model.py | 33 ++++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index c4f30c04..bfcd0b43 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -40,7 +40,7 @@ y = 100 + 100*np.sin(s) init = np.array([x, y]).T snake = active_contour(gaussian_filter(img, 3), - init, alpha=0.015, beta=10, gamma=0.001) + init, alpha=0.015, beta=10, gamma=0.001) fig = plt.figure(figsize=(7, 7)) ax = fig.add_subplot(111) @@ -67,7 +67,7 @@ y = np.linspace(136, 50, 100) init = np.array([x, y]).T snake = active_contour(gaussian_filter(img, 1), init, bc='fixed', - alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) + alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) fig = plt.figure(figsize=(9, 5)) ax = fig.add_subplot(111) diff --git a/skimage/segmentation/active_contour_model.py b/skimage/segmentation/active_contour_model.py index 43904306..b2f89b3b 100644 --- a/skimage/segmentation/active_contour_model.py +++ b/skimage/segmentation/active_contour_model.py @@ -1,4 +1,3 @@ -import warnings import numpy as np from skimage import img_as_float import scipy @@ -6,6 +5,7 @@ import scipy.linalg from scipy.interpolate import RectBivariateSpline, interp2d from skimage.filters import sobel + def active_contour(image, snake, alpha=0.01, beta=0.1, w_line=0, w_edge=1, gamma=0.01, bc='periodic', max_px_move=1.0, @@ -100,7 +100,7 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, valid_bcs = ['periodic', 'free', 'fixed', 'free-fixed', 'fixed-free', 'fixed-fixed', 'free-free'] if bc not in valid_bcs: - raise ValueError("Invalid boundary condition.\n"+ + raise ValueError("Invalid boundary condition.\n" + "Should be one of: "+", ".join(valid_bcs)+'.') img = img_as_float(image) RGB = img.ndim == 3 @@ -130,11 +130,12 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, # Interpolate for smoothness: if new_scipy: intp = RectBivariateSpline(np.arange(img.shape[1]), - np.arange(img.shape[0]), img.T, kx=2, ky=2, s=0) + np.arange(img.shape[0]), + img.T, kx=2, ky=2, s=0) else: intp = np.vectorize(interp2d(np.arange(img.shape[1]), - np.arange(img.shape[0]), img, kind='cubic', copy=False, - bounds_error=False, fill_value=0)) + np.arange(img.shape[0]), img, kind='cubic', + copy=False, bounds_error=False, fill_value=0)) x, y = snake[:, 0].copy(), snake[:, 1].copy() xsave = np.empty((convergence_order, len(x))) @@ -142,14 +143,14 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, # Build snake shape matrix for Euler equation n = len(x) - a = np.roll(np.eye(n), -1, axis=0) \ - + np.roll(np.eye(n), -1, axis=1) \ - - 2*np.eye(n) # second order derivative, central difference - b = np.roll(np.eye(n), -2, axis=0) \ - + np.roll(np.eye(n), -2, axis=1) \ - - 4*np.roll(np.eye(n), -1, axis=0) \ - - 4*np.roll(np.eye(n), -1, axis=1) \ - + 6*np.eye(n) # fourth order derivative, central difference + a = np.roll(np.eye(n), -1, axis=0) + \ + np.roll(np.eye(n), -1, axis=1) - \ + 2*np.eye(n) # second order derivative, central difference + b = np.roll(np.eye(n), -2, axis=0) + \ + np.roll(np.eye(n), -2, axis=1) - \ + 4*np.roll(np.eye(n), -1, axis=0) - \ + 4*np.roll(np.eye(n), -1, axis=1) + \ + 6*np.eye(n) # fourth order derivative, central difference A = -alpha*a + beta*b # Impose boundary conditions different from periodic: @@ -220,13 +221,13 @@ def active_contour(image, snake, alpha=0.01, beta=0.1, # Convergence criteria needs to compare to a number of previous # configurations since oscillations can occur. - j = i%(convergence_order+1) + j = i % (convergence_order+1) if j < convergence_order: xsave[j, :] = x ysave[j, :] = y else: - dist = np.min(np.max(np.abs(xsave-x[None, :]) - + np.abs(ysave-y[None, :]), 1)) + dist = np.min(np.max(np.abs(xsave-x[None, :]) + + np.abs(ysave-y[None, :]), 1)) if dist < convergence: break From 15d09f91b8b511263cca2548e30d89ac6da6b6db Mon Sep 17 00:00:00 2001 From: emmanuelle Date: Sat, 12 Dec 2015 16:14:17 +0100 Subject: [PATCH 14/14] Avoids crash of active contour example when run with old scipy version --- doc/examples/plot_active_contours.py | 54 +++++++++++++++++----------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/doc/examples/plot_active_contours.py b/doc/examples/plot_active_contours.py index bfcd0b43..a8cf7432 100644 --- a/doc/examples/plot_active_contours.py +++ b/doc/examples/plot_active_contours.py @@ -31,6 +31,13 @@ from skimage import data from skimage.filters import gaussian_filter from skimage.segmentation import active_contour +# Test scipy version, since active contour is only possible +# with recent scipy version +import scipy +scipy_version = list(map(int, scipy.__version__.split('.'))) +new_scipy = scipy_version[0] > 0 or \ + (scipy_version[0] == 0 and scipy_version[1] >= 14) + img = data.astronaut() img = rgb2gray(img) @@ -39,17 +46,23 @@ x = 220 + 100*np.cos(s) y = 100 + 100*np.sin(s) init = np.array([x, y]).T -snake = active_contour(gaussian_filter(img, 3), - init, alpha=0.015, beta=10, gamma=0.001) +if not new_scipy: + print('You are using an old version of scipy. ' + 'Active contours is implemented for scipy versions ' + '0.14.0 and above.') -fig = plt.figure(figsize=(7, 7)) -ax = fig.add_subplot(111) -plt.gray() -ax.imshow(img) -ax.plot(init[:, 0], init[:, 1], '--r') -ax.plot(snake[:, 0], snake[:, 1], '-b') -ax.set_xticks([]), ax.set_yticks([]) -ax.axis([0, img.shape[1], img.shape[0], 0]) +if new_scipy: + snake = active_contour(gaussian_filter(img, 3), + init, alpha=0.015, beta=10, gamma=0.001) + + fig = plt.figure(figsize=(7, 7)) + ax = fig.add_subplot(111) + plt.gray() + ax.imshow(img) + ax.plot(init[:, 0], init[:, 1], '--r') + ax.plot(snake[:, 0], snake[:, 1], '-b') + ax.set_xticks([]), ax.set_yticks([]) + ax.axis([0, img.shape[1], img.shape[0], 0]) """ .. image:: PLOT2RST.current_figure @@ -66,17 +79,18 @@ x = np.linspace(5, 424, 100) y = np.linspace(136, 50, 100) init = np.array([x, y]).T -snake = active_contour(gaussian_filter(img, 1), init, bc='fixed', - alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) +if new_scipy: + snake = active_contour(gaussian_filter(img, 1), init, bc='fixed', + alpha=0.1, beta=1.0, w_line=-5, w_edge=0, gamma=0.1) -fig = plt.figure(figsize=(9, 5)) -ax = fig.add_subplot(111) -plt.gray() -ax.imshow(img) -ax.plot(init[:, 0], init[:, 1], '--r') -ax.plot(snake[:, 0], snake[:, 1], '-b') -ax.set_xticks([]), ax.set_yticks([]) -ax.axis([0, img.shape[1], img.shape[0], 0]) + fig = plt.figure(figsize=(9, 5)) + ax = fig.add_subplot(111) + plt.gray() + ax.imshow(img) + ax.plot(init[:, 0], init[:, 1], '--r') + ax.plot(snake[:, 0], snake[:, 1], '-b') + ax.set_xticks([]), ax.set_yticks([]) + ax.axis([0, img.shape[1], img.shape[0], 0]) plt.show()