From 9ece0b576c0d42435711a1a4c728d8c636c5695c Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Fri, 28 Jun 2013 16:55:33 -0500 Subject: [PATCH 1/7] FEAT: Automatically switch between RGB / grayscale lineprofile --- skimage/viewer/plugins/lineprofile.py | 92 ++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 17 deletions(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index c9ceedc5..8c01488f 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -60,7 +60,7 @@ class LineProfile(PlotPlugin): if not self._limit_type is None: self.ax.set_ylim(self.limits) - h, w = image.shape + h, w = image.shape[0:2] x = [w / 3, 2 * w / 3] y = [h / 2] * 2 @@ -71,7 +71,9 @@ class LineProfile(PlotPlugin): self.line_tool.end_points = np.transpose([x, y]) scan_data = profile_line(image, self.line_tool.end_points) - self.profile = self.ax.plot(scan_data, 'k-')[0] + + self.reset_axes(scan_data) + self._autoscale_view() def help(self): @@ -90,7 +92,7 @@ class LineProfile(PlotPlugin): profile: 1d array Profile of intensity values. """ - profile = self.profile.get_ydata() + profile = self.profile[0].get_ydata() return self.line_tool.end_points, profile def _autoscale_view(self): @@ -105,17 +107,36 @@ class LineProfile(PlotPlugin): scan = profile_line(self.image_viewer.original_image, end_points, linewidth=self.line_tool.linewidth) - self.profile.set_xdata(np.arange(scan.shape[0])) - self.profile.set_ydata(scan) + try: + if scan[1].shape != len(self.profile): + self.reset_axes(scan) + except: + self.reset_axes(scan) + + for i in range(len(scan[0])): + self.profile[i].set_xdata(np.arange(scan.shape[0])) + self.profile[i].set_ydata(scan[:, i]) self.ax.relim() if self.useblit: - self.ax.draw_artist(self.profile) + self.ax.draw_artist(self.profile[0]) self._autoscale_view() self.redraw() + def reset_axes(self, scan_data): + # Clear lines out + for line in self.ax.lines: + self.ax.lines = [] + + if scan_data.shape[1] == 1: + self.profile = self.ax.plot(scan_data, 'k-') + else: + self.profile = self.ax.plot(scan_data[:, 0], 'r-', + scan_data[:, 1], 'g-', + scan_data[:, 2], 'b-') + def profile_line(img, end_points, linewidth=1): """Return the intensity profile of an image measured along a scan line. @@ -140,17 +161,44 @@ def profile_line(img, end_points, linewidth=1): x2, y2 = point2 = np.asarray(point2, dtype=float) dx, dy = point2 - point1 - # Quick calculation if perfectly horizontal or vertical (remove?) + # Quick calculation if perfectly horizontal or vertical if x1 == x2: - pixels = img[min(y1, y2): max(y1, y2) + 1, - x1 - linewidth / 2: x1 + linewidth / 2 + 1] - intensities = pixels.mean(axis=1) - return intensities + if img.ndim == 2: + pixels = img[min(y1, y2): max(y1, y2) + 1, + x1 - linewidth / 2: x1 + linewidth / 2 + 1] + return pixels.mean(axis=1)[:, np.newaxis] + else: + for i in range(3): + try: + temp = img[min(y1, y2): max(y1, y2) + 1, + x1 - linewidth / 2: x1 + linewidth / 2 + 1, i] + pixels = np.concatenate((pixels, temp[..., np.newaxis]), + axis=2) + del temp + except: + pixels = img[min(y1, y2): max(y1, y2) + 1, + x1 - linewidth / 2: x1 + linewidth / 2 + 1, + i][..., np.newaxis] + return pixels.mean(axis=1) + elif y1 == y2: - pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, - min(x1, x2): max(x1, x2) + 1] - intensities = pixels.mean(axis=0) - return intensities + if img.ndim == 2: + pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, + min(x1, x2): max(x1, x2) + 1] + return pixels.mean(axis=1)[..., np.newaxis] + else: + for i in range(3): + try: + temp = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, + min(x1, x2): max(x1, x2) + 1, i] + pixels = np.concatenate((pixels, temp[..., np.newaxis]), + axis=2) + del temp + except: + pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, + min(x1, x2): max(x1, x2) + 1, + i][..., np.newaxis] + return pixels.mean(axis=0) theta = np.arctan2(dy, dx) a = dy / dx @@ -165,7 +213,17 @@ def profile_line(img, end_points, linewidth=1): perp_xs = - a * perp_ys + (line_x + a * line_y)[:, np.newaxis] perp_lines = np.array([perp_ys, perp_xs]) - pixels = ndi.map_coordinates(img, perp_lines) + if img.ndim == 3: + pixels = np.zeros((perp_lines.shape[1], y_width * 2 + 1, 3)) + for i in range(3): + pixels[..., i] = ndi.map_coordinates(img[..., i], perp_lines) + else: + pixels = ndi.map_coordinates(img, perp_lines) + pixels = pixels[..., np.newaxis] + intensities = pixels.mean(axis=1) - return intensities + if intensities.ndim == 1: + return intensities[..., np.newaxis] + else: + return intensities From 2b5930ad60c091bef5bb92683b73542b89ab5845 Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Fri, 28 Jun 2013 16:56:25 -0500 Subject: [PATCH 2/7] DOC: Add viewer example for RGB line profile --- viewer_examples/plugins/lineprofile_rgb.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 viewer_examples/plugins/lineprofile_rgb.py diff --git a/viewer_examples/plugins/lineprofile_rgb.py b/viewer_examples/plugins/lineprofile_rgb.py new file mode 100644 index 00000000..86b71d5d --- /dev/null +++ b/viewer_examples/plugins/lineprofile_rgb.py @@ -0,0 +1,9 @@ +from skimage import data +from skimage.viewer import ImageViewer +from skimage.viewer.plugins.lineprofile import LineProfile + + +image = data.chelsea() +viewer = ImageViewer(image) +viewer += LineProfile() +viewer.show() From e20aa7c3815da3a5a2b76722835f0589a34d259b Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Fri, 28 Jun 2013 17:40:05 -0500 Subject: [PATCH 3/7] FIX: refactor code, fix linewidth calculation --- skimage/viewer/plugins/lineprofile.py | 77 +++++++++++++-------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index 8c01488f..2ea20187 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -138,13 +138,29 @@ class LineProfile(PlotPlugin): scan_data[:, 2], 'b-') +def _calc_horiz(img, x1, x2, y1, y2, linewidth): + # Quick calculation if perfectly horizontal + pixels = img[min(y1, y2): max(y1, y2) + 1, + x1 - linewidth / 2: x1 + linewidth / 2 + 1] + intensities = pixels.mean(axis=1) + return intensities + + +def _calc_vert(img, x1, x2, y1, y2, linewidth): + # Quick calculation if perfectly vertical + pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, + min(x1, x2): max(x1, x2) + 1] + intensities = pixels.mean(axis=0) + return intensities + + def profile_line(img, end_points, linewidth=1): """Return the intensity profile of an image measured along a scan line. Parameters ---------- - img : 2d array - The image. + img : 2d or 3d array + The image, in grayscale (2d) or RGB (3d) format. end_points: (2, 2) list End points ((x1, y1), (x2, y2)) of scan line. linewidth: int @@ -160,45 +176,28 @@ def profile_line(img, end_points, linewidth=1): x1, y1 = point1 = np.asarray(point1, dtype=float) x2, y2 = point2 = np.asarray(point2, dtype=float) dx, dy = point2 - point1 + channels = 1 + if img.ndim == 3: + channels = 3 # Quick calculation if perfectly horizontal or vertical if x1 == x2: - if img.ndim == 2: - pixels = img[min(y1, y2): max(y1, y2) + 1, - x1 - linewidth / 2: x1 + linewidth / 2 + 1] - return pixels.mean(axis=1)[:, np.newaxis] - else: - for i in range(3): - try: - temp = img[min(y1, y2): max(y1, y2) + 1, - x1 - linewidth / 2: x1 + linewidth / 2 + 1, i] - pixels = np.concatenate((pixels, temp[..., np.newaxis]), - axis=2) - del temp - except: - pixels = img[min(y1, y2): max(y1, y2) + 1, - x1 - linewidth / 2: x1 + linewidth / 2 + 1, - i][..., np.newaxis] - return pixels.mean(axis=1) + for i in range(channels): + try: + intensities = np.concatenate( + (intensities, + _calc_horiz(img, x1, x2, y1, y2, linewidth)), axis=1) + except: + intensities = _calc_horiz(img, x1, x2, y1, y2, linewidth) elif y1 == y2: - if img.ndim == 2: - pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, - min(x1, x2): max(x1, x2) + 1] - return pixels.mean(axis=1)[..., np.newaxis] - else: - for i in range(3): - try: - temp = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, - min(x1, x2): max(x1, x2) + 1, i] - pixels = np.concatenate((pixels, temp[..., np.newaxis]), - axis=2) - del temp - except: - pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, - min(x1, x2): max(x1, x2) + 1, - i][..., np.newaxis] - return pixels.mean(axis=0) + for i in range(channels): + try: + intensities = np.concatenate( + (intensities, + _calc_vert(img, x1, x2, y1, y2, linewidth)), axis=1) + except: + intensities = _calc_vert(img, x1, x2, y1, y2, linewidth) theta = np.arctan2(dy, dx) a = dy / dx @@ -214,9 +213,9 @@ def profile_line(img, end_points, linewidth=1): perp_lines = np.array([perp_ys, perp_xs]) if img.ndim == 3: - pixels = np.zeros((perp_lines.shape[1], y_width * 2 + 1, 3)) - for i in range(3): - pixels[..., i] = ndi.map_coordinates(img[..., i], perp_lines) + pixels = [ndi.map_coordinates(img[..., i], perp_lines) + for i in range(3)] + pixels = np.transpose(np.asarray(pixels), (1, 2, 0)) else: pixels = ndi.map_coordinates(img, perp_lines) pixels = pixels[..., np.newaxis] From e790fcc44eb3edfd1e9dce0257512c79f34148eb Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Fri, 28 Jun 2013 22:25:12 -0500 Subject: [PATCH 4/7] FIX: lineprofile no longer flips on left half and cardinals work --- skimage/viewer/plugins/lineprofile.py | 35 ++++++++++----------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index 2ea20187..28b2f4aa 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -138,20 +138,16 @@ class LineProfile(PlotPlugin): scan_data[:, 2], 'b-') -def _calc_horiz(img, x1, x2, y1, y2, linewidth): +def _calc_vert(img, x1, x2, y1, y2, linewidth): # Quick calculation if perfectly horizontal pixels = img[min(y1, y2): max(y1, y2) + 1, x1 - linewidth / 2: x1 + linewidth / 2 + 1] - intensities = pixels.mean(axis=1) - return intensities + # Reverse index if necessary + if y2 > y1: + pixels = pixels[::-1, :] -def _calc_vert(img, x1, x2, y1, y2, linewidth): - # Quick calculation if perfectly vertical - pixels = img[y1 - linewidth / 2: y1 + linewidth / 2 + 1, - min(x1, x2): max(x1, x2) + 1] - intensities = pixels.mean(axis=0) - return intensities + return pixels.mean(axis=1)[:, np.newaxis] def profile_line(img, end_points, linewidth=1): @@ -180,31 +176,26 @@ def profile_line(img, end_points, linewidth=1): if img.ndim == 3: channels = 3 - # Quick calculation if perfectly horizontal or vertical + # Quick calculation if perfectly vertical; shortcuts div0 error if x1 == x2: for i in range(channels): try: intensities = np.concatenate( (intensities, - _calc_horiz(img, x1, x2, y1, y2, linewidth)), axis=1) + _calc_vert(img[..., i], x1, x2, y1, y2, + linewidth)), axis=1) except: - intensities = _calc_horiz(img, x1, x2, y1, y2, linewidth) - - elif y1 == y2: - for i in range(channels): - try: - intensities = np.concatenate( - (intensities, - _calc_vert(img, x1, x2, y1, y2, linewidth)), axis=1) - except: - intensities = _calc_vert(img, x1, x2, y1, y2, linewidth) + intensities = _calc_vert(img[..., i], + x1, x2, y1, y2, + linewidth) + return intensities theta = np.arctan2(dy, dx) a = dy / dx b = y1 - a * x1 length = np.hypot(dx, dy) - line_x = np.linspace(min(x1, x2), max(x1, x2), np.ceil(length)) + line_x = np.linspace(x2, x1, np.ceil(length)) line_y = line_x * a + b y_width = abs(linewidth * np.cos(theta) / 2) perp_ys = np.array([np.linspace(yi - y_width, From b4e71ecd432ddfde43769121eb5a1610006f74a4 Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Sat, 29 Jun 2013 15:09:58 -0500 Subject: [PATCH 5/7] FIX: remove blit, fix 0-length error on grayscale images --- skimage/viewer/plugins/lineprofile.py | 29 ++++++++++----------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index 28b2f4aa..d15d22b1 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -82,18 +82,18 @@ class LineProfile(PlotPlugin): "Select and drag ends of the scan line to adjust it.") return '\n'.join(helpstr) - def get_profile(self): + def get_profiles(self): """Return intensity profile of the selected line. Returns ------- end_points: (2, 2) array The positions ((x1, y1), (x2, y2)) of the line ends. - profile: 1d array - Profile of intensity values. + profile: list of 1d arrays + Profile of intensity values. Length 1 (grayscale) or 3 (rgb). """ - profile = self.profile[0].get_ydata() - return self.line_tool.end_points, profile + profiles = [data.get_ydata() for data in self.profile] + return self.line_tool.end_points, profiles def _autoscale_view(self): if self.limits is None: @@ -119,9 +119,6 @@ class LineProfile(PlotPlugin): self.ax.relim() - if self.useblit: - self.ax.draw_artist(self.profile[0]) - self._autoscale_view() self.redraw() @@ -178,16 +175,12 @@ def profile_line(img, end_points, linewidth=1): # Quick calculation if perfectly vertical; shortcuts div0 error if x1 == x2: - for i in range(channels): - try: - intensities = np.concatenate( - (intensities, - _calc_vert(img[..., i], x1, x2, y1, y2, - linewidth)), axis=1) - except: - intensities = _calc_vert(img[..., i], - x1, x2, y1, y2, - linewidth) + if channels == 1: + img = img[:, :, np.newaxis] + + img = np.rollaxis(img, -1) + intensities = np.hstack([_calc_vert(im, x1, x2, y1, y2, linewidth) + for im in img]) return intensities theta = np.arctan2(dy, dx) From 19a3d335730b11085852e29bbe8d358b1cd8c158 Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Sat, 29 Jun 2013 15:18:53 -0500 Subject: [PATCH 6/7] FIX: only reset axes when gray <-> rgb, not every update --- skimage/viewer/plugins/lineprofile.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index d15d22b1..af65f081 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -107,10 +107,7 @@ class LineProfile(PlotPlugin): scan = profile_line(self.image_viewer.original_image, end_points, linewidth=self.line_tool.linewidth) - try: - if scan[1].shape != len(self.profile): - self.reset_axes(scan) - except: + if scan[1].shape != len(self.profile): self.reset_axes(scan) for i in range(len(scan[0])): From 6f775400b5643fe4b015b11c65be8a331ee2d238 Mon Sep 17 00:00:00 2001 From: "Josh Warner (Mac)" Date: Sat, 29 Jun 2013 15:34:03 -0500 Subject: [PATCH 7/7] FIX: No longer reset for each update --- skimage/viewer/plugins/lineprofile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skimage/viewer/plugins/lineprofile.py b/skimage/viewer/plugins/lineprofile.py index af65f081..0d555eb5 100644 --- a/skimage/viewer/plugins/lineprofile.py +++ b/skimage/viewer/plugins/lineprofile.py @@ -107,7 +107,7 @@ class LineProfile(PlotPlugin): scan = profile_line(self.image_viewer.original_image, end_points, linewidth=self.line_tool.linewidth) - if scan[1].shape != len(self.profile): + if scan.shape[1] != len(self.profile): self.reset_axes(scan) for i in range(len(scan[0])):