Merge pull request #875 from blink1073/lineprofile-overlay

Add Shaded Polygon to LineProfile Plugin Output

Closes #865
This commit is contained in:
Juan Nunez-Iglesias
2014-02-12 19:25:24 +11:00
3 changed files with 97 additions and 15 deletions
+39 -14
View File
@@ -50,6 +50,44 @@ def profile_line(img, src, dst, linewidth=1,
The destination point is included in the profile, in contrast to
standard numpy indexing.
"""
perp_lines = _line_profile_coordinates(src, dst, linewidth=linewidth)
if img.ndim == 3:
pixels = [nd.map_coordinates(img[..., i], perp_lines,
order=order, mode=mode, cval=cval)
for i in range(img.shape[2])]
pixels = np.transpose(np.asarray(pixels), (1, 2, 0))
else:
pixels = nd.map_coordinates(img, perp_lines,
order=order, mode=mode, cval=cval)
intensities = pixels.mean(axis=1)
return intensities
def _line_profile_coordinates(src, dst, linewidth=1):
"""Return the coordinates of the profile of an image along a scan line.
Parameters
----------
src : 2-tuple of numeric scalar (float or int)
The start point of the scan line.
dst : 2-tuple of numeric scalar (float or int)
The end point of the scan line.
linewidth : int, optional
Width of the scan, perpendicular to the line
Returns
-------
coords : array, shape (2, N, C), float
The coordinates of the profile along the scan line. The length of the
profile is the ceil of the computed length of the scan line.
Notes
-----
This is a utility method meant to be used internally by skimage functions.
The destination point is included in the profile, in contrast to
standard numpy indexing.
"""
src_row, src_col = src = np.asarray(src, dtype=float)
dst_row, dst_col = dst = np.asarray(dst, dtype=float)
d_row, d_col = dst - src
@@ -70,18 +108,5 @@ def profile_line(img, src, dst, linewidth=1,
linewidth) for row_i in line_row])
perp_cols = np.array([np.linspace(col_i - col_width, col_i + col_width,
linewidth) for col_i in line_col])
perp_lines = np.array([perp_rows, perp_cols])
if img.ndim == 3:
pixels = [nd.map_coordinates(img[..., i], perp_lines,
order=order, mode=mode, cval=cval)
for i in range(img.shape[2])]
pixels = np.transpose(np.asarray(pixels), (1, 2, 0))
else:
pixels = nd.map_coordinates(img, perp_lines,
order=order, mode=mode, cval=cval)
intensities = pixels.mean(axis=1)
return intensities
return np.array([perp_rows, perp_cols])
+14 -1
View File
@@ -1,3 +1,4 @@
from __future__ import division
import warnings
import numpy as np
@@ -147,12 +148,24 @@ class LineProfile(PlotPlugin):
-------
line_image : (M, N) uint8 array, same shape as image
An array of 0s with the scanned line set to 255.
If the linewidth of the line tool is greater than 1,
sets the values within the profiled polygon to 128.
scan : (P,) or (P, 3) array of int or float
The line scan values across the image.
"""
(x1, y1), (x2, y2) = self.line_tool.end_points
end_points = self.line_tool.end_points
line_image = np.zeros(self.image_viewer.original_image.shape[:2],
np.uint8)
width = self.line_tool.linewidth
if width > 1:
rp, cp = measure.profile._line_profile_coordinates(
*end_points[:, ::-1], linewidth=width)
# the points are aliased, so create a polygon using the corners
yp = np.rint(rp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
xp = np.rint(cp[[0, 0, -1, -1],[0, -1, -1, 0]]).astype(int)
rp, cp = draw.polygon(yp, xp, line_image.shape)
line_image[rp, cp] = 128
(x1, y1), (x2, y2) = end_points.astype(int)
rr, cc = draw.line(y1, x1, y2, x2)
line_image[rr, cc] = 255
return line_image, self.scan_data
+44
View File
@@ -0,0 +1,44 @@
import skimage
import skimage.data as data
from skimage.viewer import ImageViewer
from numpy.testing import assert_equal, assert_allclose
def setup_line_profile(image):
from skimage.viewer.plugins.lineprofile import LineProfile
viewer = ImageViewer(skimage.img_as_float(image))
plugin = LineProfile()
viewer += plugin
return plugin
def test_line_profile():
""" Test a line profile using an ndim=2 image"""
plugin = setup_line_profile(data.camera())
line_image, scan_data = plugin.output()
for inp in [line_image.nonzero()[0].size,
line_image.sum() / line_image.max(),
scan_data.size]:
assert_equal(inp, 172)
assert_equal(line_image.shape, (512, 512))
assert_allclose(scan_data.max(), 0.9139, rtol=1e-3)
assert_allclose(scan_data.mean(), 0.2828, rtol=1e-3)
def test_line_profile_rgb():
""" Test a line profile using an ndim=3 image"""
plugin = setup_line_profile(data.chelsea())
for i in range(6):
plugin.line_tool._thicken_scan_line()
line_image, scan_data = plugin.output()
assert_equal(line_image[line_image == 128].size, 755)
assert_equal(line_image[line_image == 255].size, 151)
assert_equal(line_image.shape, (300, 451))
assert_equal(scan_data.shape, (152, 3))
assert_allclose(scan_data.max(), 0.772, rtol=1e-3)
assert_allclose(scan_data.mean(), 0.4355, rtol=1e-3)
if __name__ == "__main__":
from numpy.testing import run_module_suite
run_module_suite()