diff --git a/skimage/__init__.py b/skimage/__init__.py index bcdaf2b0..a3deb6c6 100644 --- a/skimage/__init__.py +++ b/skimage/__init__.py @@ -61,8 +61,8 @@ try: except ImportError: __version__ = "unbuilt-dev" + def _setup_test(verbose=False): - import gzip import functools args = ['', '--exe', '-w', pkg_dir] @@ -93,6 +93,7 @@ if test_verbose is None: except NameError: pass + def get_log(name=None): """Return a console logger. @@ -120,11 +121,13 @@ def get_log(name=None): log = logging.getLogger(name) return log + def _setup_log(): """Configure root logger. """ - import logging, sys + import logging + import sys log = logging.getLogger() diff --git a/skimage/_build.py b/skimage/_build.py index dbf5e678..8f255f29 100644 --- a/skimage/_build.py +++ b/skimage/_build.py @@ -1,9 +1,8 @@ import sys import os -import shutil import hashlib import subprocess -import platform + def cython(pyx_files, working_path=''): """Use Cython to convert the given files to C. @@ -39,17 +38,18 @@ def cython(pyx_files, working_path=''): print(cmd) try: - status = subprocess.call(['cython', '-o', c_file, pyxfile]) + subprocess.call(['cython', '-o', c_file, pyxfile]) except WindowsError: # On Windows cython.exe may be missing if Cython was installed # via distutils. Run the cython.py script instead. - status = subprocess.call( + subprocess.call( [sys.executable, os.path.join(os.path.dirname(sys.executable), 'Scripts', 'cython.py'), '-o', c_file, pyxfile], shell=True) + def _md5sum(f): m = hashlib.new('md5') while True: @@ -60,6 +60,7 @@ def _md5sum(f): m.update(d) return m.hexdigest() + def _changed(filename): """Compare the hash of a Cython file to the cached hash value on disk. diff --git a/skimage/color/colorconv.py b/skimage/color/colorconv.py index 36165d68..274581b5 100644 --- a/skimage/color/colorconv.py +++ b/skimage/color/colorconv.py @@ -485,7 +485,7 @@ def rgb2grey(rgb): Raises ------ ValueError - If `rgb2grey` is not a 3-D array of shape (.., .., 3) or + If `rgb2grey` is not a 3-D array of shape (.., .., 3) or (.., .., 4). References diff --git a/skimage/color/tests/test_colorconv.py b/skimage/color/tests/test_colorconv.py index be0d330c..84c921b8 100644 --- a/skimage/color/tests/test_colorconv.py +++ b/skimage/color/tests/test_colorconv.py @@ -57,8 +57,7 @@ class TestColorconv(TestCase): self.assertRaises(ValueError, rgb2hsv, self.img_grayscale) def test_rgb2hsv_error_one_element(self): - self.assertRaises(ValueError, rgb2hsv, self.img_rgb[0,0]) - + self.assertRaises(ValueError, rgb2hsv, self.img_rgb[0, 0]) # HSV to RGB def test_hsv2rgb_conversion(self): @@ -74,19 +73,18 @@ class TestColorconv(TestCase): self.assertRaises(ValueError, hsv2rgb, self.img_grayscale) def test_hsv2rgb_error_one_element(self): - self.assertRaises(ValueError, hsv2rgb, self.img_rgb[0,0]) - + self.assertRaises(ValueError, hsv2rgb, self.img_rgb[0, 0]) # RGB to XYZ def test_rgb2xyz_conversion(self): - gt = np.array([[[ 0.950456, 1. , 1.088754], - [ 0.538003, 0.787329, 1.06942 ], - [ 0.592876, 0.28484 , 0.969561], - [ 0.180423, 0.072169, 0.950227]], - [[ 0.770033, 0.927831, 0.138527], - [ 0.35758 , 0.71516 , 0.119193], - [ 0.412453, 0.212671, 0.019334], - [ 0. , 0. , 0. ]]]) + gt = np.array([[[0.950456, 1. , 1.088754], + [0.538003, 0.787329, 1.06942 ], + [0.592876, 0.28484 , 0.969561], + [0.180423, 0.072169, 0.950227]], + [[0.770033, 0.927831, 0.138527], + [0.35758 , 0.71516 , 0.119193], + [0.412453, 0.212671, 0.019334], + [0. , 0. , 0. ]]]) assert_almost_equal(rgb2xyz(self.colbars_array), gt) # stop repeating the "raises" checks for all other functions that are @@ -95,8 +93,7 @@ class TestColorconv(TestCase): self.assertRaises(ValueError, rgb2xyz, self.img_grayscale) def test_rgb2xyz_error_one_element(self): - self.assertRaises(ValueError, rgb2xyz, self.img_rgb[0,0]) - + self.assertRaises(ValueError, rgb2xyz, self.img_rgb[0, 0]) # XYZ to RGB def test_xyz2rgb_conversion(self): @@ -104,7 +101,6 @@ class TestColorconv(TestCase): assert_almost_equal(xyz2rgb(rgb2xyz(self.colbars_array)), self.colbars_array) - # RGB to RGB CIE def test_rgb2rgbcie_conversion(self): gt = np.array([[[ 0.1488856 , 0.18288098, 0.19277574], @@ -117,7 +113,6 @@ class TestColorconv(TestCase): [ 0. , 0. , 0. ]]]) assert_almost_equal(rgb2rgbcie(self.colbars_array), gt) - # RGB CIE to RGB def test_rgbcie2rgb_conversion(self): # only roundtrip test, we checked rgb2rgbcie above already @@ -151,6 +146,7 @@ class TestColorconv(TestCase): assert_equal(g.shape, (1, 1)) + def test_gray2rgb(): x = np.array([0, 0.5, 1]) assert_raises(ValueError, gray2rgb, x) @@ -170,4 +166,3 @@ def test_gray2rgb(): if __name__ == "__main__": run_module_suite() - diff --git a/skimage/data/__init__.py b/skimage/data/__init__.py index c4467678..7e351293 100644 --- a/skimage/data/__init__.py +++ b/skimage/data/__init__.py @@ -11,6 +11,7 @@ import os as _os from ..io import imread from skimage import data_dir + def load(f): """Load an image file located in the data directory. @@ -26,6 +27,7 @@ def load(f): """ return imread(_os.path.join(data_dir, f)) + def camera(): """Gray-level "camera" image, often used for segmentation and denoising examples. @@ -33,6 +35,7 @@ def camera(): """ return load("camera.png") + def lena(): """Colour "Lena" image. @@ -44,8 +47,9 @@ def lena(): """ return load("lena.png") + def text(): - """ Gray-level "text" image used for corner detection. + """ Gray-level "text" image used for corner detection. Notes ----- @@ -56,7 +60,8 @@ def text(): """ - return load("text.png") + return load("text.png") + def checkerboard(): """Checkerboard image. @@ -68,6 +73,7 @@ def checkerboard(): """ return load("chessboard_GRAY.png") + def coins(): """Greek coins from Pompeii. @@ -88,6 +94,7 @@ def coins(): """ return load("coins.png") + def moon(): """Surface of the moon. @@ -97,6 +104,7 @@ def moon(): """ return load("moon.png") + def page(): """Scanned page. diff --git a/skimage/data/tests/test_data.py b/skimage/data/tests/test_data.py index c239d880..1d85802b 100644 --- a/skimage/data/tests/test_data.py +++ b/skimage/data/tests/test_data.py @@ -1,22 +1,23 @@ import skimage.data as data -from numpy.testing import assert_equal, assert_array_equal -import numpy as np +from numpy.testing import assert_equal + def test_lena(): """ Test that "Lena" image can be loaded. """ lena = data.lena() assert_equal(lena.shape, (512, 512, 3)) + def test_camera(): """ Test that "camera" image can be loaded. """ cameraman = data.camera() assert_equal(cameraman.ndim, 2) + def test_checkerboard(): """ Test that checkerboard image can be loaded. """ - checkerboard = data.checkerboard() + data.checkerboard() if __name__ == "__main__": from numpy.testing import run_module_suite run_module_suite() - diff --git a/skimage/draw/setup.py b/skimage/draw/setup.py index d296fee8..4503d664 100644 --- a/skimage/draw/setup.py +++ b/skimage/draw/setup.py @@ -5,6 +5,7 @@ from skimage._build import cython base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -20,11 +21,11 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image developers', - author = 'scikits-image developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Drawing', - url = 'https://github.com/scikits-image/scikits-image', - license = 'SciPy License (BSD Style)', + setup(maintainer='scikits-image developers', + author='scikits-image developers', + maintainer_email='scikits-image@googlegroups.com', + description='Drawing', + url='https://github.com/scikits-image/scikits-image', + license='SciPy License (BSD Style)', **(configuration(top_path='').todict()) ) diff --git a/skimage/draw/tests/test_draw.py b/skimage/draw/tests/test_draw.py index dd1c81e2..9e6ca8e8 100644 --- a/skimage/draw/tests/test_draw.py +++ b/skimage/draw/tests/test_draw.py @@ -15,6 +15,7 @@ def test_line_horizontal(): assert_array_equal(img, img_) + def test_line_vertical(): img = np.zeros((10, 10)) @@ -26,6 +27,7 @@ def test_line_vertical(): assert_array_equal(img, img_) + def test_line_reverse(): img = np.zeros((10, 10)) @@ -37,6 +39,7 @@ def test_line_reverse(): assert_array_equal(img, img_) + def test_line_diag(): img = np.zeros((5, 5)) @@ -52,20 +55,21 @@ def test_polygon_rectangle(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, 1), (4, 1), (4, 4), (1, 4), (1, 1))) - rr, cc = polygon(poly[:,0], poly[:,1]) - img[rr,cc] = 1 + rr, cc = polygon(poly[:, 0], poly[:, 1]) + img[rr, cc] = 1 img_ = np.zeros((10, 10)) - img_[1:4,1:4] = 1 + img_[1:4, 1:4] = 1 assert_array_equal(img, img_) + def test_polygon_rectangle_angular(): img = np.zeros((10, 10), 'uint8') poly = np.array(((0, 3), (4, 7), (7, 4), (3, 0), (0, 3))) - rr, cc = polygon(poly[:,0], poly[:,1]) - img[rr,cc] = 1 + rr, cc = polygon(poly[:, 0], poly[:, 1]) + img[rr, cc] = 1 img_ = np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -82,12 +86,13 @@ def test_polygon_rectangle_angular(): assert_array_equal(img, img_) + def test_polygon_parallelogram(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, 1), (5, 1), (7, 6), (3, 6), (1, 1))) - rr, cc = polygon(poly[:,0], poly[:,1]) - img[rr,cc] = 1 + rr, cc = polygon(poly[:, 0], poly[:, 1]) + img[rr, cc] = 1 img_ = np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -104,23 +109,25 @@ def test_polygon_parallelogram(): assert_array_equal(img, img_) + def test_polygon_exceed(): img = np.zeros((10, 10), 'uint8') poly = np.array(((1, -1), (100, -1), (100, 100), (1, 100), (1, 1))) - rr, cc = polygon(poly[:,0], poly[:,1], img.shape) - img[rr,cc] = 1 + rr, cc = polygon(poly[:, 0], poly[:, 1], img.shape) + img[rr, cc] = 1 img_ = np.zeros((10, 10)) - img_[1:,:] = 1 + img_[1:, :] = 1 assert_array_equal(img, img_) + def test_circle(): img = np.zeros((15, 15), 'uint8') rr, cc = circle(7, 7, 6) - img[rr,cc] = 1 + img[rr, cc] = 1 img_ = np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -142,11 +149,12 @@ def test_circle(): assert_array_equal(img, img_) + def test_ellipse(): img = np.zeros((15, 15), 'uint8') rr, cc = ellipse(7, 7, 3, 7) - img[rr,cc] = 1 + img[rr, cc] = 1 img_ = np.array( [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], diff --git a/skimage/exposure/exposure.py b/skimage/exposure/exposure.py index a0a576d6..5a722308 100644 --- a/skimage/exposure/exposure.py +++ b/skimage/exposure/exposure.py @@ -183,4 +183,3 @@ def rescale_intensity(image, in_range=None, out_range=None): image = (image - imin) / float(imax - imin) return dtype(image * (omax - omin) + omin) - diff --git a/skimage/exposure/tests/test_exposure.py b/skimage/exposure/tests/test_exposure.py index 55fae5ae..b6ed0b12 100644 --- a/skimage/exposure/tests/test_exposure.py +++ b/skimage/exposure/tests/test_exposure.py @@ -74,4 +74,3 @@ def test_rescale_out_range(): if __name__ == '__main__': from numpy import testing testing.run_module_suite() - diff --git a/skimage/feature/greycomatrix.py b/skimage/feature/greycomatrix.py index 5b4bdb85..5b2b92db 100644 --- a/skimage/feature/greycomatrix.py +++ b/skimage/feature/greycomatrix.py @@ -4,7 +4,6 @@ properties to characterize image textures. """ import numpy as np -import skimage.util from ._greycomatrix import _glcm_loop @@ -28,17 +27,17 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, levels : int, optional The input image should contain integers in [0, levels-1], where levels indicate the number of grey-levels counted - (typically 256 for an 8-bit image). The maximum value is - 256. + (typically 256 for an 8-bit image). The maximum value is + 256. symmetric : bool, optional - If True, the output matrix `P[:, :, d, theta]` is symmetric. This - is accomplished by ignoring the order of value pairs, so both - (i, j) and (j, i) are accumulated when (i, j) is encountered - for a given offset. The default is False. + If True, the output matrix `P[:, :, d, theta]` is symmetric. This + is accomplished by ignoring the order of value pairs, so both + (i, j) and (j, i) are accumulated when (i, j) is encountered + for a given offset. The default is False. normed : bool, optional - If True, normalize each matrix `P[:, :, d, theta]` by dividing + If True, normalize each matrix `P[:, :, d, theta]` by dividing by the total number of accumulated co-occurrences for the given - offset. The elements of the resulting matrix sum to 1. The + offset. The elements of the resulting matrix sum to 1. The default is False. Returns @@ -54,10 +53,10 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, ---------- .. [1] The GLCM Tutorial Home Page, http://www.fp.ucalgary.ca/mhallbey/tutorial.htm - .. [2] Pattern Recognition Engineering, Morton Nadler & Eric P. + .. [2] Pattern Recognition Engineering, Morton Nadler & Eric P. Smith .. [3] Wikipedia, http://en.wikipedia.org/wiki/Co-occurrence_matrix - + Examples -------- @@ -74,7 +73,7 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, [0, 2, 0, 0], [0, 0, 3, 1], [0, 0, 0, 1]], dtype=uint32) - >>> result[:, :, 0, 1] + >>> result[:, :, 0, 1] array([[3, 0, 2, 0], [0, 2, 2, 0], [0, 0, 1, 2], @@ -82,7 +81,7 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, """ - assert levels <= 256 + assert levels <= 256 image = np.ascontiguousarray(image) assert image.ndim == 2 assert image.min() >= 0 @@ -95,7 +94,7 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, P = np.zeros((levels, levels, len(distances), len(angles)), dtype=np.uint32, order='C') - + # count co-occurences _glcm_loop(image, distances, angles, levels, P) @@ -103,8 +102,7 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, if symmetric: Pt = np.transpose(P, (1, 0, 2, 3)) P = P + Pt - - + # normalize each GLMC if normed: P = P.astype(np.float64) @@ -117,25 +115,25 @@ def greycomatrix(image, distances, angles, levels=256, symmetric=False, def greycoprops(P, prop='contrast'): """Calculate texture properties of a GLCM. - - Compute a feature of a grey level co-occurrence matrix to serve as + + Compute a feature of a grey level co-occurrence matrix to serve as a compact summary of the matrix. The properties are computed as follows: - 'contrast': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}(i-j)^2` - 'dissimilarity': :math:`\\sum_{i,j=0}^{levels-1}P_{i,j}|i-j|` - 'homogeneity': :math:`\\sum_{i,j=0}^{levels-1}\\frac{P_{i,j}}{1+(i-j)^2}` - - 'ASM': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}^2` + - 'ASM': :math:`\\sum_{i,j=0}^{levels-1} P_{i,j}^2` - 'energy': :math:`\\sqrt{ASM}` - 'correlation': - .. math:: \\sum_{i,j=0}^{levels-1} P_{i,j}\\left[\\frac{(i-\\mu_i) \\ + .. math:: \\sum_{i,j=0}^{levels-1} P_{i,j}\\left[\\frac{(i-\\mu_i) \\ (j-\\mu_j)}{\\sqrt{(\\sigma_i^2)(\\sigma_j^2)}}\\right] - + Parameters - ---------- + ---------- P : ndarray - Input array. `P` is the grey-level co-occurrence histogram + Input array. `P` is the grey-level co-occurrence histogram for which to compute the specified property. The value `P[i,j,d,theta]` is the number of times that grey-level j occurs at a distance d and at an angle theta from @@ -144,80 +142,80 @@ def greycoprops(P, prop='contrast'): prop : {'contrast', 'dissimilarity', 'homogeneity', 'energy', \ 'correlation', 'ASM'}, optional The property of the GLCM to compute. The default is 'contrast'. - + Returns ------- results : 2-D ndarray - 2-dimensional array. `results[d, a]` is the property 'prop' for + 2-dimensional array. `results[d, a]` is the property 'prop' for the d'th distance and the a'th angle. - + References ---------- .. [1] The GLCM Tutorial Home Page, - http://www.fp.ucalgary.ca/mhallbey/tutorial.htm - + http://www.fp.ucalgary.ca/mhallbey/tutorial.htm + Examples -------- Compute the contrast for GLCMs with distances [1, 2] and angles - [0 degrees, 90 degrees] - + [0 degrees, 90 degrees] + >>> image = np.array([[0, 0, 1, 1], ... [0, 0, 1, 1], ... [0, 2, 2, 2], ... [2, 2, 3, 3]], dtype=np.uint8) - >>> g = greycomatrix(image, [1, 2], [0, np.pi/2], levels=4, + >>> g = greycomatrix(image, [1, 2], [0, np.pi/2], levels=4, ... normed=True, symmetric=True) >>> contrast = greycoprops(g, 'contrast') >>> contrast array([[ 0.58333333, 1. ], [ 1.25 , 2.75 ]]) - + """ - + assert P.ndim == 4 (num_level, num_level2, num_dist, num_angle) = P.shape assert num_level == num_level2 assert num_dist > 0 assert num_angle > 0 - + # create weights for specified property I, J = np.ogrid[0:num_level, 0:num_level] if prop == 'contrast': - weights = (I - J) ** 2 + weights = (I - J)**2 elif prop == 'dissimilarity': weights = np.abs(I - J) elif prop == 'homogeneity': - weights = 1. / (1. + (I - J) ** 2) + weights = 1. / (1. + (I - J)**2) elif prop in ['ASM', 'energy', 'correlation']: pass else: raise ValueError('%s is an invalid property' % (prop)) - # compute property for each GLCM + # compute property for each GLCM if prop == 'energy': - asm = np.apply_over_axes(np.sum, (P ** 2), axes=(0, 1))[0, 0] + asm = np.apply_over_axes(np.sum, (P**2), axes=(0, 1))[0, 0] results = np.sqrt(asm) elif prop == 'ASM': - results = np.apply_over_axes(np.sum, (P ** 2), axes=(0, 1))[0, 0] + results = np.apply_over_axes(np.sum, (P**2), axes=(0, 1))[0, 0] elif prop == 'correlation': results = np.zeros((num_dist, num_angle), dtype=np.float64) I = np.array(range(num_level)).reshape((num_level, 1, 1, 1)) J = np.array(range(num_level)).reshape((1, num_level, 1, 1)) diff_i = I - np.apply_over_axes(np.sum, (I * P), axes=(0, 1))[0, 0] diff_j = J - np.apply_over_axes(np.sum, (J * P), axes=(0, 1))[0, 0] - - std_i = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_i) ** 2), + + std_i = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_i)**2), axes=(0, 1))[0, 0]) - std_j = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_j) ** 2), + std_j = np.sqrt(np.apply_over_axes(np.sum, (P * (diff_j)**2), axes=(0, 1))[0, 0]) - cov = np.apply_over_axes(np.sum, (P * (diff_i * diff_j)), + cov = np.apply_over_axes(np.sum, (P * (diff_i * diff_j)), axes=(0, 1))[0, 0] - + # handle the special case of standard deviations near zero mask_0 = std_i < 1e-15 mask_0[std_j < 1e-15] = True results[mask_0] = 1 - + # handle the standard case mask_1 = mask_0 == False results[mask_1] = cov[mask_1] / (std_i[mask_1] * std_j[mask_1]) diff --git a/skimage/feature/harris.py b/skimage/feature/harris.py index 6d0da9c0..14854ac4 100644 --- a/skimage/feature/harris.py +++ b/skimage/feature/harris.py @@ -42,7 +42,7 @@ def _compute_harris_response(image, eps=1e-6, gaussian_deviation=1): Wyy = ndimage.gaussian_filter(imy * imy, 1.5, mode='constant') # determinant and trace - Wdet = Wxx * Wyy - Wxy ** 2 + Wdet = Wxx * Wyy - Wxy**2 Wtr = Wxx + Wyy # Alternate formula for Harris response. # Alison Noble, "Descriptions of Image Surfaces", PhD thesis (1989) @@ -76,7 +76,7 @@ def harris(image, min_distance=10, threshold=0.1, eps=1e-6, ------- coordinates : (N, 2) array (row, column) coordinates of interest points. - + Examples ------- >>> square = np.zeros([10,10]) @@ -93,18 +93,17 @@ def harris(image, min_distance=10, threshold=0.1, eps=1e-6, [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [ 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) >>> harris(square, min_distance=1) - + Corners of the square - + array([[3, 3], [3, 6], [6, 3], - [6, 6]]) + [6, 6]]) """ - + harrisim = _compute_harris_response(image, eps=eps, gaussian_deviation=gaussian_deviation) coordinates = peak.peak_local_max(harrisim, min_distance=min_distance, threshold=threshold) return coordinates - diff --git a/skimage/feature/hog.py b/skimage/feature/hog.py index fc5c19da..4a24b0a2 100644 --- a/skimage/feature/hog.py +++ b/skimage/feature/hog.py @@ -2,6 +2,7 @@ import numpy as np from scipy import sqrt, pi, arctan2, cos, sin from scipy.ndimage import uniform_filter + def hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3), visualise=False, normalise=False): """Extract Histogram of Oriented Gradients (HOG) for a given image. @@ -94,7 +95,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), cell are used to vote into the orientation histogram. """ - magnitude = sqrt(gx ** 2 + gy ** 2) + magnitude = sqrt(gx**2 + gy**2) orientation = arctan2(gy, (gx + 1e-15)) * (180 / pi) + 90 sy, sx = image.shape @@ -118,12 +119,11 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), cond2 = temp_ori > 0 temp_mag = np.where(cond2, magnitude, 0) - orientation_histogram[:,:,i] = uniform_filter(temp_mag, size=(cy, cx))[cy/2::cy, cx/2::cx] - + orientation_histogram[:, :, i] = uniform_filter(temp_mag, + size=(cy, cx))[cy / 2::cy, cx / 2::cx] # now for each cell, compute the histogram #orientation_histogram = np.zeros((n_cellsx, n_cellsy, orientations)) - radius = min(cx, cy) // 2 - 1 hog_image = None if visualise: @@ -131,7 +131,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), if visualise: from skimage import draw - + for x in range(n_cellsx): for y in range(n_cellsy): for o in range(orientations): @@ -166,7 +166,7 @@ def hog(image, orientations=9, pixels_per_cell=(8, 8), for y in range(n_blocksy): block = orientation_histogram[y:y + by, x:x + bx, :] eps = 1e-5 - normalised_blocks[y, x, :] = block / sqrt(block.sum() ** 2 + eps) + normalised_blocks[y, x, :] = block / sqrt(block.sum()**2 + eps) """ The final step collects the HOG descriptors from all blocks of a dense diff --git a/skimage/feature/peak.py b/skimage/feature/peak.py index 5d3f375b..d8e54dd6 100644 --- a/skimage/feature/peak.py +++ b/skimage/feature/peak.py @@ -38,15 +38,15 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', ------- coordinates : (N, 2) array (row, column) coordinates of peaks. - + Notes ----- The peak local maximum function returns the coordinates of local peaks (maxima) in a image. A maximum filter is used for finding local maxima. This operation - dilates the original image. After comparison between dilated and original image, + dilates the original image. After comparison between dilated and original image, peak_local_max function returns the coordinates of peaks where dilated image = original. - + Examples -------- >>> im = np.zeros((7, 7)) @@ -60,14 +60,14 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', [ 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. , 0. , 0. , 0. ], [ 0. , 0. , 0. , 0. , 0. , 0. , 0. ]]) - + >>> peak_local_max(im, min_distance=1) array([[3, 2], [3, 4]]) - + >>> peak_local_max(im, min_distance=2) array([[3, 2]]) - + """ if np.all(image == image.flat[0]): return [] @@ -101,4 +101,3 @@ def peak_local_max(image, min_distance=10, threshold='deprecated', coordinates = coordinates[idx_maxsort][:2] return coordinates - diff --git a/skimage/feature/setup.py b/skimage/feature/setup.py index 13d4fae5..39358fd3 100644 --- a/skimage/feature/setup.py +++ b/skimage/feature/setup.py @@ -5,6 +5,7 @@ from skimage._build import cython base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -23,11 +24,11 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - author = 'scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Features', - url = 'https://github.com/scikits-image/scikits-image', - license = 'SciPy License (BSD Style)', + setup(maintainer='scikits-image Developers', + author='scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Features', + url='https://github.com/scikits-image/scikits-image', + license='SciPy License (BSD Style)', **(configuration(top_path='').todict()) ) diff --git a/skimage/feature/template.py b/skimage/feature/template.py index 4f3449bd..500f02a0 100644 --- a/skimage/feature/template.py +++ b/skimage/feature/template.py @@ -77,8 +77,7 @@ def match_template(image, template, pad_input=False): i0, j0 = template.shape i0 /= 2 j0 /= 2 - pad_image[i0:i0+h, j0:j0+w] = image + pad_image[i0:i0 + h, j0:j0 + w] = image image = pad_image result = _template.match_template(image, template) return result - diff --git a/skimage/feature/tests/test_glcm.py b/skimage/feature/tests/test_glcm.py index 3f321e55..da90ec56 100644 --- a/skimage/feature/tests/test_glcm.py +++ b/skimage/feature/tests/test_glcm.py @@ -7,8 +7,8 @@ class TestGLCM(): self.image = np.array([[0, 0, 1, 1], [0, 0, 1, 1], [0, 2, 2, 2], - [2, 2, 3, 3]], dtype=np.uint8) - + [2, 2, 3, 3]], dtype=np.uint8) + def test_output_angles(self): result = greycomatrix(self.image, [1], [0, np.pi / 2], 4) assert result.shape == (4, 4, 1, 2) @@ -21,23 +21,23 @@ class TestGLCM(): [0, 2, 2, 0], [0, 0, 1, 2], [0, 0, 0, 0]], dtype=np.uint32) - np.testing.assert_array_equal(result[:, :, 0, 1], expected2) - - def test_output_symmetric_1(self): - result = greycomatrix(self.image, [1], [np.pi / 2], 4, + np.testing.assert_array_equal(result[:, :, 0, 1], expected2) + + def test_output_symmetric_1(self): + result = greycomatrix(self.image, [1], [np.pi / 2], 4, symmetric=True) assert result.shape == (4, 4, 1, 1) expected = np.array([[6, 0, 2, 0], [0, 4, 2, 0], [2, 2, 2, 2], [0, 0, 2, 0]], dtype=np.uint32) - np.testing.assert_array_equal(result[:, :, 0, 0], expected) + np.testing.assert_array_equal(result[:, :, 0, 0], expected) def test_output_distance(self): im = np.array([[0, 0, 0, 0], [1, 0, 0, 1], [2, 0, 0, 2], - [3, 0, 0, 3]], dtype=np.uint8) + [3, 0, 0, 3]], dtype=np.uint8) result = greycomatrix(im, [3], [0], 4, symmetric=False) expected = np.array([[1, 0, 0, 0], [0, 1, 0, 0], @@ -52,7 +52,7 @@ class TestGLCM(): [3]], dtype=np.uint8) result = greycomatrix(im, [1, 2], [0, np.pi / 2], 4) assert result.shape == (4, 4, 2, 2) - + z = np.zeros((4, 4), dtype=np.uint32) e1 = np.array([[0, 1, 0, 0], [0, 0, 1, 0], @@ -62,7 +62,7 @@ class TestGLCM(): [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], dtype=np.uint32) - + np.testing.assert_array_equal(result[:, :, 0, 0], z) np.testing.assert_array_equal(result[:, :, 1, 0], z) np.testing.assert_array_equal(result[:, :, 0, 1], e1) @@ -70,39 +70,39 @@ class TestGLCM(): def test_output_empty(self): result = greycomatrix(self.image, [10], [0], 4) - np.testing.assert_array_equal(result[:, :, 0, 0], - np.zeros((4, 4), dtype=np.uint32)) + np.testing.assert_array_equal(result[:, :, 0, 0], + np.zeros((4, 4), dtype=np.uint32)) result = greycomatrix(self.image, [10], [0], 4, normed=True) - np.testing.assert_array_equal(result[:, :, 0, 0], - np.zeros((4, 4), dtype=np.uint32)) + np.testing.assert_array_equal(result[:, :, 0, 0], + np.zeros((4, 4), dtype=np.uint32)) - def test_normed_symmetric(self): - result = greycomatrix(self.image, [1, 2, 3], - [0, np.pi / 2, np.pi], 4, + def test_normed_symmetric(self): + result = greycomatrix(self.image, [1, 2, 3], + [0, np.pi / 2, np.pi], 4, normed=True, symmetric=True) for d in range(result.shape[2]): for a in range(result.shape[3]): - np.testing.assert_almost_equal(result[:, :, d, a].sum(), + np.testing.assert_almost_equal(result[:, :, d, a].sum(), 1.0) - np.testing.assert_array_equal(result[:, :, d, a], + np.testing.assert_array_equal(result[:, :, d, a], result[:, :, d, a].transpose()) def test_contrast(self): - result = greycomatrix(self.image, [1, 2], [0], 4, + result = greycomatrix(self.image, [1, 2], [0], 4, normed=True, symmetric=True) result = np.round(result, 3) contrast = greycoprops(result, 'contrast') np.testing.assert_almost_equal(contrast[0, 0], 0.586) - + def test_dissimilarity(self): - result = greycomatrix(self.image, [1], [0, np.pi / 2], 4, + result = greycomatrix(self.image, [1], [0, np.pi / 2], 4, normed=True, symmetric=True) result = np.round(result, 3) dissimilarity = greycoprops(result, 'dissimilarity') np.testing.assert_almost_equal(dissimilarity[0, 0], 0.418) def test_dissimilarity_2(self): - result = greycomatrix(self.image, [1, 3], [np.pi/2], 4, + result = greycomatrix(self.image, [1, 3], [np.pi / 2], 4, normed=True, symmetric=True) result = np.round(result, 3) dissimilarity = greycoprops(result, 'dissimilarity')[0, 0] @@ -110,23 +110,23 @@ class TestGLCM(): def test_invalid_property(self): result = greycomatrix(self.image, [1], [0], 4) - np.testing.assert_raises(ValueError, greycoprops, + np.testing.assert_raises(ValueError, greycoprops, result, 'ABC') - + def test_homogeneity(self): - result = greycomatrix(self.image, [1], [0, 6], 4, normed=True, + result = greycomatrix(self.image, [1], [0, 6], 4, normed=True, symmetric=True) homogeneity = greycoprops(result, 'homogeneity')[0, 0] np.testing.assert_almost_equal(homogeneity, 0.80833333) def test_energy(self): - result = greycomatrix(self.image, [1], [0, 4], 4, normed=True, + result = greycomatrix(self.image, [1], [0, 4], 4, normed=True, symmetric=True) energy = greycoprops(result, 'energy')[0, 0] np.testing.assert_almost_equal(energy, 0.38188131) - + def test_correlation(self): - result = greycomatrix(self.image, [1, 2], [0], 4, normed=True, + result = greycomatrix(self.image, [1, 2], [0], 4, normed=True, symmetric=True) energy = greycoprops(result, 'correlation') np.testing.assert_almost_equal(energy[0, 0], 0.71953255) @@ -134,9 +134,9 @@ class TestGLCM(): def test_uniform_properties(self): im = np.ones((4, 4), dtype=np.uint8) - result = greycomatrix(im, [1, 2, 8], [0, np.pi / 2], 4, normed=True, + result = greycomatrix(im, [1, 2, 8], [0, np.pi / 2], 4, normed=True, symmetric=True) - for prop in ['contrast', 'dissimilarity', 'homogeneity', + for prop in ['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation', 'ASM']: greycoprops(result, prop) diff --git a/skimage/feature/tests/test_harris.py b/skimage/feature/tests/test_harris.py index 758bfa5e..43bf28a3 100644 --- a/skimage/feature/tests/test_harris.py +++ b/skimage/feature/tests/test_harris.py @@ -13,6 +13,7 @@ def test_square_image(): assert results.any() assert len(results) == 1 + def test_noisy_square_image(): im = np.zeros((50, 50)).astype(float) im[:25, :25] = 1. @@ -21,6 +22,7 @@ def test_noisy_square_image(): assert results.any() assert len(results) == 1 + def test_squared_dot(): im = np.zeros((50, 50)) im[4:8, 4:8] = 1 @@ -28,6 +30,7 @@ def test_squared_dot(): results = harris(im, min_distance=3) assert (results == np.array([[6, 6]])).all() + def test_rotated_lena(): """ The harris filter should yield the same results with an image and it's @@ -44,4 +47,3 @@ def test_rotated_lena(): if __name__ == '__main__': from numpy import testing testing.run_module_suite() - diff --git a/skimage/feature/tests/test_hog.py b/skimage/feature/tests/test_hog.py index e4b8fda8..18c13c85 100644 --- a/skimage/feature/tests/test_hog.py +++ b/skimage/feature/tests/test_hog.py @@ -1,17 +1,18 @@ import numpy as np import scipy -from skimage.feature import hog +from skimage.feature import hog + def test_histogram_of_oriented_gradients(): # Replace with skimage.data.lena() after merge - img = scipy.misc.lena()[:256,:].astype(np.int8) - - fd = hog(img, orientations=9, pixels_per_cell=(8, 8), + img = scipy.misc.lena()[:256, :].astype(np.int8) + + fd = hog(img, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(1, 1)) - assert len(fd) == 9 * (256//8) * (512//8) - + assert len(fd) == 9 * (256 // 8) * (512 // 8) + if __name__ == '__main__': from numpy.testing import run_module_suite run_module_suite() diff --git a/skimage/feature/tests/test_peak.py b/skimage/feature/tests/test_peak.py index 91d38d99..eaedc84f 100644 --- a/skimage/feature/tests/test_peak.py +++ b/skimage/feature/tests/test_peak.py @@ -65,4 +65,3 @@ def test_num_peaks(): if __name__ == '__main__': from numpy import testing testing.run_module_suite() - diff --git a/skimage/feature/tests/test_template.py b/skimage/feature/tests/test_template.py index 7097a70a..1b9ff213 100644 --- a/skimage/feature/tests/test_template.py +++ b/skimage/feature/tests/test_template.py @@ -50,8 +50,8 @@ def test_normalization(): image[ineg:ineg + n, jneg:jneg + n] = 0 # white square with a black border - template = np.zeros((n+2, n+2)) - template[1:1+n, 1:1+n] = 1 + template = np.zeros((n + 2, n + 2)) + template[1:1 + n, 1:1 + n] = 1 result = match_template(image, template) @@ -121,4 +121,3 @@ def test_pad_input(): if __name__ == "__main__": from numpy import testing testing.run_module_suite() - diff --git a/skimage/filter/canny.py b/skimage/filter/canny.py index 4818916c..8be77894 100644 --- a/skimage/filter/canny.py +++ b/skimage/filter/canny.py @@ -56,7 +56,7 @@ def canny(image, sigma=1., low_threshold=.1, high_threshold=.2, mask=None): Parameters ----------- image : array_like, dtype=float - The greyscale input image to detect edges on; should be normalized to + The greyscale input image to detect edges on; should be normalized to 0.0 to 1.0. sigma : float @@ -85,21 +85,21 @@ def canny(image, sigma=1., low_threshold=.1, high_threshold=.2, mask=None): The steps of the algorithm are as follows: * Smooth the image using a Gaussian with ``sigma`` width. - + * Apply the horizontal and vertical Sobel operators to get the gradients within the image. The edge strength is the norm of the gradient. - - * Thin potential edges to 1-pixel wide curves. First, find the normal - to the edge at each point. This is done by looking at the + + * Thin potential edges to 1-pixel wide curves. First, find the normal + to the edge at each point. This is done by looking at the signs and the relative magnitude of the X-Sobel and Y-Sobel to sort the points into 4 categories: horizontal, vertical, - diagonal and antidiagonal. Then look in the normal and reverse - directions to see if the values in either of those directions are - greater than the point in question. Use interpolation to get a mix of - points instead of picking the one that's the closest to the normal. - - * Perform a hysteresis thresholding: first label all points above the - high threshold as edges. Then recursively label any point above the + diagonal and antidiagonal. Then look in the normal and reverse + directions to see if the values in either of those directions are + greater than the point in question. Use interpolation to get a mix of + points instead of picking the one that's the closest to the normal. + + * Perform a hysteresis thresholding: first label all points above the + high threshold as edges. Then recursively label any point above the low threshold that is 8-connected to a labeled point as an edge. References @@ -120,7 +120,7 @@ def canny(image, sigma=1., low_threshold=.1, high_threshold=.2, mask=None): >>> # First trial with the Canny filter, with the default smoothing >>> edges1 = filter.canny(im) >>> # Increase the smoothing for better results - >>> edges2 = filter.canny(im, sigma=3) + >>> edges2 = filter.canny(im, sigma=3) ''' # diff --git a/skimage/filter/edges.py b/skimage/filter/edges.py index f6bf7031..cd91effb 100644 --- a/skimage/filter/edges.py +++ b/skimage/filter/edges.py @@ -12,6 +12,7 @@ import numpy as np from skimage import img_as_float from scipy.ndimage import convolve, binary_erosion, generate_binary_structure + def sobel(image, mask=None): """Calculate the absolute magnitude Sobel to find edges. @@ -38,6 +39,7 @@ def sobel(image, mask=None): """ return np.sqrt(hsobel(image, mask)**2 + vsobel(image, mask)**2) + def hsobel(image, mask=None): """Find the horizontal edges of an image using the Sobel transform. @@ -68,7 +70,7 @@ def hsobel(image, mask=None): mask = np.ones(image.shape, bool) big_mask = binary_erosion(mask, generate_binary_structure(2, 2), - border_value = 0) + border_value=0) result = np.abs(convolve(image, np.array([[ 1, 2, 1], [ 0, 0, 0], @@ -76,6 +78,7 @@ def hsobel(image, mask=None): result[big_mask == False] = 0 return result + def vsobel(image, mask=None): """Find the vertical edges of an image using the Sobel transform. @@ -114,6 +117,7 @@ def vsobel(image, mask=None): result[big_mask == False] = 0 return result + def prewitt(image, mask=None): """Find the edge magnitude using the Prewitt transform. @@ -134,7 +138,8 @@ def prewitt(image, mask=None): Return the square root of the sum of squares of the horizontal and vertical Prewitt transforms. """ - return np.sqrt(hprewitt(image, mask) ** 2 + vprewitt(image, mask) ** 2) + return np.sqrt(hprewitt(image, mask)**2 + vprewitt(image, mask)**2) + def hprewitt(image, mask=None): """Find the horizontal edges of an image using the Prewitt transform. @@ -174,6 +179,7 @@ def hprewitt(image, mask=None): result[big_mask == False] = 0 return result + def vprewitt(image, mask=None): """Find the vertical edges of an image using the Prewitt transform. diff --git a/skimage/filter/lpi_filter.py b/skimage/filter/lpi_filter.py index 50d2de17..60eb1d63 100644 --- a/skimage/filter/lpi_filter.py +++ b/skimage/filter/lpi_filter.py @@ -11,10 +11,12 @@ from scipy.fftpack import fftshift, ifftshift eps = np.finfo(float).eps + def _min_limit(x, val=eps): mask = np.abs(x) < eps x[mask] = np.sign(x[mask]) * eps + def _centre(x, oshape): """Return an array of oshape from the centre of x. @@ -23,6 +25,7 @@ def _centre(x, oshape): out = x[[slice(s, s + n) for s, n in zip(start, oshape)]] return out + def _pad(data, shape): """Pad the data to the given shape with zeros. @@ -38,7 +41,6 @@ def _pad(data, shape): return out - class LPIFilter2D(object): """Linear Position-Invariant Filter (2-dimensional) @@ -83,21 +85,21 @@ class LPIFilter2D(object): """ dshape = np.array(data.shape) - dshape += (dshape % 2 == 0) # all filter dimensions must be uneven + dshape += (dshape % 2 == 0) # all filter dimensions must be uneven oshape = np.array(data.shape) * 2 - 1 if self._cache is None or np.any(self._cache.shape != oshape): coords = np.mgrid[[slice(0, float(n)) for n in dshape]] # this steps over two sets of coordinates, # not over the coordinates individually - for k,coord in enumerate(coords): - coord -= (dshape[k] - 1)/2. - coords = coords.reshape(2, -1).T # coordinate pairs (r,c) + for k, coord in enumerate(coords): + coord -= (dshape[k] - 1) / 2. + coords = coords.reshape(2, -1).T # coordinate pairs (r,c) - f = self.impulse_response(coords[:,0],coords[:,1], + f = self.impulse_response(coords[:, 0], coords[:, 1], **self.filter_params).reshape(dshape) - f = _pad(f,oshape) + f = _pad(f, oshape) F = np.dual.fftn(f) self._cache = F else: @@ -108,7 +110,7 @@ class LPIFilter2D(object): return F, G - def __call__(self,data): + def __call__(self, data): """Apply the filter to the given data. *Parameters*: @@ -120,6 +122,7 @@ class LPIFilter2D(object): out = np.abs(_centre(out, data.shape)) return out + def forward(data, impulse_response=None, filter_params={}, predefined_filter=None): """Apply the given filter to data. @@ -155,6 +158,7 @@ def forward(data, impulse_response=None, filter_params={}, predefined_filter = LPIFilter2D(impulse_response, **filter_params) return predefined_filter(data) + def inverse(data, impulse_response=None, filter_params={}, max_gain=2, predefined_filter=None): """Apply the filter in reverse to the given data. @@ -189,12 +193,13 @@ def inverse(data, impulse_response=None, filter_params={}, max_gain=2, F, G = filt._prepare(data) _min_limit(F) - F = 1/F + F = 1 / F mask = np.abs(F) > max_gain F[mask] = np.sign(F[mask]) * max_gain return _centre(np.abs(ifftshift(np.dual.ifftn(G * F))), data.shape) + def wiener(data, impulse_response=None, filter_params={}, K=0.25, predefined_filter=None): """Minimum Mean Square Error (Wiener) inverse filter. @@ -228,11 +233,11 @@ def wiener(data, impulse_response=None, filter_params={}, K=0.25, _min_limit(F) H_mag_sqr = np.abs(F)**2 - F = 1/F * H_mag_sqr / (H_mag_sqr + K) + F = 1 / F * H_mag_sqr / (H_mag_sqr + K) return _centre(np.abs(ifftshift(np.dual.ifftn(G * F))), data.shape) + def constrained_least_squares(data, lam, impulse_response=None, filter_params={}): raise NotImplementedError - diff --git a/skimage/filter/rank_order.py b/skimage/filter/rank_order.py index 3da98974..f878702f 100644 --- a/skimage/filter/rank_order.py +++ b/skimage/filter/rank_order.py @@ -10,9 +10,10 @@ Original author: Lee Kamentstky """ import numpy + def rank_order(image): """Return an image of the same shape where each pixel is the - index of the pixel value in the ascending order of the unique + index of the pixel value in the ascending order of the unique values of `image`, aka the rank-order value. Parameters @@ -48,14 +49,12 @@ def rank_order(image): flat_image = image.ravel() sort_order = flat_image.argsort().astype(numpy.uint32) flat_image = flat_image[sort_order] - sort_rank = numpy.zeros_like(sort_order) + sort_rank = numpy.zeros_like(sort_order) is_different = flat_image[:-1] != flat_image[1:] numpy.cumsum(is_different, out=sort_rank[1:]) - original_values = numpy.zeros((sort_rank[-1]+1,),image.dtype) + original_values = numpy.zeros((sort_rank[-1] + 1,), image.dtype) original_values[0] = flat_image[0] - original_values[1:] = flat_image[1:][is_different] + original_values[1:] = flat_image[1:][is_different] int_image = numpy.zeros_like(sort_order) int_image[sort_order] = sort_rank return (int_image.reshape(image.shape), original_values) - - diff --git a/skimage/filter/setup.py b/skimage/filter/setup.py index 12cb84a7..03ea7def 100644 --- a/skimage/filter/setup.py +++ b/skimage/filter/setup.py @@ -5,6 +5,7 @@ from skimage._build import cython base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -20,11 +21,11 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - author = 'scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Filters', - url = 'https://github.com/scikits-image/scikits-image', - license = 'SciPy License (BSD Style)', + setup(maintainer='scikits-image Developers', + author='scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Filters', + url='https://github.com/scikits-image/scikits-image', + license='SciPy License (BSD Style)', **(configuration(top_path='').todict()) ) diff --git a/skimage/filter/tests/test_edges.py b/skimage/filter/tests/test_edges.py index 0c306273..bd9a2702 100644 --- a/skimage/filter/tests/test_edges.py +++ b/skimage/filter/tests/test_edges.py @@ -7,6 +7,7 @@ from scipy.ndimage import binary_dilation, binary_erosion import skimage.filter as F from skimage import data_dir, img_as_float + class TestSobel(): def test_00_00_zeros(self): """Sobel on an array of all zeros""" @@ -70,6 +71,7 @@ class TestHSobel(): result = F.hsobel(image) assert (np.all(result == 0)) + class TestVSobel(): def test_00_00_zeros(self): """Vertical sobel on an array of all zeros""" @@ -101,6 +103,7 @@ class TestVSobel(): eps = .000001 assert (np.all(np.abs(result) < eps)) + class TestPrewitt(): def test_00_00_zeros(self): """Prewitt on an array of all zeros""" @@ -132,10 +135,11 @@ class TestPrewitt(): image = (j >= 0).astype(float) result = F.prewitt(image) eps = .000001 - j[np.abs(i)==5] = 10000 + j[np.abs(i) == 5] = 10000 assert (np.all(result[j == 0] == 1)) assert (np.all(np.abs(result[np.abs(j) > 1]) < eps)) + class TestHPrewitt(): def test_00_00_zeros(self): """Horizontal sobel on an array of all zeros""" @@ -169,6 +173,7 @@ class TestHPrewitt(): eps = .000001 assert (np.all(np.abs(result) < eps)) + class TestVPrewitt(): def test_00_00_zeros(self): """Vertical prewitt on an array of all zeros""" diff --git a/skimage/filter/tests/test_lpi_filter.py b/skimage/filter/tests/test_lpi_filter.py index 1cf7b3ca..08e46a58 100644 --- a/skimage/filter/tests/test_lpi_filter.py +++ b/skimage/filter/tests/test_lpi_filter.py @@ -7,12 +7,13 @@ from skimage import data_dir from skimage.io import * from skimage.filter import * + class TestLPIFilter2D(): img = imread(os.path.join(data_dir, 'camera.png'), - flatten=True)[:50,:50] + flatten=True)[:50, :50] - def filt_func(self,r,c): - return np.exp(-np.hypot(r,c)/1) + def filt_func(self, r, c): + return np.exp(-np.hypot(r, c) / 1) def setUp(self): self.f = LPIFilter2D(self.filt_func) @@ -33,27 +34,26 @@ class TestLPIFilter2D(): g = inverse(F, predefined_filter=self.f) assert_equal(g.shape, self.img.shape) - g1 = inverse(F[::-1,::-1], predefined_filter=self.f) - assert ((g - g1[::-1,::-1]).sum() < 55) + g1 = inverse(F[::-1, ::-1], predefined_filter=self.f) + assert ((g - g1[::-1, ::-1]).sum() < 55) # test cache - g1 = inverse(F[::-1,::-1], predefined_filter=self.f) - assert ((g - g1[::-1,::-1]).sum() < 55) + g1 = inverse(F[::-1, ::-1], predefined_filter=self.f) + assert ((g - g1[::-1, ::-1]).sum() < 55) g1 = inverse(F[::-1, ::-1], self.filt_func) - assert ((g - g1[::-1,::-1]).sum() < 55) + assert ((g - g1[::-1, ::-1]).sum() < 55) def test_wiener(self): F = self.f(self.img) g = wiener(F, predefined_filter=self.f) assert_equal(g.shape, self.img.shape) - g1 = wiener(F[::-1,::-1], predefined_filter=self.f) - assert ((g - g1[::-1,::-1]).sum() < 1) + g1 = wiener(F[::-1, ::-1], predefined_filter=self.f) + assert ((g - g1[::-1, ::-1]).sum() < 1) - g1 = wiener(F[::-1,::-1], self.filt_func) - assert ((g - g1[::-1,::-1]).sum() < 1) + g1 = wiener(F[::-1, ::-1], self.filt_func) + assert ((g - g1[::-1, ::-1]).sum() < 1) if __name__ == "__main__": run_module_suite() - diff --git a/skimage/filter/tests/test_thresholding.py b/skimage/filter/tests/test_thresholding.py index 6177b9f5..59d7e431 100644 --- a/skimage/filter/tests/test_thresholding.py +++ b/skimage/filter/tests/test_thresholding.py @@ -75,17 +75,19 @@ class TestSimpleImage(): def test_otsu_camera_image(): assert threshold_otsu(data.camera()) == 87 + def test_otsu_coins_image(): assert threshold_otsu(data.coins()) == 107 + def test_otsu_coins_image_as_float(): coins = skimage.img_as_float(data.coins()) assert 0.41 < threshold_otsu(coins) < 0.42 + def test_otsu_lena_image(): assert threshold_otsu(data.lena()) == 141 if __name__ == '__main__': np.testing.run_module_suite() - diff --git a/skimage/filter/tests/test_tv_denoise.py b/skimage/filter/tests/test_tv_denoise.py index f851f4f1..4cc6adbb 100644 --- a/skimage/filter/tests/test_tv_denoise.py +++ b/skimage/filter/tests/test_tv_denoise.py @@ -4,6 +4,7 @@ from numpy.testing import run_module_suite from skimage import filter, data, color from skimage import img_as_uint + class TestTvDenoise(): def test_tv_denoise_2d(self): @@ -14,7 +15,7 @@ class TestTvDenoise(): # lena image lena = color.rgb2gray(data.lena())[:256, :256] # add noise to lena - lena += 0.5 * lena.std()*np.random.randn(*lena.shape) + lena += 0.5 * lena.std() * np.random.randn(*lena.shape) # clip noise so that it does not exceed allowed range for float images. lena = np.clip(lena, 0, 1) # denoise @@ -22,25 +23,26 @@ class TestTvDenoise(): # which dtype? assert denoised_lena.dtype in [np.float, np.float32, np.float64] from scipy import ndimage - grad = ndimage.morphological_gradient(lena, size=((3,3))) - grad_denoised = ndimage.morphological_gradient(denoised_lena, size=((3,3))) + grad = ndimage.morphological_gradient(lena, size=((3, 3))) + grad_denoised = ndimage.morphological_gradient( + denoised_lena, size=((3, 3))) # test if the total variation has decreased - assert np.sqrt((grad_denoised**2).sum()) < np.sqrt((grad**2).sum()) / 2 + assert (np.sqrt((grad_denoised**2).sum()) + < np.sqrt((grad**2).sum()) / 2) denoised_lena_int = filter.tv_denoise(img_as_uint(lena), weight=60.0, keep_type=True) assert denoised_lena_int.dtype is np.dtype('uint16') - def test_tv_denoise_3d(self): """ Apply the TV denoising algorithm on a 3D image representing a sphere. """ x, y, z = np.ogrid[0:40, 0:40, 0:40] - mask = (x -22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 + mask = (x - 22)**2 + (y - 20)**2 + (z - 17)**2 < 8**2 mask = 100 * mask.astype(np.float) mask += 60 - mask += 20*np.random.randn(*mask.shape) + mask += 20 * np.random.randn(*mask.shape) mask[mask < 0] = 0 mask[mask > 255] = 255 res = filter.tv_denoise(mask.astype(np.uint8), diff --git a/skimage/filter/thresholding.py b/skimage/filter/thresholding.py index 4e3ebca3..022cacc5 100644 --- a/skimage/filter/thresholding.py +++ b/skimage/filter/thresholding.py @@ -86,6 +86,7 @@ def threshold_adaptive(image, block_size, method='gaussian', offset=0, return image > (thresh_image - offset) + def threshold_otsu(image, nbins=256): """Return threshold value based on Otsu's method. diff --git a/skimage/filter/tv_denoise.py b/skimage/filter/tv_denoise.py index bb74a4bc..e8736786 100644 --- a/skimage/filter/tv_denoise.py +++ b/skimage/filter/tv_denoise.py @@ -1,5 +1,6 @@ import numpy as np + def _tv_denoise_3d(im, weight=100, eps=2.e-4, n_iter_max=200): """ Perform total-variation denoising on 3-D arrays @@ -10,8 +11,8 @@ def _tv_denoise_3d(im, weight=100, eps=2.e-4, n_iter_max=200): 3-D input data to be denoised weight: float, optional - denoising weight. The greater ``weight``, the more denoising (at - the expense of fidelity to ``input``) + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) eps: float, optional relative difference of the value of the cost function that determines @@ -29,7 +30,7 @@ def _tv_denoise_3d(im, weight=100, eps=2.e-4, n_iter_max=200): Notes ----- - Rudin, Osher and Fatemi algorithm + Rudin, Osher and Fatemi algorithm Examples --------- @@ -50,25 +51,25 @@ def _tv_denoise_3d(im, weight=100, eps=2.e-4, n_iter_max=200): i = 0 while i < n_iter_max: d = - px - py - pz - d[1:] += px[:-1] - d[:, 1:] += py[:, :-1] - d[:, :, 1:] += pz[:, :, :-1] - + d[1:] += px[:-1] + d[:, 1:] += py[:, :-1] + d[:, :, 1:] += pz[:, :, :-1] + out = im + d E = (d**2).sum() - gx[:-1] = np.diff(out, axis=0) - gy[:, :-1] = np.diff(out, axis=1) - gz[:, :, :-1] = np.diff(out, axis=2) + gx[:-1] = np.diff(out, axis=0) + gy[:, :-1] = np.diff(out, axis=1) + gz[:, :, :-1] = np.diff(out, axis=2) norm = np.sqrt(gx**2 + gy**2 + gz**2) E += weight * norm.sum() norm *= 0.5 / weight norm += 1. - px -= 1./6.*gx + px -= 1. / 6. * gx px /= norm - py -= 1./6.*gy + py -= 1. / 6. * gy py /= norm - pz -= 1/6.*gz + pz -= 1 / 6. * gz pz /= norm E /= float(im.size) if i == 0: @@ -81,7 +82,8 @@ def _tv_denoise_3d(im, weight=100, eps=2.e-4, n_iter_max=200): E_previous = E i += 1 return out - + + def _tv_denoise_2d(im, weight=50, eps=2.e-4, n_iter_max=200): """ Perform total-variation denoising @@ -92,8 +94,8 @@ def _tv_denoise_2d(im, weight=50, eps=2.e-4, n_iter_max=200): input data to be denoised weight: float, optional - denoising weight. The greater ``weight``, the more denoising (at - the expense of fidelity to ``input``) + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) eps: float, optional relative difference of the value of the cost function that determines @@ -114,14 +116,14 @@ def _tv_denoise_2d(im, weight=50, eps=2.e-4, n_iter_max=200): The principle of total variation denoising is explained in http://en.wikipedia.org/wiki/Total_variation_denoising - This code is an implementation of the algorithm of Rudin, Fatemi and Osher + This code is an implementation of the algorithm of Rudin, Fatemi and Osher that was proposed by Chambolle in [1]_. References ---------- - .. [1] A. Chambolle, An algorithm for total variation minimization and - applications, Journal of Mathematical Imaging and Vision, + .. [1] A. Chambolle, An algorithm for total variation minimization and + applications, Journal of Mathematical Imaging and Vision, Springer, 2004, 20, 89-97. Examples @@ -140,21 +142,21 @@ def _tv_denoise_2d(im, weight=50, eps=2.e-4, n_iter_max=200): d = np.zeros_like(im) i = 0 while i < n_iter_max: - d = -px -py - d[1:] += px[:-1] - d[:, 1:] += py[:, :-1] - + d = -px - py + d[1:] += px[:-1] + d[:, 1:] += py[:, :-1] + out = im + d E = (d**2).sum() - gx[:-1] = np.diff(out, axis=0) - gy[:, :-1] = np.diff(out, axis=1) + gx[:-1] = np.diff(out, axis=0) + gy[:, :-1] = np.diff(out, axis=1) norm = np.sqrt(gx**2 + gy**2) E += weight * norm.sum() norm *= 0.5 / weight norm += 1 - px -= 0.25*gx + px -= 0.25 * gx px /= norm - py -= 0.25*gy + py -= 0.25 * gy py /= norm E /= float(im.size) if i == 0: @@ -168,6 +170,7 @@ def _tv_denoise_2d(im, weight=50, eps=2.e-4, n_iter_max=200): i += 1 return out + def tv_denoise(im, weight=50, eps=2.e-4, keep_type=False, n_iter_max=200): """ Perform total-variation denoising on 2-d and 3-d images @@ -176,21 +179,21 @@ def tv_denoise(im, weight=50, eps=2.e-4, keep_type=False, n_iter_max=200): ---------- im: ndarray (2d or 3d) of ints, uints or floats input data to be denoised. `im` can be of any numeric type, - but it is cast into an ndarray of floats for the computation + but it is cast into an ndarray of floats for the computation of the denoised image. weight: float, optional - denoising weight. The greater ``weight``, the more denoising (at - the expense of fidelity to ``input``) + denoising weight. The greater ``weight``, the more denoising (at + the expense of fidelity to ``input``) eps: float, optional - relative difference of the value of the cost function that + relative difference of the value of the cost function that determines the stop criterion. The algorithm stops when: (E_(n-1) - E_n) < eps * E_0 keep_type: bool, optional (False) - whether the output has the same dtype as the input array. + whether the output has the same dtype as the input array. keep_type is False by default, and the dtype of the output is np.float @@ -209,19 +212,19 @@ def tv_denoise(im, weight=50, eps=2.e-4, keep_type=False, n_iter_max=200): http://en.wikipedia.org/wiki/Total_variation_denoising The principle of total variation denoising is to minimize the - total variation of the image, which can be roughly described as - the integral of the norm of the image gradient. Total variation - denoising tends to produce "cartoon-like" images, that is, + total variation of the image, which can be roughly described as + the integral of the norm of the image gradient. Total variation + denoising tends to produce "cartoon-like" images, that is, piecewise-constant images. - This code is an implementation of the algorithm of Rudin, Fatemi and Osher + This code is an implementation of the algorithm of Rudin, Fatemi and Osher that was proposed by Chambolle in [1]_. References ---------- - .. [1] A. Chambolle, An algorithm for total variation minimization and - applications, Journal of Mathematical Imaging and Vision, + .. [1] A. Chambolle, An algorithm for total variation minimization and + applications, Journal of Mathematical Imaging and Vision, Springer, 2004, 20, 89-97. Examples @@ -249,9 +252,9 @@ def tv_denoise(im, weight=50, eps=2.e-4, keep_type=False, n_iter_max=200): elif im.ndim == 3: out = _tv_denoise_3d(im, weight, eps, n_iter_max) else: - raise ValueError('only 2-d and 3-d images may be denoised with this function') + raise ValueError('only 2-d and 3-d images may be denoised with this ' + 'function') if keep_type: return out.astype(im_type) else: return out - diff --git a/skimage/graph/mcp.py b/skimage/graph/mcp.py index a803f58c..68921371 100644 --- a/skimage/graph/mcp.py +++ b/skimage/graph/mcp.py @@ -1,5 +1,6 @@ from ._mcp import MCP, MCP_Geometric, make_offsets + def route_through_array(array, start, end, fully_connected=True, geometric=True): """Simple example of how to use the MCP and MCP_Geometric classes. diff --git a/skimage/graph/setup.py b/skimage/graph/setup.py index 5e0a2f06..2c432011 100644 --- a/skimage/graph/setup.py +++ b/skimage/graph/setup.py @@ -5,6 +5,7 @@ import os.path base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -28,10 +29,10 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Graph-based Image-processing Algorithms', - url = 'https://github.com/scikits-image/scikits-image', - license = 'Modified BSD', + setup(maintainer='scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Graph-based Image-processing Algorithms', + url='https://github.com/scikits-image/scikits-image', + license='Modified BSD', **(configuration(top_path='').todict()) ) diff --git a/skimage/graph/spath.py b/skimage/graph/spath.py index a912a693..d8ec3526 100644 --- a/skimage/graph/spath.py +++ b/skimage/graph/spath.py @@ -1,6 +1,7 @@ import numpy as np from . import _spath + def shortest_path(arr, reach=1, axis=-1, output_indexlist=False): """Find the shortest path through an n-d array from one side to another. @@ -39,7 +40,7 @@ def shortest_path(arr, reach=1, axis=-1, output_indexlist=False): # a grid defined by the reach. if axis < 0: axis += arr.ndim - offset_ind_shape = (2*reach + 1,) * (arr.ndim - 1) + offset_ind_shape = (2 * reach + 1,) * (arr.ndim - 1) offset_indices = np.indices(offset_ind_shape) - reach offset_indices = np.insert(offset_indices, axis, np.ones(offset_ind_shape), axis=0) @@ -49,7 +50,7 @@ def shortest_path(arr, reach=1, axis=-1, output_indexlist=False): # Valid starting positions are anywhere on the hyperplane defined by # position 0 on the given axis. Ending positions are anywhere on the # hyperplane at position -1 along the same. - non_axis_shape = arr.shape[:axis] + arr.shape[axis+1:] + non_axis_shape = arr.shape[:axis] + arr.shape[axis + 1:] non_axis_indices = np.indices(non_axis_shape) non_axis_size = np.multiply.reduce(non_axis_shape) start_indices = np.insert(non_axis_indices, axis, @@ -72,7 +73,7 @@ def shortest_path(arr, reach=1, axis=-1, output_indexlist=False): if not output_indexlist: traceback = np.array(traceback) - traceback = np.concatenate([traceback[:,:axis], traceback[:,axis+1:]], + traceback = np.concatenate([traceback[:, :axis], traceback[:, axis + 1:]], axis=1) traceback = np.squeeze(traceback) diff --git a/skimage/graph/tests/test_heap.py b/skimage/graph/tests/test_heap.py index 0cc41c92..2dc7fb76 100644 --- a/skimage/graph/tests/test_heap.py +++ b/skimage/graph/tests/test_heap.py @@ -5,18 +5,20 @@ import time import random import skimage.graph.heap as heap + def test_heap(): _test_heap(100000, True) _test_heap(100000, False) + def _test_heap(n, fast_update): # generate random numbers with duplicates random.seed(0) - a = [random.uniform(1.0,100.0) for i in range(n//2)] - a = a+a - + a = [random.uniform(1.0, 100.0) for i in range(n // 2)] + a = a + a + t0 = time.clock() - + # insert in heap with random removals if fast_update: h = heap.FastUpdateBinaryHeap(128, n) @@ -25,12 +27,12 @@ def _test_heap(n, fast_update): for i in range(len(a)): h.push(a[i], i) if a[i] < 25: - # double-push same ref sometimes to test fast update codepaths - h.push(2*a[i], i) + # double-push same ref sometimes to test fast update codepaths + h.push(2 * a[i], i) if 25 < a[i] < 50: - # pop some to test random removal - h.pop() - + # pop some to test random removal + h.pop() + # pop from heap b = [] while True: @@ -38,14 +40,14 @@ def _test_heap(n, fast_update): b.append(h.pop()[0]) except IndexError: break - + t1 = time.clock() - + # verify - for i in range(1,len(b)): - assert(b[i] >= b[i-1]) - - return t1-t0 + for i in range(1, len(b)): + assert(b[i] >= b[i - 1]) + + return t1 - t0 if __name__ == "__main__": run_module_suite() diff --git a/skimage/graph/tests/test_mcp.py b/skimage/graph/tests/test_mcp.py index 9d36a6ec..e3fd45a0 100644 --- a/skimage/graph/tests/test_mcp.py +++ b/skimage/graph/tests/test_mcp.py @@ -3,7 +3,7 @@ from numpy.testing import * import skimage.graph.mcp as mcp -a = np.ones((8,8), dtype=np.float32) +a = np.ones((8, 8), dtype=np.float32) a[1:-1, 1] = 0 a[1, 1:-1] = 0 @@ -16,19 +16,20 @@ a[1, 1:-1] = 0 ## [ 1., 0., 1., 1., 1., 1., 1., 1.], ## [ 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32) + def test_basic(): m = mcp.MCP(a, fully_connected=True) - costs, traceback = m.find_costs([(1,6)]) + costs, traceback = m.find_costs([(1, 6)]) return_path = m.traceback((7, 2)) assert_array_equal(costs, - [[ 1., 1., 1., 1., 1., 1., 1., 1.], - [ 1., 0., 0., 0., 0., 0., 0., 1.], - [ 1., 0., 1., 1., 1., 1., 1., 1.], - [ 1., 0., 1., 2., 2., 2., 2., 2.], - [ 1., 0., 1., 2., 3., 3., 3., 3.], - [ 1., 0., 1., 2., 3., 4., 4., 4.], - [ 1., 0., 1., 2., 3., 4., 5., 5.], - [ 1., 1., 1., 2., 3., 4., 5., 6.]]) + [[1., 1., 1., 1., 1., 1., 1., 1.], + [1., 0., 0., 0., 0., 0., 0., 1.], + [1., 0., 1., 1., 1., 1., 1., 1.], + [1., 0., 1., 2., 2., 2., 2., 2.], + [1., 0., 1., 2., 3., 3., 3., 3.], + [1., 0., 1., 2., 3., 4., 4., 4.], + [1., 0., 1., 2., 3., 4., 5., 5.], + [1., 1., 1., 2., 3., 4., 5., 6.]]) assert_array_equal(return_path, [(1, 6), @@ -43,8 +44,9 @@ def test_basic(): (6, 1), (7, 2)]) + def test_neg_inf(): - expected_costs = np.where(a==1, np.inf, 0) + expected_costs = np.where(a == 1, np.inf, 0) expected_path = [(1, 6), (1, 5), (1, 4), @@ -55,8 +57,8 @@ def test_neg_inf(): (4, 1), (5, 1), (6, 1)] - test_neg = np.where(a==1, -1, 0) - test_inf = np.where(a==1, np.inf, 0) + test_neg = np.where(a == 1, -1, 0) + test_inf = np.where(a == 1, np.inf, 0) m = mcp.MCP(test_neg, fully_connected=True) costs, traceback = m.find_costs([(1, 6)]) return_path = m.traceback((6, 1)) @@ -67,11 +69,12 @@ def test_neg_inf(): return_path = m.traceback((6, 1)) assert_array_equal(costs, expected_costs) assert_array_equal(return_path, expected_path) - + def test_route(): - return_path, cost = mcp.route_through_array(a, (1,6), (7,2), geometric=True) - assert_almost_equal(cost, np.sqrt(2)/2) + return_path, cost = mcp.route_through_array(a, (1, 6), (7, 2), + geometric=True) + assert_almost_equal(cost, np.sqrt(2) / 2) assert_array_equal(return_path, [(1, 6), (1, 5), @@ -85,19 +88,20 @@ def test_route(): (6, 1), (7, 2)]) + def test_no_diagonal(): m = mcp.MCP(a, fully_connected=False) - costs, traceback = m.find_costs([(1,6)]) + costs, traceback = m.find_costs([(1, 6)]) return_path = m.traceback((7, 2)) assert_array_equal(costs, - [[ 2., 1., 1., 1., 1., 1., 1., 2.], - [ 1., 0., 0., 0., 0., 0., 0., 1.], - [ 1., 0., 1., 1., 1., 1., 1., 2.], - [ 1., 0., 1., 2., 2., 2., 2., 3.], - [ 1., 0., 1., 2., 3., 3., 3., 4.], - [ 1., 0., 1., 2., 3., 4., 4., 5.], - [ 1., 0., 1., 2., 3., 4., 5., 6.], - [ 2., 1., 2., 3., 4., 5., 6., 7.]]) + [[2., 1., 1., 1., 1., 1., 1., 2.], + [1., 0., 0., 0., 0., 0., 0., 1.], + [1., 0., 1., 1., 1., 1., 1., 2.], + [1., 0., 1., 2., 2., 2., 2., 3.], + [1., 0., 1., 2., 3., 3., 3., 4.], + [1., 0., 1., 2., 3., 4., 4., 5.], + [1., 0., 1., 2., 3., 4., 5., 6.], + [2., 1., 2., 3., 4., 5., 6., 7.]]) assert_array_equal(return_path, [(1, 6), (1, 5), @@ -115,34 +119,36 @@ def test_no_diagonal(): def test_offsets(): - offsets = [(1,i) for i in range(10)] + [(1, -i) for i in range(1,10)] - m = mcp.MCP(a, offsets=offsets) - costs, traceback = m.find_costs([(1,6)]) - assert_array_equal(traceback, - [[-1, -1, -1, -1, -1, -1, -1, -1], - [-1, -1, -1, -1, -1, -1, -1, -1], - [15, 14, 13, 12, 11, 10, 0, 1], - [10, 0, 1, 2, 3, 4, 5, 6], - [10, 0, 1, 2, 3, 4, 5, 6], - [10, 0, 1, 2, 3, 4, 5, 6], - [10, 0, 1, 2, 3, 4, 5, 6], - [10, 0, 1, 2, 3, 4, 5, 6]]) - + offsets = [(1, i) for i in range(10)] + [(1, -i) for i in range(1, 10)] + m = mcp.MCP(a, offsets=offsets) + costs, traceback = m.find_costs([(1, 6)]) + assert_array_equal(traceback, + [[-1, -1, -1, -1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1, -1, -1, -1], + [15, 14, 13, 12, 11, 10, 0, 1], + [10, 0, 1, 2, 3, 4, 5, 6], + [10, 0, 1, 2, 3, 4, 5, 6], + [10, 0, 1, 2, 3, 4, 5, 6], + [10, 0, 1, 2, 3, 4, 5, 6], + [10, 0, 1, 2, 3, 4, 5, 6]]) + def test_crashing(): - for shape in [(100, 100), (5, 8, 13, 17)]*5: + for shape in [(100, 100), (5, 8, 13, 17)] * 5: yield _test_random, shape + def _test_random(shape): # Just tests for crashing -- not for correctness. np.random.seed(0) a = np.random.random(shape).astype(np.float32) - starts = [[0]*len(shape), [-1]*len(shape), - (np.random.random(len(shape))*shape).astype(int)] - ends = [(np.random.random(len(shape))*shape).astype(int) for i in range(4)] + starts = [[0] * len(shape), [-1] * len(shape), + (np.random.random(len(shape)) * shape).astype(int)] + ends = [(np.random.random(len(shape)) * shape).astype(int) + for i in range(4)] m = mcp.MCP(a, fully_connected=True) costs, offsets = m.find_costs(starts) - for point in [(np.random.random(len(shape))*shape).astype(int) + for point in [(np.random.random(len(shape)) * shape).astype(int) for i in range(4)]: m.traceback(point) m._reset() diff --git a/skimage/graph/tests/test_spath.py b/skimage/graph/tests/test_spath.py index a9f6b274..62f9f303 100644 --- a/skimage/graph/tests/test_spath.py +++ b/skimage/graph/tests/test_spath.py @@ -3,6 +3,7 @@ from numpy.testing import * import skimage.graph.spath as spath + def test_basic(): x = np.array([[1, 1, 3], [0, 2, 0], @@ -11,6 +12,7 @@ def test_basic(): assert_array_equal(path, [0, 0, 1]) assert_equal(cost, 1) + def test_reach(): x = np.array([[1, 1, 3], [0, 2, 0], @@ -19,6 +21,7 @@ def test_reach(): assert_array_equal(path, [0, 0, 2]) assert_equal(cost, 0) + def test_non_square(): x = np.array([[1, 1, 1, 1, 5, 5, 5], [5, 0, 0, 5, 9, 1, 1], diff --git a/skimage/io/_io.py b/skimage/io/_io.py index 05a7a8b7..477fe8bc 100644 --- a/skimage/io/_io.py +++ b/skimage/io/_io.py @@ -8,6 +8,7 @@ import numpy as np # Shared image queue _image_stack = [] + def push(img): """Push an image onto the shared image stack. @@ -22,6 +23,7 @@ def push(img): _image_stack.append(img) + def pop(): """Pop an image from the shared image stack. @@ -33,6 +35,7 @@ def pop(): """ return _image_stack.pop() + def imread(fname, as_grey=False, plugin=None, flatten=None, **plugin_args): """Load an image from file. @@ -76,6 +79,7 @@ def imread(fname, as_grey=False, plugin=None, flatten=None, return img + def imread_collection(load_pattern, conserve_memory=True, plugin=None, **plugin_args): """ @@ -128,6 +132,7 @@ def imsave(fname, arr, plugin=None, **plugin_args): """ return call_plugin('imsave', fname, arr, plugin=plugin, **plugin_args) + def imshow(arr, plugin=None, **plugin_args): """Display an image. @@ -148,6 +153,7 @@ def imshow(arr, plugin=None, **plugin_args): """ return call_plugin('imshow', arr, plugin=plugin, **plugin_args) + def show(): '''Display pending images. diff --git a/skimage/io/_plugins/fits_plugin.py b/skimage/io/_plugins/fits_plugin.py index b6627032..9eb8e028 100644 --- a/skimage/io/_plugins/fits_plugin.py +++ b/skimage/io/_plugins/fits_plugin.py @@ -49,9 +49,9 @@ def imread(fname, dtype=None): for hdu in hdulist: if isinstance(hdu, pyfits.ImageHDU) or \ isinstance(hdu, pyfits.PrimaryHDU): - if hdu.data is not None: - img_array = hdu.data - break + if hdu.data is not None: + img_array = hdu.data + break hdulist.close() return img_array @@ -109,7 +109,7 @@ def imread_collection(load_pattern, conserve_memory=True): def FITSFactory(image_ext): """Load an image extension from a FITS file and return a NumPy array - + Parameters ---------- @@ -136,7 +136,7 @@ def FITSFactory(image_ext): raise ValueError("Expected a (filename, extension) tuple") hdulist = pyfits.open(filename) - + data = hdulist[extnum].data hdulist.close() @@ -146,4 +146,3 @@ def FITSFactory(image_ext): (extnum, filename)) return data - diff --git a/skimage/io/_plugins/freeimage_plugin.py b/skimage/io/_plugins/freeimage_plugin.py index 5d023909..bbb1772b 100644 --- a/skimage/io/_plugins/freeimage_plugin.py +++ b/skimage/io/_plugins/freeimage_plugin.py @@ -20,7 +20,7 @@ def _generate_candidate_libs(): lib_dirs.append(os.path.join(os.environ['HOME'], 'lib')) lib_dirs = [ld for ld in lib_dirs if os.path.exists(ld)] - lib_names = ['libfreeimage', 'freeimage'] # should be lower-case! + lib_names = ['libfreeimage', 'freeimage'] # should be lower-case! # Now attempt to find libraries of that name in the given directory # (case-insensitive and without regard for extension) lib_paths = [] @@ -34,6 +34,7 @@ def _generate_candidate_libs(): return lib_dirs, lib_paths + def load_freeimage(): if sys.platform == 'win32': loader = ctypes.windll @@ -70,13 +71,13 @@ def load_freeimage(): if errors: # No freeimage library loaded, and load-errors reported for some # candidate libs - err_txt = ['%s:\n%s'%(l, str(e.message)) for l, e in errors] + err_txt = ['%s:\n%s' % (l, str(e.message)) for l, e in errors] raise OSError('One or more FreeImage libraries were found, but ' - 'could not be loaded due to the following errors:\n'+ + 'could not be loaded due to the following errors:\n' '\n\n'.join(err_txt)) else: # No errors, because no potential libraries found at all! - raise OSError('Could not find a FreeImage library in any of:\n'+ + raise OSError('Could not find a FreeImage library in any of:\n' + '\n'.join(lib_dirs)) # FreeImage found @@ -113,7 +114,9 @@ API = { } # Albert's ctypes pattern -def register_api(lib,api): + + +def register_api(lib, api): for f, (restype, argtypes) in api.items(): func = getattr(lib, f) func.restype = restype @@ -205,111 +208,112 @@ class FI_TYPES(object): extra_dims = cls.extra_dims[fi_type] return numpy.dtype(dtype), extra_dims + [w, h] + class IO_FLAGS(object): - FIF_LOAD_NOPIXELS = 0x8000 # loading: load the image header only - # (not supported by all plugins) + FIF_LOAD_NOPIXELS = 0x8000 # loading: load the image header only + # (not supported by all plugins) BMP_DEFAULT = 0 BMP_SAVE_RLE = 1 CUT_DEFAULT = 0 DDS_DEFAULT = 0 - EXR_DEFAULT = 0 # save data as half with piz-based wavelet compression - EXR_FLOAT = 0x0001 # save data as float instead of as half (not recommended) - EXR_NONE = 0x0002 # save with no compression - EXR_ZIP = 0x0004 # save with zlib compression, in blocks of 16 scan lines - EXR_PIZ = 0x0008 # save with piz-based wavelet compression - EXR_PXR24 = 0x0010 # save with lossy 24-bit float compression - EXR_B44 = 0x0020 # save with lossy 44% float compression + EXR_DEFAULT = 0 # save data as half with piz-based wavelet compression + EXR_FLOAT = 0x0001 # save data as float instead of as half (not recommended) + EXR_NONE = 0x0002 # save with no compression + EXR_ZIP = 0x0004 # save with zlib compression, in blocks of 16 scan lines + EXR_PIZ = 0x0008 # save with piz-based wavelet compression + EXR_PXR24 = 0x0010 # save with lossy 24-bit float compression + EXR_B44 = 0x0020 # save with lossy 44% float compression # - goes to 22% when combined with EXR_LC - EXR_LC = 0x0040 # save images with one luminance and two chroma channels, + EXR_LC = 0x0040 # save images with one luminance and two chroma channels, # rather than as RGB (lossy compression) FAXG3_DEFAULT = 0 GIF_DEFAULT = 0 - GIF_LOAD256 = 1 # Load the image as a 256 color image with ununsed - # palette entries, if it's 16 or 2 color - GIF_PLAYBACK = 2 # 'Play' the GIF to generate each frame (as 32bpp) - # instead of returning raw frame data when loading + GIF_LOAD256 = 1 # Load the image as a 256 color image with ununsed + # palette entries, if it's 16 or 2 color + GIF_PLAYBACK = 2 # 'Play' the GIF to generate each frame (as 32bpp) + # instead of returning raw frame data when loading HDR_DEFAULT = 0 ICO_DEFAULT = 0 - ICO_MAKEALPHA = 1 # convert to 32bpp and create an alpha channel from the - # AND-mask when loading + ICO_MAKEALPHA = 1 # convert to 32bpp and create an alpha channel from the + # AND-mask when loading IFF_DEFAULT = 0 - J2K_DEFAULT = 0 # save with a 16:1 rate - JP2_DEFAULT = 0 # save with a 16:1 rate - JPEG_DEFAULT = 0 # loading (see JPEG_FAST); - # saving (see JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420) - JPEG_FAST = 0x0001 # load the file as fast as possible, - # sacrificing some quality - JPEG_ACCURATE = 0x0002 # load the file with the best quality, - # sacrificing some speed - JPEG_CMYK = 0x0004 # load separated CMYK "as is" - # (use | to combine with other load flags) - JPEG_EXIFROTATE = 0x0008 # load and rotate according to - # Exif 'Orientation' tag if available - JPEG_QUALITYSUPERB = 0x80 # save with superb quality (100:1) - JPEG_QUALITYGOOD = 0x0100 # save with good quality (75:1) - JPEG_QUALITYNORMAL = 0x0200 # save with normal quality (50:1) - JPEG_QUALITYAVERAGE = 0x0400 # save with average quality (25:1) - JPEG_QUALITYBAD = 0x0800 # save with bad quality (10:1) - JPEG_PROGRESSIVE = 0x2000 # save as a progressive-JPEG - # (use | to combine with other save flags) - JPEG_SUBSAMPLING_411 = 0x1000 # save with high 4x1 chroma - # subsampling (4:1:1) - JPEG_SUBSAMPLING_420 = 0x4000 # save with medium 2x2 medium chroma - # subsampling (4:2:0) - default value - JPEG_SUBSAMPLING_422 = 0x8000 # save with low 2x1 chroma subsampling (4:2:2) - JPEG_SUBSAMPLING_444 = 0x10000 # save with no chroma subsampling (4:4:4) - JPEG_OPTIMIZE = 0x20000 # on saving, compute optimal Huffman coding tables - # (can reduce a few percent of file size) - JPEG_BASELINE = 0x40000 # save basic JPEG, without metadata or any markers + J2K_DEFAULT = 0 # save with a 16:1 rate + JP2_DEFAULT = 0 # save with a 16:1 rate + JPEG_DEFAULT = 0 # loading (see JPEG_FAST); + # saving (see JPEG_QUALITYGOOD|JPEG_SUBSAMPLING_420) + JPEG_FAST = 0x0001 # load the file as fast as possible, + # sacrificing some quality + JPEG_ACCURATE = 0x0002 # load the file with the best quality, + # sacrificing some speed + JPEG_CMYK = 0x0004 # load separated CMYK "as is" + # (use | to combine with other load flags) + JPEG_EXIFROTATE = 0x0008 # load and rotate according to + # Exif 'Orientation' tag if available + JPEG_QUALITYSUPERB = 0x80 # save with superb quality (100:1) + JPEG_QUALITYGOOD = 0x0100 # save with good quality (75:1) + JPEG_QUALITYNORMAL = 0x0200 # save with normal quality (50:1) + JPEG_QUALITYAVERAGE = 0x0400 # save with average quality (25:1) + JPEG_QUALITYBAD = 0x0800 # save with bad quality (10:1) + JPEG_PROGRESSIVE = 0x2000 # save as a progressive-JPEG + # (use | to combine with other save flags) + JPEG_SUBSAMPLING_411 = 0x1000 # save with high 4x1 chroma + # subsampling (4:1:1) + JPEG_SUBSAMPLING_420 = 0x4000 # save with medium 2x2 medium chroma + # subsampling (4:2:0) - default value + JPEG_SUBSAMPLING_422 = 0x8000 # save with low 2x1 chroma subsampling (4:2:2) + JPEG_SUBSAMPLING_444 = 0x10000 # save with no chroma subsampling (4:4:4) + JPEG_OPTIMIZE = 0x20000 # on saving, compute optimal Huffman coding tables + # (can reduce a few percent of file size) + JPEG_BASELINE = 0x40000 # save basic JPEG, without metadata or any markers KOALA_DEFAULT = 0 LBM_DEFAULT = 0 MNG_DEFAULT = 0 PCD_DEFAULT = 0 - PCD_BASE = 1 # load the bitmap sized 768 x 512 - PCD_BASEDIV4 = 2 # load the bitmap sized 384 x 256 - PCD_BASEDIV16 = 3 # load the bitmap sized 192 x 128 + PCD_BASE = 1 # load the bitmap sized 768 x 512 + PCD_BASEDIV4 = 2 # load the bitmap sized 384 x 256 + PCD_BASEDIV16 = 3 # load the bitmap sized 192 x 128 PCX_DEFAULT = 0 PFM_DEFAULT = 0 PICT_DEFAULT = 0 PNG_DEFAULT = 0 - PNG_IGNOREGAMMA = 1 # loading: avoid gamma correction - PNG_Z_BEST_SPEED = 0x0001 # save using ZLib level 1 compression flag - # (default value is 6) - PNG_Z_DEFAULT_COMPRESSION = 0x0006 # save using ZLib level 6 compression - # flag (default recommended value) - PNG_Z_BEST_COMPRESSION = 0x0009 # save using ZLib level 9 compression flag - # (default value is 6) - PNG_Z_NO_COMPRESSION = 0x0100 # save without ZLib compression - PNG_INTERLACED = 0x0200 # save using Adam7 interlacing (use | to combine - # with other save flags) + PNG_IGNOREGAMMA = 1 # loading: avoid gamma correction + PNG_Z_BEST_SPEED = 0x0001 # save using ZLib level 1 compression flag + # (default value is 6) + PNG_Z_DEFAULT_COMPRESSION = 0x0006 # save using ZLib level 6 compression + # flag (default recommended value) + PNG_Z_BEST_COMPRESSION = 0x0009 # save using ZLib level 9 compression flag + # (default value is 6) + PNG_Z_NO_COMPRESSION = 0x0100 # save without ZLib compression + PNG_INTERLACED = 0x0200 # save using Adam7 interlacing (use | to combine + # with other save flags) PNM_DEFAULT = 0 - PNM_SAVE_RAW = 0 # Writer saves in RAW format (i.e. P4, P5 or P6) - PNM_SAVE_ASCII = 1 # Writer saves in ASCII format (i.e. P1, P2 or P3) + PNM_SAVE_RAW = 0 # Writer saves in RAW format (i.e. P4, P5 or P6) + PNM_SAVE_ASCII = 1 # Writer saves in ASCII format (i.e. P1, P2 or P3) PSD_DEFAULT = 0 - PSD_CMYK = 1 # reads tags for separated CMYK (default is conversion to RGB) - PSD_LAB = 2 # reads tags for CIELab (default is conversion to RGB) + PSD_CMYK = 1 # reads tags for separated CMYK (default is conversion to RGB) + PSD_LAB = 2 # reads tags for CIELab (default is conversion to RGB) RAS_DEFAULT = 0 - RAW_DEFAULT = 0 # load the file as linear RGB 48-bit - RAW_PREVIEW = 1 # try to load the embedded JPEG preview with included - # Exif Data or default to RGB 24-bit - RAW_DISPLAY = 2 # load the file as RGB 24-bit + RAW_DEFAULT = 0 # load the file as linear RGB 48-bit + RAW_PREVIEW = 1 # try to load the embedded JPEG preview with included + # Exif Data or default to RGB 24-bit + RAW_DISPLAY = 2 # load the file as RGB 24-bit SGI_DEFAULT = 0 TARGA_DEFAULT = 0 - TARGA_LOAD_RGB888 = 1 # Convert RGB555 and ARGB8888 -> RGB888. - TARGA_SAVE_RLE = 2 # Save with RLE compression + TARGA_LOAD_RGB888 = 1 # Convert RGB555 and ARGB8888 -> RGB888. + TARGA_SAVE_RLE = 2 # Save with RLE compression TIFF_DEFAULT = 0 - TIFF_CMYK = 0x0001 # reads/stores tags for separated CMYK - # (use | to combine with compression flags) - TIFF_PACKBITS = 0x0100 # save using PACKBITS compression - TIFF_DEFLATE = 0x0200 # save using DEFLATE (a.k.a. ZLIB) compression - TIFF_ADOBE_DEFLATE = 0x0400 # save using ADOBE DEFLATE compression - TIFF_NONE = 0x0800 # save without any compression - TIFF_CCITTFAX3 = 0x1000 # save using CCITT Group 3 fax encoding - TIFF_CCITTFAX4 = 0x2000 # save using CCITT Group 4 fax encoding - TIFF_LZW = 0x4000 # save using LZW compression - TIFF_JPEG = 0x8000 # save using JPEG compression - TIFF_LOGLUV = 0x10000 # save using LogLuv compression + TIFF_CMYK = 0x0001 # reads/stores tags for separated CMYK + # (use | to combine with compression flags) + TIFF_PACKBITS = 0x0100 # save using PACKBITS compression + TIFF_DEFLATE = 0x0200 # save using DEFLATE (a.k.a. ZLIB) compression + TIFF_ADOBE_DEFLATE = 0x0400 # save using ADOBE DEFLATE compression + TIFF_NONE = 0x0800 # save without any compression + TIFF_CCITTFAX3 = 0x1000 # save using CCITT Group 3 fax encoding + TIFF_CCITTFAX4 = 0x2000 # save using CCITT Group 4 fax encoding + TIFF_LZW = 0x4000 # save using LZW compression + TIFF_JPEG = 0x8000 # save using JPEG compression + TIFF_LOGLUV = 0x10000 # save using LogLuv compression WBMP_DEFAULT = 0 XBM_DEFAULT = 0 XPM_DEFAULT = 0 @@ -329,23 +333,23 @@ class METADATA_MODELS(object): class METADATA_DATATYPE(object): - FIDT_BYTE = 1 # 8-bit unsigned integer - FIDT_ASCII = 2 # 8-bit bytes w/ last byte null - FIDT_SHORT = 3 # 16-bit unsigned integer - FIDT_LONG = 4 # 32-bit unsigned integer - FIDT_RATIONAL = 5 # 64-bit unsigned fraction - FIDT_SBYTE = 6 # 8-bit signed integer - FIDT_UNDEFINED = 7 # 8-bit untyped data - FIDT_SSHORT = 8 # 16-bit signed integer - FIDT_SLONG = 9 # 32-bit signed integer - FIDT_SRATIONAL = 10 # 64-bit signed fraction - FIDT_FLOAT = 11 # 32-bit IEEE floating point - FIDT_DOUBLE = 12 # 64-bit IEEE floating point - FIDT_IFD = 13 # 32-bit unsigned integer (offset) - FIDT_PALETTE = 14 # 32-bit RGBQUAD - FIDT_LONG8 = 16 # 64-bit unsigned integer - FIDT_SLONG8 = 17 # 64-bit signed integer - FIDT_IFD8 = 18 # 64-bit unsigned integer (offset) + FIDT_BYTE = 1 # 8-bit unsigned integer + FIDT_ASCII = 2 # 8-bit bytes w/ last byte null + FIDT_SHORT = 3 # 16-bit unsigned integer + FIDT_LONG = 4 # 32-bit unsigned integer + FIDT_RATIONAL = 5 # 64-bit unsigned fraction + FIDT_SBYTE = 6 # 8-bit signed integer + FIDT_UNDEFINED = 7 # 8-bit untyped data + FIDT_SSHORT = 8 # 16-bit signed integer + FIDT_SLONG = 9 # 32-bit signed integer + FIDT_SRATIONAL = 10 # 64-bit signed fraction + FIDT_FLOAT = 11 # 32-bit IEEE floating point + FIDT_DOUBLE = 12 # 64-bit IEEE floating point + FIDT_IFD = 13 # 32-bit unsigned integer (offset) + FIDT_PALETTE = 14 # 32-bit RGBQUAD + FIDT_LONG8 = 16 # 64-bit unsigned integer + FIDT_SLONG8 = 17 # 64-bit signed integer + FIDT_IFD8 = 18 # 64-bit unsigned integer (offset) dtypes = { FIDT_BYTE: numpy.uint8, @@ -384,6 +388,7 @@ def _process_bitmap(filename, flags, process_func): finally: _FI.FreeImage_Unload(bitmap) + def read(filename, flags=0): """Read an image to a numpy array of shape (height, width) for greyscale images, or shape (height, width, nchannels) for RGB or @@ -394,6 +399,7 @@ def read(filename, flags=0): """ return _process_bitmap(filename, flags, _array_from_bitmap) + def read_metadata(filename): """Return a dict containing all image metadata. @@ -404,6 +410,7 @@ def read_metadata(filename): flags = IO_FLAGS.FIF_LOAD_NOPIXELS return _process_bitmap(filename, flags, _read_metadata) + def _process_multipage(filename, flags, process_func): filename = asbytes(filename) ftype = _FI.FreeImage_GetFileType(filename, 0) @@ -435,6 +442,7 @@ def _process_multipage(filename, flags, process_func): finally: _FI.FreeImage_CloseMultiBitmap(multibitmap, 0) + def read_multipage(filename, flags=0): """Read a multipage image to a list of numpy arrays, where each array is of shape (height, width) for greyscale images, or shape @@ -445,6 +453,7 @@ def read_multipage(filename, flags=0): """ return _process_multipage(filename, flags, _array_from_bitmap) + def read_multipage_metadata(filename): """Read a multipage image to a list of metadata dicts, one dict for each page. The dict format is as in read_metadata(). @@ -452,26 +461,28 @@ def read_multipage_metadata(filename): flags = IO_FLAGS.FIF_LOAD_NOPIXELS return _process_multipage(filename, flags, _read_metadata) + def _wrap_bitmap_bits_in_array(bitmap, shape, dtype): - """Return an ndarray view on the data in a FreeImage bitmap. Only - valid for as long as the bitmap is loaded (if single page) / locked - in memory (if multipage). + """Return an ndarray view on the data in a FreeImage bitmap. Only + valid for as long as the bitmap is loaded (if single page) / locked + in memory (if multipage). - """ - pitch = _FI.FreeImage_GetPitch(bitmap) - height = shape[-1] - byte_size = height * pitch - itemsize = dtype.itemsize + """ + pitch = _FI.FreeImage_GetPitch(bitmap) + height = shape[-1] + byte_size = height * pitch + itemsize = dtype.itemsize + + if len(shape) == 3: + strides = (itemsize, shape[0] * itemsize, pitch) + else: + strides = (itemsize, pitch) + bits = _FI.FreeImage_GetBits(bitmap) + array = numpy.ndarray(shape, dtype=dtype, + buffer=(ctypes.c_char * byte_size).from_address(bits), + strides=strides) + return array - if len(shape) == 3: - strides = (itemsize, shape[0]*itemsize, pitch) - else: - strides = (itemsize, pitch) - bits = _FI.FreeImage_GetBits(bitmap) - array = numpy.ndarray(shape, dtype=dtype, - buffer=(ctypes.c_char*byte_size).from_address(bits), - strides=strides) - return array def _array_from_bitmap(bitmap): """Convert a FreeImage bitmap pointer to a numpy array. @@ -482,6 +493,7 @@ def _array_from_bitmap(bitmap): # swizzle the color components and flip the scanlines to go from # FreeImage's BGR[A] and upside-down internal memory format to something # more normal + def n(arr): return arr[..., ::-1].T if len(shape) == 3 and _FI.FreeImage_IsLittleEndian() and \ @@ -490,10 +502,10 @@ def _array_from_bitmap(bitmap): g = n(array[1]) r = n(array[2]) if shape[0] == 3: - return numpy.dstack( (r, g, b) ) + return numpy.dstack((r, g, b)) elif shape[0] == 4: a = n(array[3]) - return numpy.dstack( (r, g, b, a) ) + return numpy.dstack((r, g, b, a)) else: raise ValueError('Cannot handle images of shape %s' % shape) @@ -501,6 +513,7 @@ def _array_from_bitmap(bitmap): # after bitmap is freed. return n(array).copy() + def _read_metadata(bitmap): metadata = {} models = [(name[5:], number) for name, number in @@ -531,6 +544,7 @@ def _read_metadata(bitmap): _FI.FreeImage_FindCloseMetadata(mdhandle) return metadata + def write(array, filename, flags=0): """Write a (height, width) or (height, width, nchannels) array to a greyscale, RGB, or RGBA image, with file type deduced from the @@ -558,7 +572,8 @@ def write(array, filename, flags=0): if not res: raise RuntimeError('Could not save image properly.') finally: - _FI.FreeImage_Unload(bitmap) + _FI.FreeImage_Unload(bitmap) + def write_multipage(arrays, filename, flags=0): """Write a list of (height, width) or (height, width, nchannels) @@ -592,19 +607,20 @@ def write_multipage(arrays, filename, flags=0): # 4-byte quads of 0,v,v,v from 0,0,0,0 to 0,255,255,255 _GREY_PALETTE = numpy.arange(0, 0x01000000, 0x00010101, dtype=numpy.uint32) + def _array_to_bitmap(array): """Allocate a FreeImage bitmap and copy a numpy array into it. """ shape = array.shape dtype = array.dtype - r,c = shape[:2] + r, c = shape[:2] if len(shape) == 2: n_channels = 1 - w_shape = (c,r) + w_shape = (c, r) elif len(shape) == 3: n_channels = shape[2] - w_shape = (n_channels,c,r) + w_shape = (n_channels, c, r) else: n_channels = shape[0] try: @@ -619,18 +635,18 @@ def _array_to_bitmap(array): if not bitmap: raise RuntimeError('Could not allocate image for storage') try: - def n(arr): # normalise to freeimage's in-memory format - return arr.T[:,::-1] + def n(arr): # normalise to freeimage's in-memory format + return arr.T[:, ::-1] wrapped_array = _wrap_bitmap_bits_in_array(bitmap, w_shape, dtype) # swizzle the color components and flip the scanlines to go to # FreeImage's BGR[A] and upside-down internal memory format if len(shape) == 3 and _FI.FreeImage_IsLittleEndian() and \ dtype.type == numpy.uint8: - wrapped_array[0] = n(array[:,:,2]) - wrapped_array[1] = n(array[:,:,1]) - wrapped_array[2] = n(array[:,:,0]) + wrapped_array[0] = n(array[:, :, 2]) + wrapped_array[1] = n(array[:, :, 1]) + wrapped_array[2] = n(array[:, :, 0]) if shape[2] == 4: - wrapped_array[3] = n(array[:,:,3]) + wrapped_array[3] = n(array[:, :, 3]) else: wrapped_array[:] = n(array) if len(shape) == 2 and dtype.type == numpy.uint8: @@ -641,8 +657,8 @@ def _array_to_bitmap(array): ctypes.memmove(palette, _GREY_PALETTE.ctypes.data, 1024) return bitmap, fi_type except: - _FI.FreeImage_Unload(bitmap) - raise + _FI.FreeImage_Unload(bitmap) + raise def imread(filename): @@ -661,6 +677,7 @@ def imread(filename): img = read(filename) return img + def imsave(filename, img): ''' imsave(filename, img) diff --git a/skimage/io/_plugins/gdal_plugin.py b/skimage/io/_plugins/gdal_plugin.py index acc6db36..f4c2a1ae 100644 --- a/skimage/io/_plugins/gdal_plugin.py +++ b/skimage/io/_plugins/gdal_plugin.py @@ -9,6 +9,7 @@ except ImportError: "Please refer to http://www.gdal.org/ " "for further instructions.") + def imread(fname, dtype=None): """Load an image from file. @@ -16,4 +17,3 @@ def imread(fname, dtype=None): ds = gdal.Open(fname) return ds.ReadAsArray().astype(dtype) - diff --git a/skimage/io/_plugins/matplotlib_plugin.py b/skimage/io/_plugins/matplotlib_plugin.py index 652c8be1..ece44d93 100644 --- a/skimage/io/_plugins/matplotlib_plugin.py +++ b/skimage/io/_plugins/matplotlib_plugin.py @@ -1,5 +1,6 @@ import matplotlib.pyplot as plt + def imshow(*args, **kwargs): kwargs.setdefault('interpolation', 'nearest') kwargs.setdefault('cmap', 'gray') @@ -8,5 +9,6 @@ def imshow(*args, **kwargs): imread = plt.imread show = plt.show + def _app_show(): show() diff --git a/skimage/io/_plugins/pil_plugin.py b/skimage/io/_plugins/pil_plugin.py index 914f7e9f..96106c5c 100644 --- a/skimage/io/_plugins/pil_plugin.py +++ b/skimage/io/_plugins/pil_plugin.py @@ -11,6 +11,7 @@ except ImportError: from skimage.util import img_as_ubyte + def imread(fname, dtype=None): """Load an image from file. @@ -33,6 +34,7 @@ def imread(fname, dtype=None): return np.array(im, dtype=dtype) + def _palette_is_grayscale(pil_image): """Return True if PIL image in palette mode is grayscale. @@ -56,6 +58,7 @@ def _palette_is_grayscale(pil_image): # are all zero. return np.allclose(np.diff(valid_palette), 0) + def imsave(fname, arr): """Save an image to disk. @@ -100,6 +103,7 @@ def imsave(fname, arr): img = Image.fromstring(mode, (arr.shape[1], arr.shape[0]), arr.tostring()) img.save(fname) + def imshow(arr): """Display an image, using PIL's default display command. @@ -112,5 +116,6 @@ def imshow(arr): """ Image.fromarray(img_as_ubyte(arr)).show() + def _app_show(): pass diff --git a/skimage/io/_plugins/plugin.py b/skimage/io/_plugins/plugin.py index 66ac3911..da69363d 100644 --- a/skimage/io/_plugins/plugin.py +++ b/skimage/io/_plugins/plugin.py @@ -4,7 +4,6 @@ __all__ = ['use', 'available', 'call', 'info', 'configuration', 'reset_plugins'] -import warnings from ConfigParser import ConfigParser import os.path from glob import glob @@ -15,8 +14,9 @@ plugin_provides = {} plugin_module_name = {} plugin_meta_data = {} + def reset_plugins(): - """Clear the plugin state to the default, i.e., where no plugins are loaded. + """Clear the plugin state to the default, i.e., where no plugins are loaded """ global plugin_store @@ -28,6 +28,7 @@ def reset_plugins(): reset_plugins() + def _scan_plugins(): """Scan the plugins directory for .ini files and parse them to gather plugin meta-data. @@ -59,6 +60,7 @@ def _scan_plugins(): _scan_plugins() + def call(kind, *args, **kwargs): """Find the appropriate plugin of 'kind' and execute it. @@ -90,13 +92,14 @@ command. A list of all available plugins can be found using else: _load(plugin) try: - func = [f for (p,f) in plugin_funcs if p == plugin][0] + func = [f for (p, f) in plugin_funcs if p == plugin][0] except IndexError: raise RuntimeError('Could not find the plugin "%s" for %s.' % \ (plugin, kind)) return func(*args, **kwargs) + def use(name, kind=None): """Set the default plugin for a specified operation. The plugin will be loaded if it hasn't been already. @@ -149,6 +152,7 @@ def use(name, kind=None): plugin_store[k] = funcs + def available(loaded=False): """List available plugins. @@ -178,6 +182,7 @@ def available(loaded=False): return d + def _load(plugin): """Load the given plugin. @@ -211,6 +216,7 @@ def _load(plugin): if not (plugin, func) in store: store.append((plugin, func)) + def info(plugin): """Return plugin meta-data. @@ -230,6 +236,7 @@ def info(plugin): except KeyError: raise ValueError('No information on plugin "%s"' % plugin) + def configuration(): """Return the currently preferred plugin order. diff --git a/skimage/io/_plugins/q_color_mixer.py b/skimage/io/_plugins/q_color_mixer.py index 5a60b92b..3fe9e29c 100644 --- a/skimage/io/_plugins/q_color_mixer.py +++ b/skimage/io/_plugins/q_color_mixer.py @@ -1,7 +1,6 @@ # the module for the qt color_mixer plugin from PyQt4 import QtGui, QtCore -from PyQt4.QtGui import (QWidget, QStackedWidget, QSlider, QVBoxLayout, - QGridLayout, QLabel) +from PyQt4.QtGui import (QWidget, QStackedWidget, QSlider, QGridLayout, QLabel) from util import ColorMixer @@ -36,7 +35,8 @@ class IntelligentSlider(QWidget): self.name_label.setAlignment(QtCore.Qt.AlignCenter) self.value_label = QLabel() - self.value_label.setText('%2.2f' % (self.slider.value() * self.a + self.b)) + self.value_label.setText('%2.2f' % (self.slider.value() * self.a + + self.b)) self.value_label.setAlignment(QtCore.Qt.AlignCenter) self.layout = QGridLayout(self) @@ -120,11 +120,9 @@ class MixerPanel(QtGui.QFrame): self.rgb_widget.layout.addWidget(self.gs, 2, 1) self.rgb_widget.layout.addWidget(self.bs, 2, 2) - #--------------------------------------------------------------- # HSV sliders #--------------------------------------------------------------- - # radio buttons self.hsv_add = QtGui.QRadioButton('Additive') self.hsv_mul = QtGui.QRadioButton('Multiplicative') @@ -147,11 +145,9 @@ class MixerPanel(QtGui.QFrame): self.hsv_widget.layout.addWidget(self.ss, 2, 1) self.hsv_widget.layout.addWidget(self.vs, 2, 2) - #--------------------------------------------------------------- # Brightness/Contrast sliders #--------------------------------------------------------------- - # sliders cont = IntelligentSlider('x', 0.002, 0, self.bright_changed) bright = IntelligentSlider('+', 0.51, -255, self.bright_changed) @@ -164,10 +160,9 @@ class MixerPanel(QtGui.QFrame): self.bright_widget.layout.addWidget(self.cont, 0, 0) self.bright_widget.layout.addWidget(self.bright, 0, 1) - - #----------------------------------------------------------------------- + #---------------------------------------------------------------------- # Gamma Slider - #----------------------------------------------------------------------- + #---------------------------------------------------------------------- gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed) self.gamma = gamma @@ -176,11 +171,9 @@ class MixerPanel(QtGui.QFrame): self.gamma_widget.layout = QtGui.QGridLayout(self.gamma_widget) self.gamma_widget.layout.addWidget(self.gamma, 0, 0) - #--------------------------------------------------------------- # Sigmoid Gamma sliders #--------------------------------------------------------------- - # sliders alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed) beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed) @@ -225,7 +218,6 @@ class MixerPanel(QtGui.QFrame): self.rgb_mul.setChecked(True) self.hsv_mul.setChecked(True) - def set_callback(self, callback): self.callback = callback @@ -281,7 +273,6 @@ class MixerPanel(QtGui.QFrame): self.a_gamma.set_value(1) self.b_gamma.set_value(0.5) - def rgb_changed(self, name, val): if name == 'R': channel = self.mixer.RED @@ -346,4 +337,4 @@ class MixerPanel(QtGui.QFrame): self.reset_sliders() if self.callback: - self.callback() \ No newline at end of file + self.callback() diff --git a/skimage/io/_plugins/q_histogram.py b/skimage/io/_plugins/q_histogram.py index 36edb30d..0a60ce9f 100644 --- a/skimage/io/_plugins/q_histogram.py +++ b/skimage/io/_plugins/q_histogram.py @@ -39,7 +39,7 @@ class ColorHistogram(QWidget): orig_height = self.height() # fill perc % of the widget - perc = 1 + perc = 1 width = int(orig_width * perc) height = int(orig_height * perc) @@ -60,14 +60,14 @@ class ColorHistogram(QWidget): remainder = width % nbars bar_width = [int(width / nbars)] * nbars for i in range(remainder): - bar_width[i]+=1 + bar_width[i] += 1 paint = QPainter() paint.begin(self) # determine the scaling factor max_val = np.max(self.counts) - scale = 1. * height / max_val + scale = 1. * height / max_val # determine if we have a colormap and drop into the appopriate # loop. @@ -95,7 +95,6 @@ class ColorHistogram(QWidget): paint.end() - def update_hist(self, counts, cmap): self._validate_input(counts, cmap) self.counts = counts @@ -121,17 +120,17 @@ class QuadHistogram(QFrame): self.b_hist = ColorHistogram(b, (0, 0, 255)) self.v_hist = ColorHistogram(v, (0, 0, 0)) - self.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) + self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.layout = QGridLayout(self) self.layout.setMargin(0) order_map = {'R': self.r_hist, 'G': self.g_hist, 'B': self.b_hist, 'V': self.v_hist} - if layout=='vertical': + if layout == 'vertical': for i in range(len(order)): self.layout.addWidget(order_map[order[i]], i, 0) - elif layout=='horizontal': + elif layout == 'horizontal': for i in range(len(order)): self.layout.addWidget(order_map[order[i]], 0, i) @@ -140,4 +139,4 @@ class QuadHistogram(QFrame): self.r_hist.update_hist(r, (255, 0, 0)) self.g_hist.update_hist(g, (0, 255, 0)) self.b_hist.update_hist(b, (0, 0, 255)) - self.v_hist.update_hist(v, (0, 0, 0)) \ No newline at end of file + self.v_hist.update_hist(v, (0, 0, 0)) diff --git a/skimage/io/_plugins/skivi.py b/skimage/io/_plugins/skivi.py index 440e3c2c..241bdf94 100644 --- a/skimage/io/_plugins/skivi.py +++ b/skimage/io/_plugins/skivi.py @@ -69,7 +69,7 @@ class ImageLabel(QLabel): class RGBHSVDisplay(QFrame): def __init__(self): QFrame.__init__(self) - self.setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Sunken) + self.setFrameStyle(QtGui.QFrame.Box | QtGui.QFrame.Sunken) self.posx_label = QLabel('X-pos:') self.posx_value = QLabel() @@ -118,7 +118,6 @@ class RGBHSVDisplay(QFrame): self.v_value.setText(str(v)[:5]) - class SkiviImageWindow(QMainWindow): def __init__(self, arr, mgr): QMainWindow.__init__(self) @@ -132,7 +131,8 @@ class SkiviImageWindow(QMainWindow): self.label = ImageLabel(self, arr) self.label_container = QFrame() - self.label_container.setFrameShape(QtGui.QFrame.StyledPanel|QtGui.QFrame.Sunken) + self.label_container.setFrameShape( + QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken) self.label_container.setLineWidth(1) self.label_container.layout = QtGui.QGridLayout(self.label_container) @@ -171,7 +171,6 @@ class SkiviImageWindow(QMainWindow): self.layout.addWidget(self.save_stack, 1, 1) self.layout.addWidget(self.save_file, 1, 2) - def closeEvent(self, event): # Allow window to be destroyed by removing any # references to it @@ -206,14 +205,13 @@ class SkiviImageWindow(QMainWindow): if x >= maxw or y >= maxh or x < 0 or y < 0: r = g = b = h = s = v = '' else: - r = self.arr[y,x,0] - g = self.arr[y,x,1] - b = self.arr[y,x,2] + r = self.arr[y, x, 0] + g = self.arr[y, x, 1] + b = self.arr[y, x, 2] h, s, v = self.mixer_panel.mixer.rgb_2_hsv_pixel(r, g, b) self.rgb_hsv_disp.update_vals((x, y, r, g, b, h, s, v)) - def save_to_stack(self): from skimage import io img = self.arr.copy() @@ -238,5 +236,3 @@ class SkiviImageWindow(QMainWindow): if len(filename) == 0: return io.imsave(filename, self.arr) - - diff --git a/skimage/io/_plugins/test_plugin.py b/skimage/io/_plugins/test_plugin.py index 2956f14f..18f750f3 100644 --- a/skimage/io/_plugins/test_plugin.py +++ b/skimage/io/_plugins/test_plugin.py @@ -1,18 +1,22 @@ # This mock-up is called by ../tests/test_plugin.py # to verify the behaviour of the plugin infrastructure + def imread(fname, dtype=None): assert fname == 'test.png' assert dtype == 'i4' + def imsave(fname, arr): assert fname == 'test.png' assert arr == [1, 2, 3] + def imshow(arr, plugin_arg=None): assert arr == [1, 2, 3] assert plugin_arg == (1, 2) + def imread_collection(x, conserve_memory=True): assert conserve_memory == False assert x == '*.png' diff --git a/skimage/io/_plugins/util.py b/skimage/io/_plugins/util.py index 7eb109b7..ed547222 100644 --- a/skimage/io/_plugins/util.py +++ b/skimage/io/_plugins/util.py @@ -12,6 +12,7 @@ try: except: CPU_COUNT = 2 + class GuiLockError(Exception): def __init__(self, msg): self.msg = msg @@ -19,6 +20,7 @@ class GuiLockError(Exception): def __str__(self): return self.msg + class WindowManager(object): ''' A class to keep track of spawned windows, and make any needed callback once all the windows, @@ -62,7 +64,8 @@ class WindowManager(object): self._gui_lock = False self._guikit = '' else: - raise RuntimeError('Only the toolkit that owns the lock may release it') + raise RuntimeError('Only the toolkit that owns the lock may ' + 'release it') def add_window(self, win): self._check_locked() @@ -138,13 +141,13 @@ def prepare_for_display(npy_img): if npy_img.ndim == 2 or \ (npy_img.ndim == 3 and npy_img.shape[2] == 1): npy_plane = npy_img.reshape((height, width)) - out[:,:,0] = npy_plane - out[:,:,1] = npy_plane - out[:,:,2] = npy_plane + out[:, :, 0] = npy_plane + out[:, :, 1] = npy_plane + out[:, :, 2] = npy_plane elif npy_img.ndim == 3: if npy_img.shape[2] == 3 or npy_img.shape[2] == 4: - out[:,:,:3] = npy_img[:,:,:3] + out[:, :, :3] = npy_img[:, :, :3] else: raise ValueError('Image must have 1, 3, or 4 channels') @@ -184,10 +187,10 @@ class ImgThread(threading.Thread): def run(self): self.func(*self.args) + class ThreadDispatch(object): def __init__(self, img, stateimg, func, *args): - width = img.shape[1] height = img.shape[0] self.cores = CPU_COUNT self.threads = [] @@ -197,21 +200,21 @@ class ThreadDispatch(object): self.chunks.append((img, stateimg)) elif self.cores >= 4: - self.chunks.append((img[:(height/4), :, :], - stateimg[:(height/4), :, :])) - self.chunks.append((img[(height/4):(height/2), :, :], - stateimg[(height/4):(height/2), :, :])) - self.chunks.append((img[(height/2):(3*height/4), :, :], - stateimg[(height/2):(3*height/4), :, :])) - self.chunks.append((img[(3*height/4):, :, :], - stateimg[(3*height/4):, :, :])) + self.chunks.append((img[:(height / 4), :, :], + stateimg[:(height / 4), :, :])) + self.chunks.append((img[(height / 4):(height / 2), :, :], + stateimg[(height / 4):(height / 2), :, :])) + self.chunks.append((img[(height / 2):(3 * height / 4), :, :], + stateimg[(height / 2):(3 * height / 4), :, :])) + self.chunks.append((img[(3 * height / 4):, :, :], + stateimg[(3 * height / 4):, :, :])) # if they dont have 1, or 4 or more, 2 is good. else: - self.chunks.append((img[:(height/2), :, :], - stateimg[:(height/2), :, :])) - self.chunks.append((img[(height/2):, :, :], - stateimg[(height/2):, :, :])) + self.chunks.append((img[:(height / 2), :, :], + stateimg[:(height / 2), :, :])) + self.chunks.append((img[(height / 2):, :, :], + stateimg[(height / 2):, :, :])) for i in range(len(self.chunks)): self.threads.append(ImgThread(func, self.chunks[i][0], @@ -224,7 +227,6 @@ class ThreadDispatch(object): t.join() - class ColorMixer(object): ''' a class to manage mixing colors in an image. The input array must be an RGB uint8 image. @@ -300,8 +302,6 @@ class ColorMixer(object): _colormixer.add, channel, ammount) pool.run() - - def multiply(self, channel, ammount): '''Mutliply the indicated channel by the specified value. @@ -320,7 +320,6 @@ class ColorMixer(object): _colormixer.multiply, channel, ammount) pool.run() - def brightness(self, factor, offset): '''Adjust the brightness off an image with an offset and factor. @@ -338,13 +337,11 @@ class ColorMixer(object): _colormixer.brightness, factor, offset) pool.run() - def sigmoid_gamma(self, alpha, beta): pool = ThreadDispatch(self.img, self.stateimg, _colormixer.sigmoid_gamma, alpha, beta) pool.run() - def gamma(self, gamma): pool = ThreadDispatch(self.img, self.stateimg, _colormixer.gamma, gamma) @@ -435,4 +432,3 @@ class ColorMixer(object): ''' R, G, B = _colormixer.py_hsv_2_rgb(H, S, V) return (R, G, B) - diff --git a/skimage/io/collection.py b/skimage/io/collection.py index 19d3ab57..e698b52b 100644 --- a/skimage/io/collection.py +++ b/skimage/io/collection.py @@ -118,7 +118,8 @@ class MultiImage(object): if -numframes <= n < numframes: n = n % numframes else: - raise IndexError("There are only %s frames in the image"%numframes) + raise IndexError("There are only %s frames in the image" + % numframes) if self.conserve_memory: if not self._cached == n: @@ -279,7 +280,8 @@ class ImageCollection(object): if -num <= n < num: n = n % num else: - raise IndexError("There are only %s images in the collection"%num) + raise IndexError("There are only %s images in the collection" + % num) return n def __iter__(self): diff --git a/skimage/io/setup.py b/skimage/io/setup.py index d8db9e66..55526461 100644 --- a/skimage/io/setup.py +++ b/skimage/io/setup.py @@ -6,6 +6,7 @@ import os.path base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -30,10 +31,10 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Image I/O Routines', - url = 'https://github.com/scikits-image/scikits-image', - license = 'Modified BSD', + setup(maintainer='scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Image I/O Routines', + url='https://github.com/scikits-image/scikits-image', + license='Modified BSD', **(configuration(top_path='').todict()) ) diff --git a/skimage/io/sift.py b/skimage/io/sift.py index e594da4b..d80ba427 100644 --- a/skimage/io/sift.py +++ b/skimage/io/sift.py @@ -10,6 +10,7 @@ __all__ = ['load_sift', 'load_surf'] import numpy as np + def _sift_read(f, mode='SIFT'): """Read SIFT or SURF features from a file. @@ -56,9 +57,11 @@ def _sift_read(f, mode='SIFT'): return data.view(datatype) + def load_sift(f): return _sift_read(f, mode='SIFT') + def load_surf(f): return _sift_read(f, mode='SURF') diff --git a/skimage/io/tests/test_collection.py b/skimage/io/tests/test_collection.py index fa311e3e..0d420ae7 100644 --- a/skimage/io/tests/test_collection.py +++ b/skimage/io/tests/test_collection.py @@ -41,7 +41,7 @@ class TestImageCollection(): def return_img(n): return self.collection[n] assert_raises(IndexError, return_img, num) - assert_raises(IndexError, return_img, -num-1) + assert_raises(IndexError, return_img, -num - 1) def test_files_property(self): assert isinstance(self.collection.files, list) @@ -52,6 +52,7 @@ class TestImageCollection(): def test_custom_load(self): load_pattern = [(1, 'one'), (2, 'two')] + def load_fn(x): return x @@ -83,7 +84,7 @@ class TestMultiImage(): def return_img(n): return self.img[n] assert_raises(IndexError, return_img, num) - assert_raises(IndexError, return_img, -num-1) + assert_raises(IndexError, return_img, -num - 1) @skipif(not PIL_available) def test_files_property(self): @@ -102,10 +103,5 @@ class TestMultiImage(): assert_raises(AttributeError, set_mem, True) - - - - if __name__ == "__main__": run_module_suite() - diff --git a/skimage/io/tests/test_colormixer.py b/skimage/io/tests/test_colormixer.py index bf0363c5..b9f362a2 100644 --- a/skimage/io/tests/test_colormixer.py +++ b/skimage/io/tests/test_colormixer.py @@ -3,6 +3,7 @@ import numpy as np import skimage.io._plugins._colormixer as cm + class ColorMixerTest(object): def setup(self): self.state = np.ones((18, 33, 3), dtype=np.uint8) * 200 @@ -91,7 +92,8 @@ class TestColorMixer(object): def test_gamma(self): gamma = 1.5 cm.gamma(self.img, self.state, gamma) - img = np.asarray(((self.state/255.)**(1/gamma))*255, dtype='uint8') + img = np.asarray(((self.state / 255.)**(1 / gamma)) * 255, + dtype='uint8') assert_array_almost_equal(img, self.img) def test_rgb_2_hsv(self): @@ -112,7 +114,6 @@ class TestColorMixer(object): assert_almost_equal(np.array([g]), np.array([0])) assert_almost_equal(np.array([b]), np.array([0])) - def test_hsv_add(self): cm.hsv_add(self.img, self.state, 360, 0, 0) assert_almost_equal(self.img, self.state) @@ -123,7 +124,7 @@ class TestColorMixer(object): def test_hsv_add_clip_pos(self): cm.hsv_add(self.img, self.state, 0, 0, 1) - assert_equal(self.img, np.ones_like(self.state)*255) + assert_equal(self.img, np.ones_like(self.state) * 255) def test_hsv_mul(self): cm.hsv_multiply(self.img, self.state, 360, 1, 1) @@ -134,7 +135,5 @@ class TestColorMixer(object): assert_equal(self.img, np.zeros_like(self.state)) - - if __name__ == "__main__": run_module_suite() diff --git a/skimage/io/tests/test_fits.py b/skimage/io/tests/test_fits.py index bf918882..d432b611 100644 --- a/skimage/io/tests/test_fits.py +++ b/skimage/io/tests/test_fits.py @@ -15,6 +15,7 @@ except ImportError: else: import skimage.io._plugins.fits_plugin as fplug + def test_fits_plugin_import(): # Make sure we get an import exception if PyFITS isn't there # (not sure how useful this is, but it ensures there isn't some other @@ -36,14 +37,16 @@ def test_imread_MEF(): io.use_plugin('fits') testfile = os.path.join(data_dir, 'multi.fits') img = io.imread(testfile) - assert np.all(img==pyfits.getdata(testfile, 1)) + assert np.all(img == pyfits.getdata(testfile, 1)) + @skipif(not pyfits_available) def test_imread_simple(): io.use_plugin('fits') testfile = os.path.join(data_dir, 'simple.fits') img = io.imread(testfile) - assert np.all(img==pyfits.getdata(testfile, 0)) + assert np.all(img == pyfits.getdata(testfile, 0)) + @skipif(not pyfits_available) def test_imread_collection_single_MEF(): @@ -54,6 +57,7 @@ def test_imread_collection_single_MEF(): load_func=fplug.FITSFactory) assert _same_ImageCollection(ic1, ic2) + @skipif(not pyfits_available) def test_imread_collection_MEF_and_simple(): io.use_plugin('fits') @@ -65,6 +69,7 @@ def test_imread_collection_MEF_and_simple(): load_func=fplug.FITSFactory) assert _same_ImageCollection(ic1, ic2) + def _same_ImageCollection(collection1, collection2): """Ancillary function to compare two ImageCollection objects, checking that their constituent arrays are equal. @@ -79,4 +84,3 @@ def _same_ImageCollection(collection1, collection2): if __name__ == '__main__': run_module_suite() - diff --git a/skimage/io/tests/test_freeimage.py b/skimage/io/tests/test_freeimage.py index 3d8d16f4..565f37bd 100644 --- a/skimage/io/tests/test_freeimage.py +++ b/skimage/io/tests/test_freeimage.py @@ -35,7 +35,8 @@ def teardown(): def test_imread(): img = sio.imread(os.path.join(si.data_dir, 'color.png')) assert img.shape == (370, 371, 3) - assert all(img[274,135] == [0, 130, 253]) + assert all(img[274, 135] == [0, 130, 253]) + @skipif(not FI_available) def test_imread_uint16(): @@ -44,6 +45,7 @@ def test_imread_uint16(): assert img.dtype == np.uint16 assert_array_almost_equal(img, expected) + @skipif(not FI_available) def test_imread_uint16_big_endian(): expected = np.load(os.path.join(si.data_dir, 'chessboard_GRAY_U8.npy')) @@ -54,7 +56,7 @@ def test_imread_uint16_big_endian(): class TestSave: def roundtrip(self, dtype, x, suffix): - f = NamedTemporaryFile(suffix='.'+suffix) + f = NamedTemporaryFile(suffix='.' + suffix) fname = f.name f.close() sio.imsave(fname, x) @@ -64,12 +66,12 @@ class TestSave: @skipif(not FI_available) def test_imsave_roundtrip(self): for shape, dtype, format in [ - [(10, 10), (np.uint8, np.uint16), ('tif', 'png')], - [(10, 10), (np.float32,), ('tif',)], - [(10, 10, 3), (np.uint8,), ('png',)], + [(10, 10), (np.uint8, np.uint16), ('tif', 'png')], + [(10, 10), (np.float32,), ('tif',)], + [(10, 10, 3), (np.uint8,), ('png',)], [(10, 10, 4), (np.uint8,), ('png',)] ]: - tests = [(d,f) for d in dtype for f in format] + tests = [(d, f) for d in dtype for f in format] for d, f in tests: x = np.ones(shape, dtype=d) * np.random.random(shape) if not np.issubdtype(d, float): @@ -83,7 +85,8 @@ def test_metadata(): assert meta[('EXIF_MAIN', 'Orientation')] == 1 assert meta[('EXIF_MAIN', 'Software')].startswith('ImageMagick') - meta = fi.read_multipage_metadata(os.path.join(si.data_dir, 'multipage.tif')) + meta = fi.read_multipage_metadata(os.path.join(si.data_dir, + 'multipage.tif')) assert len(meta) == 2 assert meta[0][('EXIF_MAIN', 'Orientation')] == 1 assert meta[1][('EXIF_MAIN', 'Software')].startswith('ImageMagick') diff --git a/skimage/io/tests/test_histograms.py b/skimage/io/tests/test_histograms.py index 11602f3a..4ec099e3 100644 --- a/skimage/io/tests/test_histograms.py +++ b/skimage/io/tests/test_histograms.py @@ -4,20 +4,21 @@ import numpy as np import skimage.io._plugins._colormixer as cm from skimage.io._plugins._histograms import histograms + class TestHistogram: def test_basic(self): img = np.ones((50, 50, 3), dtype=np.uint8) r, g, b, v = histograms(img, 255) for band in (r, g, b, v): - yield assert_equal, band.sum(), 50*50 + yield assert_equal, band.sum(), 50 * 50 def test_counts(self): channel = np.arange(255).reshape(51, 5) img = np.empty((51, 5, 3), dtype='uint8') - img[:,:,0] = channel - img[:,:,1] = channel - img[:,:,2] = channel + img[:, :, 0] = channel + img[:, :, 1] = channel + img[:, :, 2] = channel r, g, b, v = histograms(img, 255) assert_array_equal(r, g) assert_array_equal(r, b) diff --git a/skimage/io/tests/test_io.py b/skimage/io/tests/test_io.py index ba05d2d3..72f8496a 100644 --- a/skimage/io/tests/test_io.py +++ b/skimage/io/tests/test_io.py @@ -3,12 +3,14 @@ import numpy as np import skimage.io as io + def test_stack_basic(): x = np.arange(12).reshape(3, 4) io.push(x) assert_array_equal(io.pop(), x) + @raises(ValueError) def test_stack_non_array(): io.push([[1, 2, 3]]) diff --git a/skimage/io/tests/test_pil.py b/skimage/io/tests/test_pil.py index 47126fce..e442c856 100644 --- a/skimage/io/tests/test_pil.py +++ b/skimage/io/tests/test_pil.py @@ -32,6 +32,7 @@ def setup_module(self): except ImportError: pass + @skipif(not PIL_available) def test_imread_flatten(): # a color image is flattened @@ -42,6 +43,7 @@ def test_imread_flatten(): # check that flattening does not occur for an image that is grey already. assert np.sctype2char(img.dtype) in np.typecodes['AllInteger'] + @skipif(not PIL_available) def test_imread_palette(): img = imread(os.path.join(data_dir, 'palette_gray.png')) @@ -49,6 +51,7 @@ def test_imread_palette(): img = imread(os.path.join(data_dir, 'palette_color.png')) assert img.ndim == 3 + @skipif(not PIL_available) def test_palette_is_gray(): from PIL import Image @@ -57,6 +60,7 @@ def test_palette_is_gray(): color = Image.open(os.path.join(data_dir, 'palette_color.png')) assert not _palette_is_grayscale(color) + @skipif(not PIL_available) def test_bilevel(): expected = np.zeros((10, 10)) @@ -65,6 +69,7 @@ def test_bilevel(): img = imread(os.path.join(data_dir, 'checker_bilevel.png')) assert_array_equal(img, expected) + @skipif(not PIL_available) def test_imread_uint16(): expected = np.load(os.path.join(data_dir, 'chessboard_GRAY_U8.npy')) @@ -72,6 +77,7 @@ def test_imread_uint16(): assert np.issubdtype(img.dtype, np.uint16) assert_array_almost_equal(img, expected) + # Big endian images not correctly loaded for PIL < 1.1.7 # Renable test when PIL 1.1.7 is more common. @skipif(True) diff --git a/skimage/io/tests/test_plugin.py b/skimage/io/tests/test_plugin.py index 6480c922..28d8c2b3 100644 --- a/skimage/io/tests/test_plugin.py +++ b/skimage/io/tests/test_plugin.py @@ -20,11 +20,13 @@ except OSError: def setup_module(self): - plugin.use('test') # see ../_plugins/test_plugin.py + plugin.use('test') # see ../_plugins/test_plugin.py + def teardown_module(self): io.reset_plugins() + class TestPlugin: def test_read(self): io.imread('test.png', as_grey=True, dtype='i4', plugin='test') diff --git a/skimage/io/tests/test_plugin_util.py b/skimage/io/tests/test_plugin_util.py index 0170fcb1..09b758d8 100644 --- a/skimage/io/tests/test_plugin_util.py +++ b/skimage/io/tests/test_plugin_util.py @@ -3,6 +3,7 @@ from skimage.io._plugins.util import prepare_for_display, WindowManager from numpy.testing import * import numpy as np + class TestPrepareForDisplay: def test_basic(self): prepare_for_display(np.random.random((10, 10))) @@ -12,7 +13,7 @@ class TestPrepareForDisplay: assert x.dtype == np.dtype(np.uint8) def test_grey(self): - x = prepare_for_display(np.arange(12, dtype=float).reshape((4,3))/11.) + x = prepare_for_display(np.arange(12, dtype=float).reshape((4, 3)) / 11) assert_array_equal(x[..., 0], x[..., 2]) assert x[0, 0, 0] == 0 assert x[3, 2, 0] == 255 @@ -31,6 +32,7 @@ class TestPrepareForDisplay: def test_wrong_depth(self): x = prepare_for_display(np.random.random((10, 10, 5))) + class TestWindowManager: callback_called = False diff --git a/skimage/io/tests/test_sift.py b/skimage/io/tests/test_sift.py index b6029461..c488d52f 100644 --- a/skimage/io/tests/test_sift.py +++ b/skimage/io/tests/test_sift.py @@ -7,6 +7,7 @@ import os from skimage.io import load_sift, load_surf + def test_load_sift(): f = NamedTemporaryFile(delete=False) fname = f.name @@ -40,6 +41,7 @@ def test_load_sift(): assert_equal(features['row'][0], 133.92) assert_equal(features['column'][1], 99.75) + def test_load_surf(): f = NamedTemporaryFile(delete=False) fname = f.name diff --git a/skimage/io/video.py b/skimage/io/video.py index 365589c1..6cb1a8e9 100644 --- a/skimage/io/video.py +++ b/skimage/io/video.py @@ -1,11 +1,12 @@ import numpy as np -import os, time +import os from skimage.io import ImageCollection try: import pygst pygst.require("0.10") - import gst, gobject + import gst + import gobject gobject.threads_init() from gst.extend.discoverer import Discoverer gstreamer_available = True @@ -36,11 +37,11 @@ class CvVideo(object): self.source = source self.capture = cv.CreateFileCapture(self.source) self.size = size - + def get(self): """ Retrieve a video frame as a numpy array. - + Returns ------- output : array (image) @@ -55,31 +56,34 @@ class CvVideo(object): else: cv.Resize(img, cv.fromarray(img_mat)) # opencv stores images in BGR format - cv.CvtColor(cv.fromarray(img_mat), cv.fromarray(img_mat), cv.CV_BGR2RGB) + cv.CvtColor(cv.fromarray(img_mat), cv.fromarray(img_mat), + cv.CV_BGR2RGB) return img_mat - + def seek_frame(self, frame_number): """ Seek to specified frame in video. - + Parameters ---------- frame_number : int Frame position """ - cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_FRAMES, frame_number) - + cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_FRAMES, + frame_number) + def seek_time(self, milliseconds): """ Seek to specified time in video. - + Parameters ---------- milliseconds : int Time position """ - cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_MSEC, milliseconds) - + cv.SetCaptureProperty(self.capture, cv.CV_CAP_PROP_POS_MSEC, + milliseconds) + def frame_count(self): """ Returns frame count of video. @@ -90,7 +94,7 @@ class CvVideo(object): Frame count. """ return cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FRAME_COUNT) - + def duration(self): """ Returns time length of video in milliseconds. @@ -102,8 +106,8 @@ class CvVideo(object): """ return cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FPS) * \ cv.GetCaptureProperty(self.capture, cv.CV_CAP_PROP_FRAME_COUNT) - - + + class GstVideo(object): """ GStreamer-based video loader. @@ -115,9 +119,9 @@ class GstVideo(object): size: tuple, optional Size of returned array. sync: bool, optional (default False) - Frames are extracted per frame or per time basis. - If enabled the video time step continues onward according to the play rate. - Useful for ip cameras and other real time video feeds. + Frames are extracted per frame or per time basis. If enabled the video + time step continues onward according to the play rate. Useful for ip + cameras and other real time video feeds. """ def __init__(self, source=None, size=None, sync=False): if not gstreamer_available: @@ -127,7 +131,7 @@ class GstVideo(object): self.video_length = 0 self.video_rate = 0 # extract video size - if not size: + if not size: gobject.idle_add(self._discover_one) self.mainloop = gobject.MainLoop() self.mainloop.run() @@ -143,7 +147,7 @@ class GstVideo(object): """ discoverer = Discoverer(self.source) discoverer.connect('discovered', self._discovered) - discoverer.discover() + discoverer.discover() return False def _discovered(self, d, is_media): @@ -152,13 +156,13 @@ class GstVideo(object): """ if is_media: self.size = (d.videowidth, d.videoheight) - self.video_length = d.videolength / gst.MSECOND + self.video_length = d.videolength / gst.MSECOND self.video_rate = d.videorate.num self.mainloop.quit() return False - + def _create_main_pipeline(self, source, size, sync): - """ + """ Create the frame extraction pipeline. """ pipeline_string = "uridecodebin name=decoder uri=%s ! ffmpegcolorspace ! videoscale ! appsink name=play_sink" % self.source @@ -173,42 +177,43 @@ class GstVideo(object): self.appsink.set_property('caps', gst.caps_from_string(caps)) if self.pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE: raise NameError("Failed to load video source %s" % self.source) - buff = self.appsink.emit('pull-preroll') - + self.appsink.emit('pull-preroll') + def get(self): """ Retrieve a video frame as a numpy array. - + Returns ------- output : array (image) Retrieved image. """ buff = self.appsink.emit('pull-buffer') - img_mat = np.ndarray(shape=(self.size[1], self.size[0], 3), dtype=np.uint8, buffer=buff.data) + img_mat = np.ndarray(shape=(self.size[1], self.size[0], 3), + dtype=np.uint8, buffer=buff.data) return img_mat def seek_frame(self, frame_number): """ Seek to specified frame in video. - + Parameters ---------- frame_number : int Frame position """ self.pipeline.seek_simple(gst.FORMAT_DEFAULT, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, frame_number) - + def seek_time(self, milliseconds): """ Seek to specified time in video. - + Parameters ---------- milliseconds : int Time position """ - self.pipeline.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, milliseconds/1000.0 * gst.SECOND) + self.pipeline.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, milliseconds / 1000.0 * gst.SECOND) def frame_count(self): """ @@ -219,8 +224,8 @@ class GstVideo(object): output : int Frame count. """ - return self.video_length/1000*self.video_rate - + return self.video_length / 1000 * self.video_rate + def duration(self): """ Returns time length of video in milliseconds. @@ -236,7 +241,7 @@ class GstVideo(object): class Video(object): """ Video loader. Supports Opencv and Gstreamer backends. - + Parameters ---------- source : str @@ -248,7 +253,7 @@ class Video(object): If enabled the video time step continues onward according to the play rate. Useful for IP cameras and other real time video feeds. backend: str, 'gstreamer' or 'opencv' - Backend to use. + Backend to use. """ def __init__(self, source=None, size=None, sync=False, backend=None): if backend == None: @@ -270,29 +275,29 @@ class Video(object): def get(self): """ Retrieve the next video frame as a numpy array. - + Returns ------- output : array (image) Retrieved image. """ return self.video.get() - + def seek_frame(self, frame_number): """ Seek to specified frame in video. - + Parameters ---------- frame_number : int Frame position """ self.video.seek_frame(frame_number) - + def seek_time(self, milliseconds): """ Seek to specified time in video. - + Parameters ---------- milliseconds : int @@ -310,7 +315,7 @@ class Video(object): Frame count. """ return self.video.frame_count() - + def duration(self): """ Returns time length of video in milliseconds. @@ -321,11 +326,11 @@ class Video(object): Time length [ms]. """ return self.video.duration() - + def get_index_frame(self, frame_number): """ Retrieve a specified video frame as a numpy array. - + Parameters ---------- frame_number : int @@ -335,28 +340,27 @@ class Video(object): ------- output : array (image) Retrieved image. - """ + """ self.video.seek_frame(frame_number) return self.video.get() - + def get_collection(self, time_range=None): """ Returns an ImageCollection object. - + Parameters ---------- time_range: range (int), optional Time steps to extract, defaults to the entire length of video. - + Returns ------- - output: ImageCollection + output: ImageCollection Collection of images iterator. """ if not time_range: time_range = range(int(self.frame_count())) return ImageCollection(time_range, load_func=self.get_index_frame) - - -__all__ = ["Video"] + +__all__ = ["Video"] diff --git a/skimage/measure/_regionprops.py b/skimage/measure/_regionprops.py index 937b80f5..6b8f6a3c 100644 --- a/skimage/measure/_regionprops.py +++ b/skimage/measure/_regionprops.py @@ -201,17 +201,17 @@ def regionprops(label_image, properties=['Area', 'Centroid'], m = _moments.central_moments(array, 0, 0, 3) # centroid - cr = m[0,1] / m[0,0] - cc = m[1,0] / m[0,0] + cr = m[0, 1] / m[0, 0] + cc = m[1, 0] / m[0, 0] mu = _moments.central_moments(array, cr, cc, 3) #: elements of the inertia tensor [a b; b c] - a = mu[2,0] / mu[0,0] - b = mu[1,1] / mu[0,0] - c = mu[0,2] / mu[0,0] + a = mu[2, 0] / mu[0, 0] + b = mu[1, 1] / mu[0, 0] + c = mu[0, 2] / mu[0, 0] #: eigen values of inertia tensor - l1 = (a + c) / 2 + sqrt(4 * b ** 2 + (a - c) ** 2) / 2 - l2 = (a + c) / 2 - sqrt(4 * b ** 2 + (a - c) ** 2) / 2 + l1 = (a + c) / 2 + sqrt(4 * b**2 + (a - c)**2) / 2 + l2 = (a + c) / 2 - sqrt(4 * b**2 + (a - c)**2) / 2 # cached results which are used by several properties _filled_image = None @@ -219,7 +219,7 @@ def regionprops(label_image, properties=['Area', 'Centroid'], _nu = None if 'Area' in properties: - obj_props['Area'] = m[0,0] + obj_props['Area'] = m[0, 0] if 'BoundingBox' in properties: obj_props['BoundingBox'] = (r0, c0, sl[0].stop, sl[1].stop) @@ -247,17 +247,17 @@ def regionprops(label_image, properties=['Area', 'Centroid'], obj_props['Eccentricity'] = sqrt(1 - l2 / l1) if 'EquivDiameter' in properties: - obj_props['EquivDiameter'] = sqrt(4 * m[0,0] / PI) + obj_props['EquivDiameter'] = sqrt(4 * m[0, 0] / PI) if 'EulerNumber' in properties: if _filled_image is None: _filled_image = ndimage.binary_fill_holes(array, STREL_8) euler_array = _filled_image != array _, num = ndimage.label(euler_array, STREL_8) - obj_props['EulerNumber'] = - num + obj_props['EulerNumber'] = - num if 'Extent' in properties: - obj_props['Extent'] = m[0,0] / (array.shape[0] * array.shape[1]) + obj_props['Extent'] = m[0, 0] / (array.shape[0] * array.shape[1]) if 'HuMoments' in properties: if _nu is None: @@ -300,16 +300,15 @@ def regionprops(label_image, properties=['Area', 'Centroid'], if 'Solidity' in properties: if _convex_image is None: _convex_image = convex_hull_image(array) - obj_props['Solidity'] = m[0,0] / np.sum(_convex_image) - + obj_props['Solidity'] = m[0, 0] / np.sum(_convex_image) if intensity_image is not None: weighted_array = array * intensity_image[sl] wm = _moments.central_moments(weighted_array, 0, 0, 3) # weighted centroid - wcr = wm[0,1] / wm[0,0] - wcc = wm[1,0] / wm[0,0] + wcr = wm[0, 1] / wm[0, 0] + wcc = wm[1, 0] / wm[0, 0] wmu = _moments.central_moments(weighted_array, wcr, wcc, 3) # cached results which are used by several properties diff --git a/skimage/measure/find_contours.py b/skimage/measure/find_contours.py index 92f848d2..68e1d53c 100755 --- a/skimage/measure/find_contours.py +++ b/skimage/measure/find_contours.py @@ -5,6 +5,7 @@ from collections import deque _param_options = ('high', 'low') + def find_contours(array, level, fully_connected='low', positive_orientation='low'): """Find iso-valued contours in a 2D array for a given level value. @@ -83,10 +84,10 @@ def find_contours(array, level, This means that to find reasonable contours, it is best to find contours midway between the expected "light" and "dark" values. In particular, - given a binarized array, *do not* choose to find contours at the low or high - value of the array. This will often yield degenerate contours, especially - around structures that are a single array element wide. Instead choose - a middle value, as above. + given a binarized array, *do not* choose to find contours at the low or + high value of the array. This will often yield degenerate contours, + especially around structures that are a single array element wide. Instead + choose a middle value, as above. References ---------- @@ -101,8 +102,8 @@ def find_contours(array, level, level = float(level) if (fully_connected not in _param_options or positive_orientation not in _param_options): - raise ValueError('Parameters "fully_connected" and' - ' "positive_orientation" must be either "high" or "low".') + raise ValueError('Parameters "fully_connected" and' + ' "positive_orientation" must be either "high" or "low".') point_list = _find_contours.iterate_and_store(array, level, fully_connected == 'high') contours = _assemble_contours(_take_2(point_list)) @@ -110,12 +111,14 @@ def find_contours(array, level, contours = [c[::-1] for c in contours] return contours + def _take_2(seq): - iterator = iter(seq) - while(True): - n1 = iterator.next() - n2 = iterator.next() - yield (n1, n2) + iterator = iter(seq) + while(True): + n1 = iterator.next() + n2 = iterator.next() + yield (n1, n2) + def _assemble_contours(points_iterator): current_index = 0 @@ -127,7 +130,8 @@ def _assemble_contours(points_iterator): # This happens when (and only when) one vertex of the square is # exactly the contour level, and the rest are above or below. # This degnerate vertex will be picked up later by neighboring squares. - if from_point == to_point: continue + if from_point == to_point: + continue tail_data = starts.get(to_point) head_data = ends.get(from_point) @@ -143,7 +147,7 @@ def _assemble_contours(points_iterator): head.append(to_point) del starts[to_point] del ends[from_point] - else: # tail is not head + else: # tail is not head # We need to join two distinct contours. # We want to keep the first contour segment created, so that # the final contours are ordered left->right, top->bottom. @@ -157,7 +161,7 @@ def _assemble_contours(points_iterator): # remove the old end of head and add the new end. del ends[from_point] ends[head[-1]] = (head, head_num) - else: # tail_num <= head_num + else: # tail_num <= head_num # head was created second. Prepend head to tail. tail.extendleft(reversed(head)) # remove all traces of head: diff --git a/skimage/measure/setup.py b/skimage/measure/setup.py index 4b02a1d1..7e4ecf0f 100644 --- a/skimage/measure/setup.py +++ b/skimage/measure/setup.py @@ -5,6 +5,7 @@ from skimage._build import cython import os base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -23,10 +24,10 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Graph-based Image-processing Algorithms', - url = 'https://github.com/scikits-image/scikits-image', - license = 'Modified BSD', + setup(maintainer='scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Graph-based Image-processing Algorithms', + url='https://github.com/scikits-image/scikits-image', + license='Modified BSD', **(configuration(top_path='').todict()) ) diff --git a/skimage/measure/tests/test_find_contours.py b/skimage/measure/tests/test_find_contours.py index 8d705878..fbc502f7 100644 --- a/skimage/measure/tests/test_find_contours.py +++ b/skimage/measure/tests/test_find_contours.py @@ -1,9 +1,9 @@ import numpy as np from numpy.testing import * -from skimage.measure import find_contours +from skimage.measure import find_contours -a = np.ones((8,8), dtype=np.float32) +a = np.ones((8, 8), dtype=np.float32) a[1:-1, 1] = 0 a[1, 1:-1] = 0 @@ -16,43 +16,45 @@ a[1, 1:-1] = 0 ## [ 1., 0., 1., 1., 1., 1., 1., 1.], ## [ 1., 1., 1., 1., 1., 1., 1., 1.]], dtype=float32) -x,y = np.mgrid[-1:1:5j,-1:1:5j] +x, y = np.mgrid[-1:1:5j, -1:1:5j] r = np.sqrt(x**2 + y**2) + def test_binary(): - contours = find_contours(a, 0.5) - assert len(contours) == 1 - assert_array_equal(contours[0], - [[ 6. , 1.5], - [ 5. , 1.5], - [ 4. , 1.5], - [ 3. , 1.5], - [ 2. , 1.5], - [ 1.5, 2. ], - [ 1.5, 3. ], - [ 1.5, 4. ], - [ 1.5, 5. ], - [ 1.5, 6. ], - [ 1. , 6.5], - [ 0.5, 6. ], - [ 0.5, 5. ], - [ 0.5, 4. ], - [ 0.5, 3. ], - [ 0.5, 2. ], - [ 0.5, 1. ], - [ 1. , 0.5], - [ 2. , 0.5], - [ 3. , 0.5], - [ 4. , 0.5], - [ 5. , 0.5], - [ 6. , 0.5], - [ 6.5, 1. ], - [ 6. , 1.5]]) + contours = find_contours(a, 0.5) + assert len(contours) == 1 + assert_array_equal(contours[0], + [[6. , 1.5], + [5. , 1.5], + [4. , 1.5], + [3. , 1.5], + [2. , 1.5], + [1.5, 2. ], + [1.5, 3. ], + [1.5, 4. ], + [1.5, 5. ], + [1.5, 6. ], + [1. , 6.5], + [0.5, 6. ], + [0.5, 5. ], + [0.5, 4. ], + [0.5, 3. ], + [0.5, 2. ], + [0.5, 1. ], + [1. , 0.5], + [2. , 0.5], + [3. , 0.5], + [4. , 0.5], + [5. , 0.5], + [6. , 0.5], + [6.5, 1. ], + [6. , 1.5]]) + def test_float(): - contours = find_contours(r, 0.5) - assert len(contours) == 1 - assert_array_equal(contours[0], + contours = find_contours(r, 0.5) + assert len(contours) == 1 + assert_array_equal(contours[0], [[ 2., 3.], [ 1., 2.], [ 2., 1.], @@ -60,7 +62,6 @@ def test_float(): [ 2., 3.]]) - if __name__ == '__main__': from numpy.testing import run_module_suite run_module_suite() diff --git a/skimage/measure/tests/test_regionprops.py b/skimage/measure/tests/test_regionprops.py index 4d040b5c..b9b16dff 100644 --- a/skimage/measure/tests/test_regionprops.py +++ b/skimage/measure/tests/test_regionprops.py @@ -18,19 +18,20 @@ SAMPLE = np.array( [0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]] ) INTENSITY_SAMPLE = SAMPLE.copy() -INTENSITY_SAMPLE[1,9:11] = 2 +INTENSITY_SAMPLE[1, 9:11] = 2 def test_area(): area = regionprops(SAMPLE, ['Area'])[0]['Area'] assert area == np.sum(SAMPLE) + def test_bbox(): bbox = regionprops(SAMPLE, ['BoundingBox'])[0]['BoundingBox'] assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1])) SAMPLE_mod = SAMPLE.copy() - SAMPLE_mod[:,-1] = 0 + SAMPLE_mod[:, -1] = 0 bbox = regionprops(SAMPLE_mod, ['BoundingBox'])[0]['BoundingBox'] assert_array_almost_equal(bbox, (0, 0, SAMPLE.shape[0], SAMPLE.shape[1]-1)) @@ -51,11 +52,13 @@ def test_centroid(): # determined with MATLAB assert_array_almost_equal(centroid, (5.66666666666666, 9.444444444444444)) + def test_convex_area(): area = regionprops(SAMPLE, ['ConvexArea'])[0]['ConvexArea'] # determined with MATLAB assert area == 124 + def test_convex_image(): img = regionprops(SAMPLE, ['ConvexImage'])[0]['ConvexImage'] # determined with MATLAB @@ -73,28 +76,33 @@ def test_convex_image(): ) assert_array_equal(img, ref) + def test_eccentricity(): eps = regionprops(SAMPLE, ['Eccentricity'])[0]['Eccentricity'] assert_almost_equal(eps, 0.814629313427) + def test_equiv_diameter(): diameter = regionprops(SAMPLE, ['EquivDiameter'])[0]['EquivDiameter'] # determined with MATLAB assert_almost_equal(diameter, 9.57461472963) + def test_euler_number(): en = regionprops(SAMPLE, ['EulerNumber'])[0]['EulerNumber'] assert en == 0 SAMPLE_mod = SAMPLE.copy() - SAMPLE_mod[7,-3] = 0 + SAMPLE_mod[7, -3] = 0 en = regionprops(SAMPLE_mod, ['EulerNumber'])[0]['EulerNumber'] assert en == -1 + def test_extent(): extent = regionprops(SAMPLE, ['Extent'])[0]['Extent'] assert_almost_equal(extent, 0.4) + def test_hu_moments(): hu = regionprops(SAMPLE, ['HuMoments'])[0]['HuMoments'] ref = np.array([ @@ -109,46 +117,54 @@ def test_hu_moments(): # bug in OpenCV caused in Central Moments calculation? assert_array_almost_equal(hu, ref) + def test_image(): img = regionprops(SAMPLE, ['Image'])[0]['Image'] assert_array_equal(img, SAMPLE) + def test_filled_area(): area = regionprops(SAMPLE, ['FilledArea'])[0]['FilledArea'] assert area == np.sum(SAMPLE) SAMPLE_mod = SAMPLE.copy() - SAMPLE_mod[7,-3] = 0 + SAMPLE_mod[7, -3] = 0 area = regionprops(SAMPLE_mod, ['FilledArea'])[0]['FilledArea'] assert area == np.sum(SAMPLE) + def test_major_axis_length(): length = regionprops(SAMPLE, ['MajorAxisLength'])[0]['MajorAxisLength'] # MATLAB has different interpretation of ellipse than found in literature, # here implemented as found in literature assert_almost_equal(length, 16.7924234999) + def test_max_intensity(): intensity = regionprops(SAMPLE, ['MaxIntensity'], INTENSITY_SAMPLE )[0]['MaxIntensity'] assert_almost_equal(intensity, 2) + def test_mean_intensity(): intensity = regionprops(SAMPLE, ['MeanIntensity'], INTENSITY_SAMPLE )[0]['MeanIntensity'] assert_almost_equal(intensity, 1.02777777777777) + def test_min_intensity(): intensity = regionprops(SAMPLE, ['MinIntensity'], INTENSITY_SAMPLE )[0]['MinIntensity'] assert_almost_equal(intensity, 1) + def test_minor_axis_length(): length = regionprops(SAMPLE, ['MinorAxisLength'])[0]['MinorAxisLength'] # MATLAB has different interpretation of ellipse than found in literature, # here implemented as found in literature assert_almost_equal(length, 9.739302807263) + def test_moments(): m = regionprops(SAMPLE, ['Moments'])[0]['Moments'] #: determined with OpenCV @@ -199,11 +215,13 @@ def test_weighted_central_moments(): np.set_printoptions(precision=10) assert_array_almost_equal(wmu, ref) + def test_weighted_centroid(): centroid = regionprops(SAMPLE, ['WeightedCentroid'], INTENSITY_SAMPLE )[0]['WeightedCentroid'] assert_array_almost_equal(centroid, (5.540540540540, 9.445945945945)) + def test_weighted_hu_moments(): whu = regionprops(SAMPLE, ['WeightedHuMoments'], INTENSITY_SAMPLE )[0]['WeightedHuMoments'] @@ -218,6 +236,7 @@ def test_weighted_hu_moments(): ]) assert_array_almost_equal(whu, ref) + def test_weighted_moments(): wm = regionprops(SAMPLE, ['WeightedMoments'], INTENSITY_SAMPLE )[0]['WeightedMoments'] diff --git a/skimage/morphology/convex_hull.py b/skimage/morphology/convex_hull.py index f461dc4d..6c4797ef 100644 --- a/skimage/morphology/convex_hull.py +++ b/skimage/morphology/convex_hull.py @@ -4,6 +4,7 @@ import numpy as np from ._pnpoly import points_inside_poly, grid_points_inside_poly from ._convex_hull import possible_hull + def convex_hull_image(image): """Compute the convex hull image of a binary image. @@ -34,7 +35,7 @@ def convex_hull_image(image): # hull. coords = possible_hull(image.astype(np.uint8)) N = len(coords) - + # Add a vertex for the middle of each pixel edge coords_corners = np.empty((N * 4, 2)) for i, (x_offset, y_offset) in enumerate(zip((0, 0, -0.5, 0.5), @@ -61,5 +62,5 @@ def convex_hull_image(image): # For each pixel coordinate, check whether that pixel # lies inside the convex hull mask = grid_points_inside_poly(image.shape[:2], v) - + return mask diff --git a/skimage/morphology/grey.py b/skimage/morphology/grey.py index ee8542dd..309fb5be 100644 --- a/skimage/morphology/grey.py +++ b/skimage/morphology/grey.py @@ -71,7 +71,7 @@ def erosion(image, selem, out=None, shift_x=False, shift_y=False): import skimage.morphology.cmorph as cmorph out = cmorph.erode(image, selem, out=out, shift_x=shift_x, shift_y=shift_y) - return out; + return out except ImportError: raise ImportError("cmorph extension not available.") @@ -130,7 +130,7 @@ def dilation(image, selem, out=None, shift_x=False, shift_y=False): from . import cmorph out = cmorph.dilate(image, selem, out=out, shift_x=shift_x, shift_y=shift_y) - return out; + return out except ImportError: raise ImportError("cmorph extension not available.") @@ -342,23 +342,27 @@ def greyscale_erode(*args, **kwargs): warnings.warn("`greyscale_erode` renamed `erosion`.") return erosion(*args, **kwargs) + def greyscale_dilate(*args, **kwargs): warnings.warn("`greyscale_dilate` renamed `dilation`.") return dilation(*args, **kwargs) + def greyscale_open(*args, **kwargs): warnings.warn("`greyscale_open` renamed `opening`.") return opening(*args, **kwargs) + def greyscale_close(*args, **kwargs): warnings.warn("`greyscale_close` renamed `closing`.") return closing(*args, **kwargs) + def greyscale_white_top_hat(*args, **kwargs): warnings.warn("`greyscale_white_top_hat` renamed `white_tophat`.") return white_tophat(*args, **kwargs) + def greyscale_black_top_hat(*args, **kwargs): warnings.warn("`greyscale_black_top_hat` renamed `black_tophat`.") return black_tophat(*args, **kwargs) - diff --git a/skimage/morphology/selem.py b/skimage/morphology/selem.py index ce58fbfa..f2234ffb 100644 --- a/skimage/morphology/selem.py +++ b/skimage/morphology/selem.py @@ -5,6 +5,7 @@ import numpy as np + def square(width, dtype=np.uint8): """ Generates a flat, square-shaped structuring element. Every pixel @@ -30,6 +31,7 @@ def square(width, dtype=np.uint8): """ return np.ones((width, width), dtype=dtype) + def rectangle(width, height, dtype=np.uint8): """ Generates a flat, rectangular-shaped structuring element of a @@ -58,6 +60,7 @@ def rectangle(width, height, dtype=np.uint8): """ return np.ones((width, height), dtype=dtype) + def diamond(radius, dtype=np.uint8): """ Generates a flat, diamond-shaped structuring element of a given @@ -75,22 +78,23 @@ def diamond(radius, dtype=np.uint8): Returns ------- - + selem : ndarray The structuring element where elements of the neighborhood are 1 and 0 otherwise. """ half = radius - (I, J) = np.meshgrid(xrange(0, radius*2+1), xrange(0, radius*2+1)) - s = np.abs(I-half)+np.abs(J-half) + (I, J) = np.meshgrid(xrange(0, radius * 2 + 1), xrange(0, radius * 2 + 1)) + s = np.abs(I - half) + np.abs(J - half) return np.array(s <= radius, dtype=dtype) - + + def disk(radius, dtype=np.uint8): """ Generates a flat, disk-shaped structuring element of a given radius. A pixel is within the neighborhood if the euclidean distance between it and the origin is no greater than a radius. - + Parameters ---------- radius : int @@ -103,9 +107,9 @@ def disk(radius, dtype=np.uint8): ------- selem : ndarray The structuring element where elements of the neighborhood - are 1 and 0 otherwise. + are 1 and 0 otherwise. """ - L = np.linspace(-radius, radius, 2*radius+1) + L = np.linspace(-radius, radius, 2 * radius + 1) (X, Y) = np.meshgrid(L, L) s = X**2 s += Y**2 diff --git a/skimage/morphology/setup.py b/skimage/morphology/setup.py index 6f227a71..fcf33f7f 100644 --- a/skimage/morphology/setup.py +++ b/skimage/morphology/setup.py @@ -5,6 +5,7 @@ from skimage._build import cython base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -35,11 +36,11 @@ def configuration(parent_package='', top_path=None): if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'scikits-image Developers', - author = 'Damian Eads', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Morphology Wrapper', - url = 'https://github.com/scikits-image/scikits-image', - license = 'SciPy License (BSD Style)', + setup(maintainer='scikits-image Developers', + author='Damian Eads', + maintainer_email='scikits-image@googlegroups.com', + description='Morphology Wrapper', + url='https://github.com/scikits-image/scikits-image', + license='SciPy License (BSD Style)', **(configuration(top_path='').todict()) ) diff --git a/skimage/morphology/skeletonize.py b/skimage/morphology/skeletonize.py index e734d8c6..a8d31bdd 100644 --- a/skimage/morphology/skeletonize.py +++ b/skimage/morphology/skeletonize.py @@ -9,31 +9,32 @@ from ._skeletonize import _skeletonize_loop, _table_lookup_index # --------- Skeletonization by morphological thinning --------- + def skeletonize(image): """Return the skeleton of a binary image. - + Thinning is used to reduce each connected component in a binary image - to a single-pixel wide skeleton. - + to a single-pixel wide skeleton. + Parameters ---------- - image : numpy.ndarray - A binary image containing the objects to be skeletonized. '1' - represents foreground, and '0' represents background. It + image : numpy.ndarray + A binary image containing the objects to be skeletonized. '1' + represents foreground, and '0' represents background. It also accepts arrays of boolean values where True is foreground. - + Returns ------- skeleton : ndarray A matrix containing the thinned image. - + See also -------- medial_axis Notes ----- - The algorithm [1] works by making successive passes of the image, + The algorithm [1] works by making successive passes of the image, removing pixels on object borders. This continues until no more pixels can be removed. The image is correlated with a mask that assigns each pixel a number in the range [0...255] @@ -41,18 +42,18 @@ def skeletonize(image): pixels. A look up table is then used to assign the pixels a value of 0, 1, 2 or 3, which are selectively removed during the iterations. - - Note that this algorithm will give different results than a + + Note that this algorithm will give different results than a medial axis transform, which is also often referred to as - "skeletonization". - + "skeletonization". + References ---------- - .. [1] A fast parallel algorithm for thinning digital patterns, + .. [1] A fast parallel algorithm for thinning digital patterns, T. Y. ZHANG and C. Y. SUEN, Communications of the ACM, March 1984, Volume 27, Number 3 - + Examples -------- >>> X, Y = np.ogrid[0:9, 0:9] @@ -78,7 +79,7 @@ def skeletonize(image): [0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=uint8) - + """ # look up table - there is one entry for each of the 2^8=256 possible # combinations of 8 binary neighbours. 1's, 2's and 3's are candidates @@ -97,38 +98,38 @@ def skeletonize(image): # check some properties of the input image: # - 2D - # - binary image with only 0's and 1's + # - binary image with only 0's and 1's if skeleton.ndim != 2: - raise ValueError('Skeletonize requires a 2D array') + raise ValueError('Skeletonize requires a 2D array') if not np.all(np.in1d(skeleton.flat, (0, 1))): raise ValueError('Image contains values other than 0 and 1') - + # create the mask that will assign a unique value based on the # arrangement of neighbouring pixels - mask = np.array([[ 1, 2, 4], + mask = np.array([[1, 2, 4], [128, 0, 8], - [ 64, 32, 16]], np.uint8) + [64, 32, 16]], np.uint8) pixelRemoved = True while pixelRemoved: - pixelRemoved = False; + pixelRemoved = False # assign each pixel a unique value based on its foreground neighbours neighbours = ndimage.correlate(skeleton, mask, mode='constant') - + # ignore background neighbours *= skeleton - + # use LUT to categorize each foreground pixel as a 0, 1, 2 or 3 codes = np.take(lut, neighbours) - + # pass 1 - remove the 1's and 3's code_mask = (codes == 1) - if np.any(code_mask): + if np.any(code_mask): pixelRemoved = True skeleton[code_mask] = 0 code_mask = (codes == 3) - if np.any(code_mask): + if np.any(code_mask): pixelRemoved = True skeleton[code_mask] = 0 @@ -137,11 +138,11 @@ def skeletonize(image): neighbours *= skeleton codes = np.take(lut, neighbours) code_mask = (codes == 2) - if np.any(code_mask): + if np.any(code_mask): pixelRemoved = True skeleton[code_mask] = 0 code_mask = (codes == 3) - if np.any(code_mask): + if np.any(code_mask): pixelRemoved = True skeleton[code_mask] = 0 @@ -159,8 +160,8 @@ def medial_axis(image, mask=None, return_distance=False): Parameters ---------- - image : binary ndarray - + image : binary ndarray + mask : binary ndarray, optional If a mask is given, only those elements with a true value in `mask` are used for computing the medial axis. @@ -177,18 +178,18 @@ def medial_axis(image, mask=None, return_distance=False): dist : ndarray of ints Distance transform of the image (only returned if `return_distance` is True) - + See also -------- - skeletonize + skeletonize Notes ----- This algorithm computes the medial axis transform of an image - as the ridges of its distance transform. - + as the ridges of its distance transform. + The different steps of the algorithm are as follows - * A lookup table is used, that assigns 0 or 1 to each configuration of + * A lookup table is used, that assigns 0 or 1 to each configuration of the 3x3 binary square, whether the central pixel should be removed or kept. We want a point to be removed if it has more than one neighbor and if removing it does not change the number of connected components. @@ -197,12 +198,12 @@ def medial_axis(image, mask=None, return_distance=False): the cornerness of the pixel. * The foreground (value of 1) points are ordered by - the distance transform, then the cornerness. - - * A cython function is called to reduce the image to its skeleton. It - processes pixels in the order determined at the previous step, and - removes or maintains a pixel according to the lookup table. Because - of the ordering, it is possible to process all pixels in only one + the distance transform, then the cornerness. + + * A cython function is called to reduce the image to its skeleton. It + processes pixels in the order determined at the previous step, and + removes or maintains a pixel according to the lookup table. Because + of the ordering, it is possible to process all pixels in only one pass. Examples @@ -234,7 +235,7 @@ def medial_axis(image, mask=None, return_distance=False): masked_image = image.astype(bool).copy() masked_image[~mask] = False # - # Build lookup table - three conditions + # Build lookup table - three conditions # 1. Keep only positive pixels (center_is_foreground array). # AND # 2. Keep if removing the pixel results in a different connectivity @@ -249,30 +250,29 @@ def medial_axis(image, mask=None, return_distance=False): (np.array([ndimage.label(_pattern_of(index), _eight_connect)[1] != ndimage.label(_pattern_of(index & ~ 2**4), _eight_connect)[1] - for index in range(512)]) # condition 2 - | + for index in range(512)]) # condition 2 + | np.array([np.sum(_pattern_of(index)) < 3 for index in range(512)])) # condition 3 ) - - + # Build distance transform distance = ndimage.distance_transform_edt(masked_image) if return_distance: store_distance = distance.copy() - + # Corners # The processing order along the edge is critical to the shape of the # resulting skeleton: if you process a corner first, that corner will # be eroded and the skeleton will miss the arm from that corner. Pixels # with fewer neighbors are more "cornery" and should be processed last. - # We use a cornerness_table lookup table where the score of a + # We use a cornerness_table lookup table where the score of a # configuration is the number of background (0-value) pixels in the # 3x3 neighbourhood cornerness_table = np.array([9 - np.sum(_pattern_of(index)) for index in range(512)]) corner_score = _table_lookup(masked_image, cornerness_table) - + # Define arrays for inner loop i, j = np.mgrid[0:image.shape[0], 0:image.shape[1]] result = masked_image.copy() @@ -280,7 +280,7 @@ def medial_axis(image, mask=None, return_distance=False): i = np.ascontiguousarray(i[result], np.int32) j = np.ascontiguousarray(j[result], np.int32) result = np.ascontiguousarray(result, np.uint8) - + # Determine the order in which pixels are processed. # We use a random # for tiebreaking. Assign each pixel in the image a # predictable, random # so that masking doesn't affect arbitrary choices @@ -305,6 +305,7 @@ def medial_axis(image, mask=None, return_distance=False): else: return result + def _pattern_of(index): """ Return the pattern represented by an index value @@ -317,9 +318,9 @@ def _pattern_of(index): def _table_lookup(image, table): """ - Perform a morphological transform on an image, directed by its + Perform a morphological transform on an image, directed by its neighbors - + Parameters ---------- image : ndarray @@ -329,7 +330,7 @@ def _table_lookup(image, table): the values of that pixel and its 8-connected neighbors. border_value : bool The value of pixels beyond the border of the image. - + Returns ------- result : ndarray of same shape as `image` @@ -338,13 +339,14 @@ def _table_lookup(image, table): Notes ----- The pixels are numbered like this:: - + + 0 1 2 3 4 5 6 7 8 The index at a pixel is the sum of 2** for pixels - that evaluate to true. + that evaluate to true. """ # # We accumulate into the indexer to get the index into the table @@ -356,11 +358,11 @@ def _table_lookup(image, table): indexer[1:, 1:] += image[:-1, :-1] * 2**0 indexer[1:, :] += image[:-1, :] * 2**1 indexer[1:, :-1] += image[:-1, 1:] * 2**2 - + indexer[:, 1:] += image[:, :-1] * 2**3 indexer[:, :] += image[:, :] * 2**4 indexer[:, :-1] += image[:, 1:] * 2**5 - + indexer[:-1, 1:] += image[1:, :-1] * 2**6 indexer[:-1, :] += image[1:, :] * 2**7 indexer[:-1, :-1] += image[1:, 1:] * 2**8 @@ -368,4 +370,3 @@ def _table_lookup(image, table): indexer = _table_lookup_index(np.ascontiguousarray(image, np.uint8)) image = table[indexer] return image - diff --git a/skimage/morphology/tests/test_ccomp.py b/skimage/morphology/tests/test_ccomp.py index cb092b4c..1e0829ca 100644 --- a/skimage/morphology/tests/test_ccomp.py +++ b/skimage/morphology/tests/test_ccomp.py @@ -3,6 +3,7 @@ from numpy.testing import assert_array_equal, run_module_suite from skimage.morphology import label + class TestConnectedComponents: def setup(self): self.x = np.array([[0, 0, 3, 2, 1, 9], diff --git a/skimage/morphology/tests/test_convex_hull.py b/skimage/morphology/tests/test_convex_hull.py index 21f45cd7..9ab514d0 100644 --- a/skimage/morphology/tests/test_convex_hull.py +++ b/skimage/morphology/tests/test_convex_hull.py @@ -10,6 +10,7 @@ try: except ImportError: scipy_spatial = False + @skipif(not scipy_spatial) def test_basic(): image = np.array( @@ -30,6 +31,7 @@ def test_basic(): assert_array_equal(convex_hull_image(image), expected) + @skipif(not scipy_spatial) def test_possible_hull(): image = np.array( diff --git a/skimage/morphology/tests/test_grey.py b/skimage/morphology/tests/test_grey.py index 1103b20d..d7242e8f 100644 --- a/skimage/morphology/tests/test_grey.py +++ b/skimage/morphology/tests/test_grey.py @@ -11,6 +11,7 @@ from skimage.morphology import selem lena = np.load(os.path.join(data_dir, 'lena_GRAY_U8.npy')) + class TestMorphology(): def morph_worker(self, img, fn, morph_func, strel_func): @@ -155,4 +156,3 @@ class TestDTypes(): if __name__ == '__main__': testing.run_module_suite() - diff --git a/skimage/morphology/tests/test_pnpoly.py b/skimage/morphology/tests/test_pnpoly.py index 33efe15f..da468b08 100644 --- a/skimage/morphology/tests/test_pnpoly.py +++ b/skimage/morphology/tests/test_pnpoly.py @@ -4,6 +4,7 @@ from numpy.testing import assert_array_equal from skimage.morphology._pnpoly import points_inside_poly, \ grid_points_inside_poly + class test_npnpoly(): def test_square(self): v = np.array([[0, 0], @@ -24,13 +25,14 @@ class test_npnpoly(): def test_type(self): assert(points_inside_poly([[0, 0]], [[0, 0]]).dtype == np.bool) + def test_grid_points_inside_poly(): v = np.array([[0, 0], [5, 0], [5, 5]]) expected = np.tril(np.ones((5, 5), dtype=bool)) - + assert_array_equal(grid_points_inside_poly((5, 5), v), expected) diff --git a/skimage/morphology/tests/test_selem.py b/skimage/morphology/tests/test_selem.py index 639cdf4e..eec6cd5b 100644 --- a/skimage/morphology/tests/test_selem.py +++ b/skimage/morphology/tests/test_selem.py @@ -10,6 +10,7 @@ from skimage.io import * from skimage import data_dir from skimage.morphology import * + class TestSElem(): def test_square_selem(self): @@ -32,13 +33,12 @@ class TestSElem(): expected_mask = matlab_masks[arrname] actual_mask = func(k) if (expected_mask.shape == (1,)): - expected_mask = expected_mask[:,np.newaxis] + expected_mask = expected_mask[:, np.newaxis] assert_equal(expected_mask, actual_mask) k = k + 1 - + def test_selem_disk(self): self.strel_worker("disk-matlab-output.npz", selem.disk) def test_selem_diamond(self): self.strel_worker("diamond-matlab-output.npz", selem.diamond) - diff --git a/skimage/morphology/tests/test_skeletonize.py b/skimage/morphology/tests/test_skeletonize.py index 539e0ad8..2f9d046e 100644 --- a/skimage/morphology/tests/test_skeletonize.py +++ b/skimage/morphology/tests/test_skeletonize.py @@ -7,75 +7,77 @@ from skimage.io import imread from skimage import data_dir import os.path + class TestSkeletonize(): def test_skeletonize_no_foreground(self): - im = np.zeros((5,5)) + im = np.zeros((5, 5)) result = skeletonize(im) - numpy.testing.assert_array_equal(result, np.zeros((5,5))) - + numpy.testing.assert_array_equal(result, np.zeros((5, 5))) + def test_skeletonize_wrong_dim1(self): im = np.zeros((5)) - numpy.testing.assert_raises(ValueError, skeletonize, im) + numpy.testing.assert_raises(ValueError, skeletonize, im) def test_skeletonize_wrong_dim2(self): im = np.zeros((5, 5, 5)) - numpy.testing.assert_raises(ValueError, skeletonize, im) + numpy.testing.assert_raises(ValueError, skeletonize, im) def test_skeletonize_not_binary(self): im = np.zeros((5, 5)) im[0, 0] = 1 im[0, 1] = 2 - numpy.testing.assert_raises(ValueError, skeletonize, im) - + numpy.testing.assert_raises(ValueError, skeletonize, im) + def test_skeletonize_unexpected_value(self): im = np.zeros((5, 5)) im[0, 0] = 2 - numpy.testing.assert_raises(ValueError, skeletonize, im) - + numpy.testing.assert_raises(ValueError, skeletonize, im) + def test_skeletonize_all_foreground(self): - im = np.ones((3,4)) + im = np.ones((3, 4)) result = skeletonize(im) - + def test_skeletonize_single_point(self): im = np.zeros((5, 5), np.uint8) im[3, 3] = 1 result = skeletonize(im) numpy.testing.assert_array_equal(result, im) - + def test_skeletonize_already_thinned(self): im = np.zeros((5, 5), np.uint8) - im[3,1:-1] = 1 + im[3, 1:-1] = 1 im[2, -1] = 1 im[4, 0] = 1 result = skeletonize(im) numpy.testing.assert_array_equal(result, im) - + def test_skeletonize_output(self): im = imread(os.path.join(data_dir, "bw_text.png"), as_grey=True) - + # make black the foreground - im = (im==0) + im = (im == 0) result = skeletonize(im) - + expected = np.load(os.path.join(data_dir, "bw_text_skeleton.npy")) numpy.testing.assert_array_equal(result, expected) - - + def test_skeletonize_num_neighbours(self): # an empty image image = np.zeros((300, 300)) - + # foreground object 1 image[10:-10, 10:100] = 1 image[-100:-10, 10:-10] = 1 image[10:-10, -100:-10] = 1 - + # foreground object 2 rs, cs = draw.bresenham(250, 150, 10, 280) - for i in range(10): image[rs+i, cs] = 1 + for i in range(10): + image[rs + i, cs] = 1 rs, cs = draw.bresenham(10, 150, 250, 280) - for i in range(20): image[rs+i, cs] = 1 - + for i in range(20): + image[rs + i, cs] = 1 + # foreground object 3 ir, ic = np.indices(image.shape) circle1 = (ic - 135)**2 + (ir - 150)**2 < 30**2 @@ -83,13 +85,14 @@ class TestSkeletonize(): image[circle1] = 1 image[circle2] = 0 result = skeletonize(image) - + # there should never be a 2x2 block of foreground pixels in a skeleton mask = np.array([[1, 1], - [1, 1]], np.uint8) + [1, 1]], np.uint8) blocks = correlate(result, mask, mode='constant') assert not numpy.any(blocks == 4) - + + class TestMedialAxis(): def test_00_00_zeros(self): '''Test skeletonize on an array of all zeros''' @@ -101,7 +104,7 @@ class TestMedialAxis(): result = medial_axis(np.zeros((10, 10), bool), np.zeros((10, 10), bool)) assert np.all(result == False) - + def test_01_01_rectangle(self): '''Test skeletonize on a rectangle''' image = np.zeros((9, 15), bool) diff --git a/skimage/morphology/tests/test_watershed.py b/skimage/morphology/tests/test_watershed.py index 82ba6917..46298ae5 100644 --- a/skimage/morphology/tests/test_watershed.py +++ b/skimage/morphology/tests/test_watershed.py @@ -43,7 +43,6 @@ Original author: Lee Kamentsky import math -import time import unittest import numpy as np @@ -54,6 +53,7 @@ from skimage.morphology.watershed import watershed, \ eps = 1e-12 + def diff(a, b): if not isinstance(a, np.ndarray): a = np.asarray(a) @@ -61,7 +61,7 @@ def diff(a, b): b = np.asarray(b) if (0 in a.shape) and (0 in b.shape): return 0.0 - b[a==0] = 0 + b[a == 0] = 0 if (a.dtype in [np.complex64, np.complex128] or b.dtype in [np.complex64, np.complex128]): a = np.asarray(a, np.complex128) @@ -75,8 +75,10 @@ def diff(a, b): t = ((a - b)**2).sum() return math.sqrt(t) + class TestWatershed(unittest.TestCase): - eight = np.ones((3, 3),bool) + eight = np.ones((3, 3), bool) + def test_watershed01(self): "watershed 1" data = np.array([[0, 0, 0, 0, 0, 0, 0], @@ -100,7 +102,7 @@ class TestWatershed(unittest.TestCase): [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0]], np.int8) - out = watershed(data, markers,self.eight) + out = watershed(data, markers, self.eight) expected = np.array([[-1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1], @@ -120,28 +122,27 @@ class TestWatershed(unittest.TestCase): def test_watershed02(self): "watershed 2" data = np.array([[0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 1, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0]], np.uint8) + markers = np.array([[-1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 1, 0, 0, 0, 1, 0], - [0, 1, 0, 0, 0, 1, 0], - [0, 1, 0, 0, 0, 1, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0]], np.uint8) - markers = np.array([[ -1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 1, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0]], - np.int8) + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0]], np.int8) out = watershed(data, markers) error = diff([[-1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1], @@ -159,26 +160,25 @@ class TestWatershed(unittest.TestCase): def test_watershed03(self): "watershed 3" data = np.array([[0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0]], np.uint8) - markers = np.array([[ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 2, 0, 3, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, -1]], - np.int8) + [0, 1, 1, 1, 1, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0]], np.uint8) + markers = np.array([[0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 2, 0, 3, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, -1]], np.int8) out = watershed(data, markers) error = diff([[-1, -1, -1, -1, -1, -1, -1], [-1, 0, 2, 0, 3, 0, -1], @@ -200,21 +200,20 @@ class TestWatershed(unittest.TestCase): [0, 1, 0, 1, 0, 1, 0], [0, 1, 0, 1, 0, 1, 0], [0, 1, 1, 1, 1, 1, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]], np.uint8) - markers = np.array([[ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 2, 0, 3, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, -1]], - np.int8) + markers = np.array([[0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 2, 0, 3, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, -1]], np.int8) out = watershed(data, markers, self.eight) error = diff([[-1, -1, -1, -1, -1, -1, -1], [-1, 2, 2, 0, 3, 3, -1], @@ -222,35 +221,34 @@ class TestWatershed(unittest.TestCase): [-1, 2, 2, 0, 3, 3, -1], [-1, 2, 2, 0, 3, 3, -1], [-1, 2, 2, 0, 3, 3, -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, -1, -1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1]], out) self.failUnless(error < eps) def test_watershed05(self): "watershed 5" data = np.array([[0, 0, 0, 0, 0, 0, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 0, 1, 0, 1, 0], - [0, 1, 1, 1, 1, 1, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0]], np.uint8) - markers = np.array([[ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 3, 0, 2, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, -1]], - np.int8) + [0, 1, 1, 1, 1, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 0, 1, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0]], np.uint8) + markers = np.array([[0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 3, 0, 2, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, -1]], np.int8) out = watershed(data, markers, self.eight) error = diff([[-1, -1, -1, -1, -1, -1, -1], [-1, 3, 3, 0, 2, 2, -1], @@ -267,24 +265,23 @@ class TestWatershed(unittest.TestCase): def test_watershed06(self): "watershed 6" data = np.array([[0, 1, 0, 0, 0, 1, 0], - [0, 1, 0, 0, 0, 1, 0], - [0, 1, 0, 0, 0, 1, 0], - [0, 1, 1, 1, 1, 1, 0], - [0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0]], np.uint8) - markers = np.array([[ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 1, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ 0, 0, 0, 0, 0, 0, 0], - [ -1, 0, 0, 0, 0, 0, 0]], - np.int8) + [0, 1, 0, 0, 0, 1, 0], + [0, 1, 0, 0, 0, 1, 0], + [0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0]], np.uint8) + markers = np.array([[0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0], + [-1, 0, 0, 0, 0, 0, 0]], np.int8) out = watershed(data, markers, self.eight) error = diff([[-1, 1, 1, 1, 1, 1, -1], [-1, 1, 1, 1, 1, 1, -1], @@ -300,28 +297,28 @@ class TestWatershed(unittest.TestCase): def test_watershed07(self): "A regression test of a competitive case that failed" data = np.array([[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], - [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], - [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], - [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], - [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], - [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], - [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], - [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], - [255,255,255,255,255,204,153,103,103,153,204,255,255,255,255,255], - [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], - [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], - [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], - [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], - [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], - [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], - [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], - [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]]) - mask = (data!=255) - markers = np.zeros(data.shape,int) + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], + [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], + [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], + [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], + [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], + [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], + [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], + [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], + [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], + [255,255,255,255,255,204,153,103,103,153,204,255,255,255,255,255], + [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], + [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], + [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], + [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], + [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], + [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], + [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], + [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]]) + mask = (data != 255) + markers = np.zeros(data.shape, int) markers[6, 7] = 1 markers[14, 7] = 2 out = watershed(data, markers, self.eight, mask=mask) @@ -331,35 +328,35 @@ class TestWatershed(unittest.TestCase): # size1 = np.sum(out == 1) size2 = np.sum(out == 2) - self.assertTrue(abs(size1-size2) <= 6) - + self.assertTrue(abs(size1 - size2) <= 6) + def test_watershed08(self): "The border pixels + an edge are all the same value" data = np.array([[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], - [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], - [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], - [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], - [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], - [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], - [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], - [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], - [255,255,255,255,255,204,153,141,141,153,204,255,255,255,255,255], - [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], - [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], - [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], - [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], - [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], - [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], - [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], - [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], - [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]]) + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], + [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], + [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], + [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], + [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], + [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], + [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], + [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], + [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], + [255,255,255,255,255,204,153,141,141,153,204,255,255,255,255,255], + [255,255,255,255,204,183,141, 94, 94,141,183,204,255,255,255,255], + [255,255,255,204,183,141,111, 72, 72,111,141,183,204,255,255,255], + [255,255,204,183,141,111, 72, 39, 39, 72,111,141,183,204,255,255], + [255,255,204,153,111, 72, 39, 1, 1, 39, 72,111,153,204,255,255], + [255,255,204,153,111, 94, 72, 52, 52, 72, 94,111,153,204,255,255], + [255,255,204,183,153,141,111,103,103,111,141,153,183,204,255,255], + [255,255,255,204,204,183,153,153,153,153,183,204,204,255,255,255], + [255,255,255,255,255,204,204,204,204,204,204,255,255,255,255,255], + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255], + [255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]]) mask = (data != 255) - markers = np.zeros(data.shape,int) - markers[6,7] = 1 - markers[14,7] = 2 + markers = np.zeros(data.shape, int) + markers[6, 7] = 1 + markers[14, 7] = 2 out = watershed(data, markers, self.eight, mask=mask) # # The two objects should be the same size, except possibly for the @@ -367,30 +364,27 @@ class TestWatershed(unittest.TestCase): # size1 = np.sum(out == 1) size2 = np.sum(out == 2) - self.assertTrue(abs(size1-size2) <= 6) - + self.assertTrue(abs(size1 - size2) <= 6) + def test_watershed09(self): """Test on an image of reasonable size - + This is here both for timing (does it take forever?) and to ensure that the memory constraints are reasonable """ image = np.zeros((1000, 1000)) coords = np.random.uniform(0, 1000, (100, 2)).astype(int) - markers = np.zeros((1000, 1000),int) + markers = np.zeros((1000, 1000), int) idx = 1 - for x,y in coords: - image[x,y] = 1 + for x, y in coords: + image[x, y] = 1 markers[x, y] = idx idx += 1 - + image = scipy.ndimage.gaussian_filter(image, 4) - before = time.clock() - out = watershed(image, markers, self.eight) - elapsed = time.clock() - before - before = time.clock() - out = scipy.ndimage.watershed_ift(image.astype(np.uint16), markers, self.eight) - elapsed = time.clock() - before + watershed(image, markers, self.eight) + scipy.ndimage.watershed_ift(image.astype(np.uint16), markers, + self.eight) class TestIsLocalMaximum(unittest.TestCase): @@ -399,7 +393,7 @@ class TestIsLocalMaximum(unittest.TestCase): labels = np.zeros((10, 20), int) result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(~ result)) - + def test_01_01_one_point(self): image = np.zeros((10, 20)) labels = np.zeros((10, 20), int) @@ -407,7 +401,7 @@ class TestIsLocalMaximum(unittest.TestCase): labels[5, 5] = 1 result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(result == (labels == 1))) - + def test_01_02_adjacent_and_same(self): image = np.zeros((10, 20)) labels = np.zeros((10, 20), int) @@ -415,7 +409,7 @@ class TestIsLocalMaximum(unittest.TestCase): labels[5, 5:6] = 1 result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(result == (labels == 1))) - + def test_01_03_adjacent_and_different(self): image = np.zeros((10, 20)) labels = np.zeros((10, 20), int) @@ -427,67 +421,67 @@ class TestIsLocalMaximum(unittest.TestCase): self.assertTrue(np.all(result == expected)) result = is_local_maximum(image, labels) self.assertTrue(np.all(result == expected)) - + def test_01_04_not_adjacent_and_different(self): - image = np.zeros((10,20)) - labels = np.zeros((10,20), int) - image[5,5] = 1 - image[5,8] = .5 + image = np.zeros((10, 20)) + labels = np.zeros((10, 20), int) + image[5, 5] = 1 + image[5, 8] = .5 labels[image > 0] = 1 expected = (labels == 1) - result = is_local_maximum(image, labels, np.ones((3,3), bool)) + result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(result == expected)) - + def test_01_05_two_objects(self): - image = np.zeros((10,20)) - labels = np.zeros((10,20), int) - image[5,5] = 1 - image[5,15] = .5 - labels[5,5] = 1 - labels[5,15] = 2 + image = np.zeros((10, 20)) + labels = np.zeros((10, 20), int) + image[5, 5] = 1 + image[5, 15] = .5 + labels[5, 5] = 1 + labels[5, 15] = 2 expected = (labels > 0) - result = is_local_maximum(image, labels, np.ones((3,3), bool)) + result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(result == expected)) def test_01_06_adjacent_different_objects(self): - image = np.zeros((10,20)) - labels = np.zeros((10,20), int) - image[5,5] = 1 - image[5,6] = .5 - labels[5,5] = 1 - labels[5,6] = 2 + image = np.zeros((10, 20)) + labels = np.zeros((10, 20), int) + image[5, 5] = 1 + image[5, 6] = .5 + labels[5, 5] = 1 + labels[5, 6] = 2 expected = (labels > 0) - result = is_local_maximum(image, labels, np.ones((3,3), bool)) + result = is_local_maximum(image, labels, np.ones((3, 3), bool)) self.assertTrue(np.all(result == expected)) - + def test_02_01_four_quadrants(self): np.random.seed(21) - image = np.random.uniform(size=(40,60)) - i,j = np.mgrid[0:40,0:60] + image = np.random.uniform(size=(40, 60)) + i, j = np.mgrid[0:40, 0:60] labels = 1 + (i >= 20) + (j >= 30) * 2 - i,j = np.mgrid[-3:4,-3:4] - footprint = (i*i + j*j <=9) + i, j = np.mgrid[-3:4, -3:4] + footprint = (i * i + j * j <= 9) expected = np.zeros(image.shape, float) for imin, imax in ((0, 20), (20, 40)): for jmin, jmax in ((0, 30), (30, 60)): - expected[imin:imax,jmin:jmax] = scipy.ndimage.maximum_filter( - image[imin:imax, jmin:jmax], footprint = footprint) + expected[imin:imax, jmin:jmax] = scipy.ndimage.maximum_filter( + image[imin:imax, jmin:jmax], footprint=footprint) expected = (expected == image) result = is_local_maximum(image, labels, footprint) self.assertTrue(np.all(result == expected)) - + def test_03_01_disk_1(self): '''regression test of img-1194, footprint = [1] - + Test is_local_maximum when every point is a local maximum ''' np.random.seed(31) - image = np.random.uniform(size=(10,20)) + image = np.random.uniform(size=(10, 20)) footprint = np.array([[1]]) - result = is_local_maximum(image, np.ones((10,20)), footprint) + result = is_local_maximum(image, np.ones((10, 20)), footprint) self.assertTrue(np.all(result)) result = is_local_maximum(image, footprint=footprint) self.assertTrue(np.all(result)) - + if __name__ == "__main__": np.testing.run_module_suite() diff --git a/skimage/morphology/watershed.py b/skimage/morphology/watershed.py index 1966a1a8..c0b1b34b 100644 --- a/skimage/morphology/watershed.py +++ b/skimage/morphology/watershed.py @@ -24,13 +24,13 @@ All rights reserved. Original author: Lee Kamentsky """ -from _heapq import heapify, heappush, heappop +from _heapq import heappush, heappop import numpy as np import scipy.ndimage from ..filter import rank_order from . import _watershed -import warnings + def watershed(image, markers, connectivity=None, offset=None, mask=None): """ @@ -87,8 +87,8 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): This implementation converts all arguments to specific, lowest common denominator types, then passes these to a C algorithm. - - Markers can be determined manually, or automatically using for example + + Markers can be determined manually, or automatically using for example the local minima of the gradient of the image, or the local maxima of the distance function to the background for separating overlapping objects (see example). @@ -122,28 +122,28 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): The algorithm works also for 3-D images, and can be used for example to separate overlapping spheres. """ - + if connectivity == None: c_connectivity = scipy.ndimage.generate_binary_structure(image.ndim, 1) else: c_connectivity = np.array(connectivity, bool) if c_connectivity.ndim != image.ndim: - raise ValueError,"Connectivity dimension must be same as image" + raise ValueError("Connectivity dimension must be same as image") if offset == None: - if any([x%2==0 for x in c_connectivity.shape]): - raise ValueError,"Connectivity array must have an unambiguous \ - center" + if any([x % 2 == 0 for x in c_connectivity.shape]): + raise ValueError("Connectivity array must have an unambiguous " + "center") # # offset to center of connectivity array # offset = np.array(c_connectivity.shape) // 2 - # pad the image, markers, and mask so that we can use the mask to + # pad the image, markers, and mask so that we can use the mask to # keep from running off the edges pads = offset def pad(im): - new_im = np.zeros([i + 2*p for i, p in zip(im.shape, pads)], im.dtype) + new_im = np.zeros([i + 2 * p for i, p in zip(im.shape, pads)], im.dtype) new_im[[slice(p, -p, None) for p in pads]] = im return new_im @@ -157,18 +157,17 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): c_image = rank_order(image)[0].astype(np.int32) c_markers = np.ascontiguousarray(markers, dtype=np.int32) if c_markers.ndim != c_image.ndim: - raise ValueError,\ - "markers (ndim=%d) must have same # of dimensions "\ - "as image (ndim=%d)"%(c_markers.ndim, cimage.ndim) + raise ValueError("markers (ndim=%d) must have same # of dimensions " + "as image (ndim=%d)" % (c_markers.ndim, c_image.ndim)) if c_markers.shape != c_image.shape: raise ValueError("image and markers must have the same shape") if mask != None: c_mask = np.ascontiguousarray(mask, dtype=bool) if c_mask.ndim != c_markers.ndim: - raise ValueError, "mask must have same # of dimensions as image" + raise ValueError("mask must have same # of dimensions as image") if c_markers.shape != c_mask.shape: - raise ValueError, "mask must have same shape as image" - c_markers[np.logical_not(mask)]=0 + raise ValueError("mask must have same shape as image") + c_markers[np.logical_not(mask)] = 0 else: c_mask = None c_output = c_markers.copy() @@ -189,9 +188,8 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): indexes = [] ignore = True for j in range(len(c_connectivity.shape)): - elems = c_image.shape[j] - idx = (i // multiplier) % c_connectivity.shape[j] - off = idx - offset[j] + idx = (i // multiplier) % c_connectivity.shape[j] + off = idx - offset[j] if off: ignore = False offs.append(off) @@ -214,10 +212,10 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): else: c_mask = c_mask.astype(np.int8).flatten() _watershed.watershed(c_image.flatten(), - pq, age, c, - c_image.ndim, + pq, age, c, + c_image.ndim, c_mask, - np.array(c_image.shape,np.int32), + np.array(c_image.shape, np.int32), c_output) c_output = c_output.reshape(c_image.shape)[[slice(1, -1, None)] * image.ndim] @@ -230,18 +228,18 @@ def watershed(image, markers, connectivity=None, offset=None, mask=None): def is_local_maximum(image, labels=None, footprint=None): """ Return a boolean array of points that are local maxima - + Parameters ---------- image: ndarray (2-D, 3-D, ...) intensity image - - labels: ndarray, optional + + labels: ndarray, optional find maxima only within labels. Zero is reserved for background. - + footprint: ndarray of bools, optional binary mask indicating the neighborhood to be examined - `footprint` must be a matrix with odd dimensions, the center is taken + `footprint` must be a matrix with odd dimensions, the center is taken to be the point in question. Returns @@ -289,7 +287,7 @@ def is_local_maximum(image, labels=None, footprint=None): footprint = np.ones([3] * image.ndim, dtype=np.uint8) assert((np.all(footprint.shape) & 1) == 1) footprint = (footprint != 0) - footprint_extent = (np.array(footprint.shape)-1) // 2 + footprint_extent = (np.array(footprint.shape) - 1) // 2 if np.all(footprint_extent == 0): return labels > 0 result = (labels > 0).copy() @@ -297,17 +295,17 @@ def is_local_maximum(image, labels=None, footprint=None): # Create a labels matrix with zeros at the borders that might be # hit by the footprint. # - big_labels = np.zeros(np.array(labels.shape) + footprint_extent*2, + big_labels = np.zeros(np.array(labels.shape) + footprint_extent * 2, labels.dtype) - big_labels[[slice(fe,-fe) for fe in footprint_extent]] = labels + big_labels[[slice(fe, -fe) for fe in footprint_extent]] = labels # # Find the relative indexes of each footprint element # image_strides = np.array(image.strides) // image.dtype.itemsize big_strides = np.array(big_labels.strides) // big_labels.dtype.itemsize result_strides = np.array(result.strides) // result.dtype.itemsize - footprint_offsets = np.mgrid[[slice(-fe,fe+1) for fe in footprint_extent]] - + footprint_offsets = np.mgrid[[slice(-fe, fe + 1) for fe in footprint_extent]] + fp_image_offsets = np.sum(image_strides[:, np.newaxis] * footprint_offsets[:, footprint], 0) fp_big_offsets = np.sum(big_strides[:, np.newaxis] * @@ -315,9 +313,9 @@ def is_local_maximum(image, labels=None, footprint=None): # # Get the index of each labeled pixel in the image and big_labels arrays # - indexes = np.mgrid[[slice(0,x) for x in labels.shape]][:, labels > 0] + indexes = np.mgrid[[slice(0, x) for x in labels.shape]][:, labels > 0] image_indexes = np.sum(image_strides[:, np.newaxis] * indexes, 0) - big_indexes = np.sum(big_strides[:, np.newaxis] * + big_indexes = np.sum(big_strides[:, np.newaxis] * (indexes + footprint_extent[:, np.newaxis]), 0) result_indexes = np.sum(result_strides[:, np.newaxis] * indexes, 0) # @@ -335,18 +333,15 @@ def is_local_maximum(image, labels=None, footprint=None): same_label = (big_labels_raveled[big_indexes + fp_big_offset] == big_labels_raveled[big_indexes]) less_than = (image_raveled[image_indexes[same_label]] < - image_raveled[image_indexes[same_label]+ fp_image_offset]) + image_raveled[image_indexes[same_label] + fp_image_offset]) result_raveled[result_indexes[same_label][less_than]] = False - + return result - # ---------------------- deprecated ------------------------------ -# Deprecate slower pure-Python code, that we keep only for +# Deprecate slower pure-Python code, that we keep only for # pedagogical purposes - - def __heapify_markers(markers, image): """Create a priority queue heap with the markers on it""" stride = np.array(image.strides) // image.itemsize @@ -354,24 +349,25 @@ def __heapify_markers(markers, image): ncoords = coords.shape[0] if ncoords > 0: pixels = image[markers != 0] - age = np.arange(ncoords) + age = np.arange(ncoords) offset = np.zeros(coords.shape[0], int) for i in range(image.ndim): offset = offset + stride[i] * coords[:, i] pq = np.column_stack((pixels, age, offset, coords)) # pixels = top priority, age=second - ordering = np.lexsort((age, pixels)) + ordering = np.lexsort((age, pixels)) pq = pq[ordering, :] else: pq = np.zeros((0, markers.ndim + 3), int) return (pq, ncoords) - + + def _slow_watershed(image, markers, connectivity=8, mask=None): """Return a matrix labeled using the watershed algorithm - + Use the `watershed` function for a faster execution. This pure Python function is solely for pedagogical purposes. - + Parameters ---------- image: 2-d ndarray of integers @@ -380,16 +376,16 @@ def _slow_watershed(image, markers, connectivity=8, mask=None): markers: 2-d ndarray of integers a two-dimensional matrix marking the basins with the values to be assigned in the label matrix. Zero means not a marker. - connectivity: {4, 8}, optional + connectivity: {4, 8}, optional either 4 for four-connected or 8 (default) for eight-connected - mask: 2-d ndarray of bools, optional + mask: 2-d ndarray of bools, optional don't label points in the mask - Returns + Returns ------- out: ndarray A labeled matrix of the same type and shape as markers - + Notes ----- @@ -409,20 +405,20 @@ def _slow_watershed(image, markers, connectivity=8, mask=None): This implementation converts all arguments to specific, lowest common denominator types, then passes these to a C algorithm. - - Markers can be determined manually, or automatically using for example + + Markers can be determined manually, or automatically using for example the local minima of the gradient of the image, or the local maxima of the distance function to the background for separating overlapping objects. """ if connectivity not in (4, 8): raise ValueError("Connectivity was %d: it should be either \ - four or eight" %(connectivity)) - + four or eight" % (connectivity)) + image = np.array(image) markers = np.array(markers) labels = markers.copy() - max_x = markers.shape[0] - max_y = markers.shape[1] + max_x = markers.shape[0] + max_y = markers.shape[1] if connectivity == 4: connect_increments = ((1, 0), (0, 1), (-1, 0), (0, -1)) else: diff --git a/skimage/scripts/skivi b/skimage/scripts/skivi index 4469f015..e3108f64 100755 --- a/skimage/scripts/skivi +++ b/skimage/scripts/skivi @@ -3,4 +3,3 @@ if __name__ == "__main__": from skimage.scripts import skivi skivi.main() - diff --git a/skimage/scripts/skivi.py b/skimage/scripts/skivi.py index 610013cc..2e907bbe 100644 --- a/skimage/scripts/skivi.py +++ b/skimage/scripts/skivi.py @@ -1,4 +1,6 @@ """skimage viewer""" + + def main(): import skimage.io as io import sys @@ -10,4 +12,3 @@ def main(): io.use_plugin('qt') io.imshow(io.imread(sys.argv[1]), fancy=True) io.show() - diff --git a/skimage/segmentation/random_walker_segmentation.py b/skimage/segmentation/random_walker_segmentation.py index 130be926..68afcc65 100644 --- a/skimage/segmentation/random_walker_segmentation.py +++ b/skimage/segmentation/random_walker_segmentation.py @@ -63,7 +63,7 @@ def _make_graph_edges_3d(n_x, n_y, n_z): def _compute_weights_3d(data, beta=130, eps=1.e-6): - gradients = _compute_gradients_3d(data) ** 2 + gradients = _compute_gradients_3d(data)**2 beta /= 10 * data.std() gradients *= beta weights = np.exp(- gradients) diff --git a/skimage/setup.py b/skimage/setup.py index c26014f8..0afc5bc9 100644 --- a/skimage/setup.py +++ b/skimage/setup.py @@ -1,21 +1,22 @@ import os + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('skimage', parent_package, top_path) - config.add_subpackage('graph') - config.add_subpackage('io') - config.add_subpackage('morphology') - config.add_subpackage('filter') - config.add_subpackage('transform') - config.add_subpackage('data') - config.add_subpackage('util') config.add_subpackage('color') + config.add_subpackage('data') config.add_subpackage('draw') config.add_subpackage('feature') + config.add_subpackage('filter') + config.add_subpackage('graph') + config.add_subpackage('io') config.add_subpackage('measure') + config.add_subpackage('morphology') + config.add_subpackage('transform') + config.add_subpackage('util') def add_test_directories(arg, dirname, fnames): if dirname.split(os.path.sep)[-1] == 'tests': @@ -37,4 +38,3 @@ if __name__ == "__main__": config = configuration(top_path='').todict() setup(**config) - diff --git a/skimage/transform/_warp_zoo.py b/skimage/transform/_warp_zoo.py index 6fd92c44..4df531d1 100644 --- a/skimage/transform/_warp_zoo.py +++ b/skimage/transform/_warp_zoo.py @@ -22,6 +22,7 @@ def _swirl_mapping(xy, center, rotation, strength, radius): return xy + def swirl(image, center=None, strength=1, radius=100, rotation=0, output_shape=None, order=1, mode='constant', cval=0): """Perform a swirl transformation. diff --git a/skimage/transform/finite_radon_transform.py b/skimage/transform/finite_radon_transform.py index 03dd0152..13007dce 100644 --- a/skimage/transform/finite_radon_transform.py +++ b/skimage/transform/finite_radon_transform.py @@ -9,6 +9,7 @@ __docformat__ = "restructuredtext en" import numpy as np from numpy import roll, newaxis + def frt2(a): """Compute the 2-dimensional finite radon transform (FRT) for an n x n integer array. @@ -65,7 +66,7 @@ def frt2(a): ai = a.copy() n = ai.shape[0] - f = np.empty((n+1, n), np.uint32) + f = np.empty((n + 1, n), np.uint32) f[0] = ai.sum(axis=0) for m in range(1, n): # Roll the pth row of ai left by p places @@ -125,7 +126,7 @@ def ifrt2(a): and Electron Physics, 139 (2006) """ - if a.ndim != 2 or a.shape[0] != a.shape[1]+1: + if a.ndim != 2 or a.shape[0] != a.shape[1] + 1: raise ValueError("Input must be an (n+1) row x n column, 2-D array") ai = a.copy()[:-1] @@ -138,5 +139,5 @@ def ifrt2(a): ai[row] = roll(ai[row], row) f[m] = ai.sum(axis=0) f += a[-1][newaxis].T - f = (f - ai[0].sum())/n + f = (f - ai[0].sum()) / n return f diff --git a/skimage/transform/hough_transform.py b/skimage/transform/hough_transform.py index 5bbe3b6c..e4cb5b5c 100644 --- a/skimage/transform/hough_transform.py +++ b/skimage/transform/hough_transform.py @@ -3,7 +3,8 @@ __all__ = ['hough', 'probabilistic_hough'] from itertools import izip as zip import numpy as np -from ._hough_transform import _probabilistic_hough +from ._hough_transform import _probabilistic_hough + def _hough(img, theta=None): if img.ndim != 2: @@ -68,28 +69,28 @@ def probabilistic_hough(img, threshold=10, line_length=50, line_gap=10, theta=No img : (M, N) ndarray Input image with nonzero values representing edges. threshold : int - Threshold + Threshold line_length : int, optional (default 50) Minimum accepted length of detected lines. Increase the parameter to extract longer lines. line_gap : int, optional, (default 10) - Maximum gap between pixels to still form a line. + Maximum gap between pixels to still form a line. Increase the parameter to merge broken lines more aggresively. theta : 1D ndarray, dtype=double, optional, default (-pi/2 .. pi/2) Angles at which to compute the transform, in radians. - + Returns ------- lines : list - List of lines identified, lines in format ((x0, y0), (x1, y0)), indicating + List of lines identified, lines in format ((x0, y0), (x1, y0)), indicating line start and end. References ---------- - .. [1] C. Galamhos, J. Matas and J. Kittler,"Progressive probabilistic Hough - transform for line detection", in IEEE Computer Society Conference on - Computer Vision and Pattern Recognition, 1999. - """ + .. [1] C. Galamhos, J. Matas and J. Kittler,"Progressive probabilistic Hough + transform for line detection", in IEEE Computer Society Conference on + Computer Vision and Pattern Recognition, 1999. + """ return _probabilistic_hough(img, threshold, line_length, line_gap, theta) @@ -141,5 +142,3 @@ def hough(img, theta=None): """ return _hough(img, theta) - - diff --git a/skimage/transform/integral.py b/skimage/transform/integral.py index 440d10e6..c34b6a19 100644 --- a/skimage/transform/integral.py +++ b/skimage/transform/integral.py @@ -26,6 +26,7 @@ def integral_image(x): """ return x.cumsum(1).cumsum(0) + def integrate(ii, r0, c0, r1, c1): """Use an integral image to integrate over a given window. diff --git a/skimage/transform/project.py b/skimage/transform/project.py index 030e86a9..a140e03e 100644 --- a/skimage/transform/project.py +++ b/skimage/transform/project.py @@ -10,6 +10,7 @@ __all__ = ['homography'] eps = np.finfo(float).eps + def homography(image, H, output_shape=None, order=1, mode='constant', cval=0.): """Perform a projective transformation (homography) on an image. @@ -86,7 +87,7 @@ def homography(image, H, output_shape=None, order=1, [ 0, 0, 0, 255, 0], [ 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0]], dtype=uint8) - + """ if image.ndim < 2: raise ValueError("Input must have more than 1 dimension.") @@ -120,13 +121,13 @@ def homography(image, H, output_shape=None, order=1, tf_coords[..., :2] /= tf_coords[..., 2, np.newaxis] # y-coordinate mapping - _stackcopy(coords[0,...], tf_coords[...,1]) + _stackcopy(coords[0, ...], tf_coords[..., 1]) # x-coordinate mapping - _stackcopy(coords[1,...], tf_coords[...,0]) + _stackcopy(coords[1, ...], tf_coords[..., 0]) # colour-coordinate mapping - coords[2,...] = range(bands) + coords[2, ...] = range(bands) # Prefilter not necessary for order 1 interpolation prefilter = order > 1 diff --git a/skimage/transform/radon_transform.py b/skimage/transform/radon_transform.py index 5da31a72..b94414c2 100644 --- a/skimage/transform/radon_transform.py +++ b/skimage/transform/radon_transform.py @@ -43,7 +43,7 @@ def radon(image, theta=None): if theta == None: theta = np.arange(180) height, width = image.shape - diagonal = np.sqrt(height ** 2 + width ** 2) + diagonal = np.sqrt(height**2 + width**2) heightpad = np.ceil(diagonal - height) widthpad = np.ceil(diagonal - width) padded_image = np.zeros((int(height + heightpad), @@ -66,7 +66,6 @@ def radon(image, theta=None): [0, 1, dh], [0, 0, 1]]) - def build_rotation(theta): T = -np.deg2rad(theta) @@ -80,7 +79,7 @@ def radon(image, theta=None): rotated = homography(padded_image, build_rotation(-theta[i])) - out[:,i] = rotated.sum(0)[::-1] + out[:, i] = rotated.sum(0)[::-1] return out @@ -131,17 +130,16 @@ def iradon(radon_image, theta=None, output_size=None, th = (np.pi / 180.0) * theta # if output size not specified, estimate from input radon image if not output_size: - output_size = int(np.floor(np.sqrt((radon_image.shape[0]) ** 2 / 2.0))) + output_size = int(np.floor(np.sqrt((radon_image.shape[0])**2 / 2.0))) n = radon_image.shape[0] img = radon_image.copy() # resize image to next power of two for fourier analysis # speeds up fourier and lessens artifacts - order = max(64., 2 ** np.ceil(np.log(2 * n) / np.log(2))) + order = max(64., 2**np.ceil(np.log(2 * n) / np.log(2))) # zero pad input image img.resize((order, img.shape[1])) # construct the fourier filter - freqs = np.zeros((order, 1)) f = fftshift(abs(np.mgrid[-1:1:2 / order])).reshape(-1, 1) w = 2 * np.pi * f @@ -151,11 +149,11 @@ def iradon(radon_image, theta=None, output_size=None, elif filter == "shepp-logan": f[1:] = f[1:] * np.sin(w[1:] / 2) / (w[1:] / 2) elif filter == "cosine": - f[1:] = f[1:] * np.cos(w[1:] / 2) + f[1:] = f[1:] * np.cos(w[1:] / 2) elif filter == "hamming": - f[1:] = f[1:] * (0.54 + 0.46 * np.cos(w[1:])) + f[1:] = f[1:] * (0.54 + 0.46 * np.cos(w[1:])) elif filter == "hann": - f[1:] = f[1:] * (1 + np.cos(w[1:])) / 2 + f[1:] = f[1:] * (1 + np.cos(w[1:])) / 2 elif filter == None: f[1:] = 1 else: @@ -184,13 +182,13 @@ def iradon(radon_image, theta=None, output_size=None, ((((k > 0) & (k < n)) * k) - 1).astype(np.int), i] elif interpolation == "linear": for i in range(len(theta)): - t = xpr*np.sin(th[i]) - ypr*np.cos(th[i]) - a = np.floor(t) - b = mid_index + a - b0 = ((((b + 1 > 0) & (b + 1 < n)) * (b + 1)) - 1).astype(np.int) - b1 = ((((b > 0) & (b < n)) * b) - 1).astype(np.int) - reconstructed += (t - a) * radon_filtered[b0, i] + \ - (a - t + 1) * radon_filtered[b1, i] + t = xpr * np.sin(th[i]) - ypr * np.cos(th[i]) + a = np.floor(t) + b = mid_index + a + b0 = ((((b + 1 > 0) & (b + 1 < n)) * (b + 1)) - 1).astype(np.int) + b1 = ((((b > 0) & (b < n)) * b) - 1).astype(np.int) + reconstructed += (t - a) * radon_filtered[b0, i] + \ + (a - t + 1) * radon_filtered[b1, i] else: raise ValueError("Unknown interpolation: %s" % interpolation) diff --git a/skimage/transform/setup.py b/skimage/transform/setup.py index 274dad94..4ecb6ba4 100644 --- a/skimage/transform/setup.py +++ b/skimage/transform/setup.py @@ -6,6 +6,7 @@ from skimage._build import cython base_path = os.path.abspath(os.path.dirname(__file__)) + def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs @@ -21,16 +22,15 @@ def configuration(parent_package='', top_path=None): config.add_extension('_project', sources=['_project.c'], include_dirs=[get_numpy_include_dirs()]) - return config if __name__ == '__main__': from numpy.distutils.core import setup - setup(maintainer = 'Scikits-image Developers', - author = 'Scikits-image Developers', - maintainer_email = 'scikits-image@googlegroups.com', - description = 'Transforms', - url = 'https://github.com/scikits-image/scikits-image', - license = 'SciPy License (BSD Style)', + setup(maintainer='Scikits-image Developers', + author='Scikits-image Developers', + maintainer_email='scikits-image@googlegroups.com', + description='Transforms', + url='https://github.com/scikits-image/scikits-image', + license='SciPy License (BSD Style)', **(configuration(top_path='').todict()) ) diff --git a/skimage/transform/tests/test_finite_radon_transform.py b/skimage/transform/tests/test_finite_radon_transform.py index d8504477..b5fcf43a 100644 --- a/skimage/transform/tests/test_finite_radon_transform.py +++ b/skimage/transform/tests/test_finite_radon_transform.py @@ -3,6 +3,7 @@ from numpy.testing import * from skimage.transform import * + def test_frt(): SIZE = 59 try: @@ -15,4 +16,4 @@ def test_frt(): L = np.tri(SIZE, dtype=np.int32) + np.tri(SIZE, dtype=np.int32)[::-1] f = frt2(L) fi = ifrt2(f) - assert len(np.nonzero(L-fi)[0]) == 0 + assert len(np.nonzero(L - fi)[0]) == 0 diff --git a/skimage/transform/tests/test_hough_transform.py b/skimage/transform/tests/test_hough_transform.py index b75291e8..03b63d07 100644 --- a/skimage/transform/tests/test_hough_transform.py +++ b/skimage/transform/tests/test_hough_transform.py @@ -5,6 +5,7 @@ import skimage.transform as tf import skimage.transform.hough_transform as ht from skimage.transform import probabilistic_hough + def append_desc(func, description): """Append the test function ``func`` and append ``description`` to its name. @@ -15,6 +16,7 @@ def append_desc(func, description): from skimage.transform import * + def test_hough(): # Generate a test image img = np.zeros((100, 100), dtype=int) @@ -39,6 +41,7 @@ def test_hough_angles(): assert_equal(len(angles), 10) + def test_py_hough(): ht._hough, fast_hough = ht._py_hough, ht._hough @@ -47,6 +50,7 @@ def test_py_hough(): tf._hough = fast_hough + def test_probabilistic_hough(): # Generate a test image img = np.zeros((100, 100), dtype=int) @@ -55,8 +59,9 @@ def test_probabilistic_hough(): img[i, i] = 100 # decrease default theta sampling because similar orientations may confuse # as mentioned in article of Galambos et al - theta=np.linspace(0, np.pi, 45) - lines = probabilistic_hough(img, theta=theta, threshold=10, line_length=10, line_gap=1) + theta = np.linspace(0, np.pi, 45) + lines = probabilistic_hough(img, theta=theta, threshold=10, line_length=10, + line_gap=1) # sort the lines according to the x-axis sorted_lines = [] for line in lines: @@ -69,4 +74,3 @@ def test_probabilistic_hough(): if __name__ == "__main__": run_module_suite() - diff --git a/skimage/transform/tests/test_integral.py b/skimage/transform/tests/test_integral.py index b8905d92..d443189c 100644 --- a/skimage/transform/tests/test_integral.py +++ b/skimage/transform/tests/test_integral.py @@ -6,19 +6,22 @@ from skimage.transform import integral_image, integrate x = (np.random.random((50, 50)) * 255).astype(np.uint8) s = integral_image(x) + def test_validity(): - y = np.arange(12).reshape((4,3)) + y = np.arange(12).reshape((4, 3)) y = (np.random.random((50, 50)) * 255).astype(np.uint8) assert_equal(integral_image(y)[-1, -1], y.sum()) + 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)) + 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)) diff --git a/skimage/transform/tests/test_project.py b/skimage/transform/tests/test_project.py index 3446c9a5..25ebda20 100644 --- a/skimage/transform/tests/test_project.py +++ b/skimage/transform/tests/test_project.py @@ -6,27 +6,30 @@ from skimage.transform import homography, fast_homography from skimage import data from skimage.color import rgb2gray + def test_stackcopy(): layers = 4 x = np.empty((3, 3, layers)) y = np.eye(3, 3) _stackcopy(x, y) for i in range(layers): - assert_array_almost_equal(x[...,i], y) + assert_array_almost_equal(x[..., i], y) + def test_homography(): x = np.arange(9, dtype=np.uint8).reshape((3, 3)) + 1 - theta = -np.pi/2 - M = np.array([[np.cos(theta),-np.sin(theta),0], - [np.sin(theta), np.cos(theta),2], - [0, 0, 1]]) + theta = -np.pi / 2 + M = np.array([[np.cos(theta), -np.sin(theta), 0], + [np.sin(theta), np.cos(theta), 2], + [0, 0, 1]]) x90 = homography(x, M, order=1) assert_array_almost_equal(x90, np.rot90(x)) + def test_fast_homography(): img = rgb2gray(data.lena()).astype(np.uint8) img = img[:, :100] - + theta = np.deg2rad(30) scale = 0.5 tx, ty = 50, 50 @@ -53,7 +56,7 @@ def test_fast_homography(): d = np.mean(np.abs(p0 - p1)) assert d < 0.2 - + if __name__ == "__main__": from numpy.testing import run_module_suite diff --git a/skimage/transform/tests/test_radon_transform.py b/skimage/transform/tests/test_radon_transform.py index b52ff69b..f8e9416f 100644 --- a/skimage/transform/tests/test_radon_transform.py +++ b/skimage/transform/tests/test_radon_transform.py @@ -4,12 +4,14 @@ import numpy as np from numpy.testing import * from skimage.transform import * + def rescale(x): x = x.astype(float) x -= x.min() x /= x.max() return x + def test_radon_iradon(): size = 100 debug = False @@ -38,6 +40,7 @@ def test_radon_iradon(): image = np.tri(size) + np.tri(size)[::-1] reconstructed = iradon(radon(image), filter="ramp", interpolation="nearest") + def test_iradon_angles(): """ Test with different number of projections @@ -60,10 +63,12 @@ def test_iradon_angles(): s = radon_image_80.sum(axis=0) assert np.allclose(s, s[0], rtol=0.01) reconstructed = iradon(radon_image_80) - delta_80 = np.mean(abs(image/np.max(image) - reconstructed/np.max(reconstructed))) + delta_80 = np.mean(abs(image / np.max(image) - + reconstructed / np.max(reconstructed))) # Loss of quality when the number of projections is reduced assert delta_80 > delta_200 + def test_radon_minimal(): """ Test for small images for various angles diff --git a/skimage/util/dtype.py b/skimage/util/dtype.py index b039123c..9697e627 100644 --- a/skimage/util/dtype.py +++ b/skimage/util/dtype.py @@ -94,7 +94,7 @@ def convert(image, dtype, force_copy=False, uniform=False): def _dtype2(kind, bits, itemsize=1): # Return dtype of `kind` that can store a `bits` wide unsigned int c = lambda x, y: x <= y if kind == 'u' else x < y - s = next(i for i in (itemsize, ) + (2, 4, 8) if c(bits, i*8)) + s = next(i for i in (itemsize, ) + (2, 4, 8) if c(bits, i * 8)) return np.dtype(kind + str(s)) def _scale(a, n, m, copy=True): @@ -205,26 +205,26 @@ def convert(image, dtype, force_copy=False, uniform=False): if kind_in == 'u': if kind == 'i': # unsigned integer -> signed integer - image = _scale(image, 8*itemsize_in, 8*itemsize-1) + image = _scale(image, 8 * itemsize_in, 8 * itemsize - 1) return image.view(dtype) else: # unsigned integer -> unsigned integer - return _scale(image, 8*itemsize_in, 8*itemsize) + return _scale(image, 8 * itemsize_in, 8 * itemsize) if kind == 'u': # signed integer -> unsigned integer sign_loss() - image = _scale(image, 8*itemsize_in-1, 8*itemsize) + image = _scale(image, 8 * itemsize_in - 1, 8 * itemsize) result = np.empty(image.shape, dtype) np.maximum(image, 0, out=result, dtype=image.dtype, casting='unsafe') return result # signed integer -> signed integer if itemsize_in > itemsize: - return _scale(image, 8*itemsize_in-1, 8*itemsize-1) - image = image.astype(_dtype2('i', itemsize*8)) + return _scale(image, 8 * itemsize_in - 1, 8 * itemsize - 1) + image = image.astype(_dtype2('i', itemsize * 8)) image -= imin_in - image = _scale(image, 8*itemsize_in, 8*itemsize, copy=False) + image = _scale(image, 8 * itemsize_in, 8 * itemsize, copy=False) image += imin return dtype(image) diff --git a/skimage/util/montage.py b/skimage/util/montage.py index 57ec7274..6c23ccbf 100644 --- a/skimage/util/montage.py +++ b/skimage/util/montage.py @@ -84,7 +84,7 @@ def montage2d(arr_in, fill='mean', rescale_intensity=False): if fill == 'mean': fill = arr_in.mean() - n_missing = int((alpha ** 2.) - n_images) + n_missing = int((alpha**2.) - n_images) missing = np.ones((n_missing, height, width), dtype=arr_in.dtype) * fill arr_out = np.vstack((arr_in, missing)) diff --git a/skimage/util/tests/test_dtype.py b/skimage/util/tests/test_dtype.py index 2ef0044f..816d5d4c 100644 --- a/skimage/util/tests/test_dtype.py +++ b/skimage/util/tests/test_dtype.py @@ -12,11 +12,13 @@ dtype_range = {np.uint8: (0, 255), np.float32: (-1.0, 1.0), np.float64: (-1.0, 1.0)} + def _verify_range(msg, x, vmin, vmax, dtype): assert_equal(x[0], vmin) assert_equal(x[-1], vmax) assert x.dtype == dtype + def test_range(): for dtype in dtype_range: imin, imax = dtype_range[dtype]