Merge pull request #205 from amueller/pep8

COSMIT: PEP8 compatibility and other cleanups.
This commit is contained in:
Stefan van der Walt
2012-06-29 02:33:43 -07:00
101 changed files with 1273 additions and 1122 deletions
+5 -2
View File
@@ -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()
+5 -4
View File
@@ -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.
+1 -1
View File
@@ -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
+12 -17
View File
@@ -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()
+10 -2
View File
@@ -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.
+5 -4
View File
@@ -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()
+7 -6
View File
@@ -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())
)
+20 -12
View File
@@ -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],
-1
View File
@@ -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)
-1
View File
@@ -74,4 +74,3 @@ def test_rescale_out_range():
if __name__ == '__main__':
from numpy import testing
testing.run_module_suite()
+43 -45
View File
@@ -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])
+6 -7
View File
@@ -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
+6 -6
View File
@@ -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
+6 -7
View File
@@ -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
+7 -6
View File
@@ -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())
)
+1 -2
View File
@@ -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
+31 -31
View File
@@ -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)
+3 -1
View File
@@ -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()
+7 -6
View File
@@ -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()
-1
View File
@@ -65,4 +65,3 @@ def test_num_peaks():
if __name__ == '__main__':
from numpy import testing
testing.run_module_suite()
+2 -3
View File
@@ -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()
+13 -13
View File
@@ -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)
'''
#
+8 -2
View File
@@ -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.
+16 -11
View File
@@ -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
+5 -6
View File
@@ -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)
+7 -6
View File
@@ -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())
)
+6 -1
View File
@@ -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"""
+13 -13
View File
@@ -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()
+3 -1
View File
@@ -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()
+9 -7
View File
@@ -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),
+1
View File
@@ -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.
+43 -40
View File
@@ -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
+1
View File
@@ -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.
+6 -5
View File
@@ -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())
)
+4 -3
View File
@@ -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)
+17 -15
View File
@@ -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()
+49 -43
View File
@@ -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()
+3
View File
@@ -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],
+6
View File
@@ -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.
+5 -6
View File
@@ -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
+148 -131
View File
@@ -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)
+1 -1
View File
@@ -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)
+2
View File
@@ -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()
+5
View File
@@ -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
+10 -3
View File
@@ -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.
+6 -15
View File
@@ -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()
self.callback()
+7 -8
View File
@@ -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))
self.v_hist.update_hist(v, (0, 0, 0))
+6 -10
View File
@@ -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)
+4
View File
@@ -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'
+21 -25
View File
@@ -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)
+4 -2
View File
@@ -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):
+6 -5
View File
@@ -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())
)
+3
View File
@@ -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')
+3 -7
View File
@@ -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()
+4 -5
View File
@@ -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()
+7 -3
View File
@@ -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()
+10 -7
View File
@@ -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')
+5 -4
View File
@@ -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)
+2
View File
@@ -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]])
+6
View File
@@ -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)
+3 -1
View File
@@ -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')
+3 -1
View File
@@ -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
+2
View File
@@ -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
+55 -51
View File
@@ -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"]
+14 -15
View File
@@ -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
+18 -14
View File
@@ -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:
+6 -5
View File
@@ -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())
)
+36 -35
View File
@@ -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()
+23 -4
View File
@@ -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']
+3 -2
View File
@@ -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
+7 -3
View File
@@ -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)
+11 -7
View File
@@ -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
+7 -6
View File
@@ -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())
)
+60 -59
View File
@@ -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**<pixel-number> 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
+1
View File
@@ -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],
@@ -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(
+1 -1
View File
@@ -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()
+3 -1
View File
@@ -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)
+3 -3
View File
@@ -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)
+31 -28
View File
@@ -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)
+188 -194
View File
@@ -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()
+52 -56
View File
@@ -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:
-1
View File
@@ -3,4 +3,3 @@
if __name__ == "__main__":
from skimage.scripts import skivi
skivi.main()
+2 -1
View File
@@ -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()
@@ -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)
+8 -8
View File
@@ -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)
+1
View File
@@ -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.
+4 -3
View File
@@ -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
+10 -11
View File
@@ -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)
+1
View File
@@ -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.
+5 -4
View File
@@ -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
+14 -16
View File
@@ -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)
+7 -7
View File
@@ -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())
)
@@ -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
@@ -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()
+4 -1
View File
@@ -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))
+10 -7
View File
@@ -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
@@ -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
+7 -7
View File
@@ -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)
+1 -1
View File
@@ -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))

Some files were not shown because too many files have changed in this diff Show More