Commit eeca288e authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'mnt/sample-line' into 'master'

RF: Sample along line tool supports 2D and multi-channel images

See merge request fsl/fsleyes/fsleyes!292
parents 2af711d7 8c7e2139
......@@ -18,6 +18,9 @@ Changed
* Small improvementsto the *File* |right_arrow| *Add from XNAT* dialog.
* The *Sample along line* tool now supports 2D and multi-channel (e.g. RGB)
images (currently plotting the mean intensity across channels for the
latter).
Fixed
......
......@@ -36,6 +36,60 @@ import fsleyes.plotting.plotcanvas as plotcanvas
import fsleyes.plugins.profiles.samplelineprofile as samplelineprofile
def sampleAlongLine(data, start, end, resolution, order):
"""Samples from ``data``, along a line between ``start`` and ``end``.
:arg data: 3D array
:arg start: Start coordinate
:arg end: End coordinate
:arg resolution: Number of points to sample
:arg order: Interpolation (see ``scipy.ndimage.map_coordinates``)
:returns: Tuple containing:
- 1D Numpy array containing the sampled values
- ``(3, N)`` numpy array containing the coordinates for
each sample
"""
start = list(start)
end = list(end)
shape = data.shape
coords = np.linspace(start, end, resolution).T
# map_coordinates doesn't take
# kindly to dims of length 1
data = data.squeeze()
if any(s == 1 for s in shape):
drop = [i for i, s in enumerate(shape) if s == 1]
mapcoords = np.delete(coords, drop, axis=0)
else:
mapcoords = coords
# multi-channel data?
if len(data.dtype) > 1:
channels = []
for chan in data.dtype.fields.keys():
channels.append(data[chan])
data = channels
else:
data = [data]
ys = []
for arr in data:
ys.append(ndimage.map_coordinates(arr,
mapcoords,
order=order,
output=np.float64))
# For multi channel data, we currently
# just take the mean across all
# channels, but this might change in
# the future (if there is any need).
if len(ys) > 1: y = np.mean(ys, axis=0)
else: y = ys[0]
return y, coords
class SampleLineAction(actions.ToggleControlPanelAction):
"""The ``SampleLineAction`` simply shows/hides a :class:`SampleLinePanel`.
"""
......@@ -164,22 +218,17 @@ class SampleLineDataSeries(plotting.DataSeries):
change. Re-samples the data from the image.
"""
overlay = self.overlay
opts = self.displayCtx.getOpts(overlay)
data = overlay[self.__index]
resolution = self.resolution
order = self.interp
normalisex = 'x' in self.normalise
normalisey = 'y' in self.normalise
opts = self.displayCtx.getOpts(self.overlay)
data = self.overlay[self.__index]
start = self.__start
end = self.__end
coords = np.zeros((3, resolution))
coords[0, :] = np.linspace(start[0], end[0], resolution)
coords[1, :] = np.linspace(start[1], end[1], resolution)
coords[2, :] = np.linspace(start[2], end[2], resolution)
y = ndimage.map_coordinates(data, coords, order=order,
output=np.float64)
y, coords = sampleAlongLine(data, start, end, resolution, order)
if normalisey:
y = (y - y.min()) / (y.max() - y.min())
......
......@@ -23,6 +23,57 @@ from fsleyes.tests import (run_with_orthopanel,
datadir = op.join(op.dirname(__file__), '..', 'testdata')
def test_sampleAlongLine():
data = np.random.randint(1, 100, (20, 20, 20))
start = [0, 0, 0]
end = [0, 0, data.shape[2] - 1]
y, coords = sampleline.sampleAlongLine(data, start, end, data.shape[2], 0)
assert np.all(np.isclose(y, data[0, 0, :]))
assert np.all(coords[0, :] == 0)
assert np.all(coords[1, :] == 0)
assert np.all(coords[2, :] == np.arange(data.shape[2]))
def test_sampleAlongLine_2D():
data = np.random.randint(1, 100, (20, 20, 1))
start = [0, 0, 0]
end = [0, 19, 0]
y, coords = sampleline.sampleAlongLine(data, start, end, 20, 0)
assert np.all(np.isclose(y, data[0, :, 0]))
assert np.all(coords[0, :] == 0)
assert np.all(coords[1, :] == np.arange(20))
assert np.all(coords[2, :] == 0)
def test_sampleAlongLine_RGB():
rgbdtype = np.dtype([('R', 'uint8'), ('G', 'uint8'), ('B', 'uint8')])
data = np.zeros((20, 20, 20), dtype=rgbdtype)
data['R'] = np.random.randint(1, 100, (20, 20, 20))
data['G'] = np.random.randint(1, 100, (20, 20, 20))
data['B'] = np.random.randint(1, 100, (20, 20, 20))
start = [0, 0, 0]
end = [0, 0, 19]
y, coords = sampleline.sampleAlongLine(data, start, end, 20, 0)
expy = np.mean((data['R'][0, 0, :],
data['G'][0, 0, :],
data['B'][0, 0, :]), axis=0)
assert np.all(np.isclose(y, expy))
assert np.all(coords[0, :] == 0)
assert np.all(coords[1, :] == 0)
assert np.all(coords[2, :] == np.arange(20))
def test_SampleLineDataSeries():
run_with_orthopanel(_test_SampleLineDataSeries)
def _test_SampleLineDataSeries(panel, overlayList, displayCtx):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment