import numpy as np
import matplotlib
import matplotlib.cm as cm
import matplotlib.pyplot as plt
import fibsem.conversions
import fibsem.milling
def _matplotlib_center_to_bottomleft(center_x, center_y, roi_width,
roi_height):
""" Convert ROI center coordinates to matplotlib bottom left coordinates.
Parameters
----------
center_x : float
Position in x of Autoscript ROI center coordinate.
center_y : float
Position in y of Autoscript ROI center coordinate.
roi_width : float
Width in meters of Autoscript milling ROI.
roi_height : float
Height in meters of Autoscript milling ROI.
Returns
-------
xy
Matplotlib bottom left coordinate is in x, y format.
"""
left = center_x - (roi_width / 2)
bottom = center_y - (roi_height / 2)
xy = (left, bottom)
return xy
def _matplotlib_line_width(image):
"""Return appropriate line width for the image dimensions.
Parameters
----------
image : AdornedImage or numpy array
Input image.
Returns
-------
line_width
Line width for use as keyword argument in matplotlib display.
"""
image_shape = fibsem.conversions._convert_to_numpy(image).shape
line_width = np.max(image_shape) / 20
try:
pixelsize = image.metadata.binary_result.pixel_size
except AttributeError:
pass
else:
line_width = line_width * pixelsize.x
return line_width
def _matplotlib_rotation_transform(ax, center_x, center_y, theta):
"""[summary]
Parameters
----------
ax : [type]
[description]
center_x : [type]
[description]
center_y : [type]
[description]
theta : [type]
[description]
Returns
-------
[type]
[description]
"""
angle = fibsem.conversions._angle_to_matplotlib(theta)
ts = ax.transData
tr = matplotlib.transforms.Affine2D().rotate_deg_around(center_x,
center_y,
angle)
t = tr + ts
return t
[docs]def quick_plot(image):
"""Display image with matplotlib.pyplot
Parameters
----------
image : Adorned image or numpy array
Input image.
Returns
-------
fig, ax
Matplotlib figure and axis objects.
"""
fig, ax = plt.subplots(1)
display_image = fibsem.conversions._convert_to_numpy(image)
height, width = display_image.shape
try:
pixelsize_x = image.metadata.binary_result.pixel_size.x
pixelsize_y = image.metadata.binary_result.pixel_size.y
except AttributeError:
extent_kwargs = [-(width / 2), +(width / 2),
-(height / 2), +(height / 2)]
ax.set_xlabel('Distance from origin (pixels)')
else:
extent_kwargs = [-(width / 2) * pixelsize_x,
+(width / 2) * pixelsize_x,
-(height / 2) * pixelsize_y,
+(height / 2) * pixelsize_y]
ax.set_xlabel('Distance from origin (meters) \n'
'1 pixel = {} meters'.format(pixelsize_x))
ax.set_xlim(extent_kwargs[0], extent_kwargs[1])
ax.set_ylim(extent_kwargs[2], extent_kwargs[3])
ax.imshow(display_image, cmap='gray', extent=extent_kwargs)
return fig, ax
[docs]def draw_object_axes(image, axis_coord_list, line_color='red'):
"""Display plot of image with major axis overlaid.
Parameters
----------
image : 2D umpy image array.
axis_coords : major axis of sample segmentation.
Coordinates are in row, column format.
line_color : optional. Color of matplotlib line patch.
Returns
-------
fig, ax
Matplotlib figure and axis objects.
"""
fig, ax = quick_plot(image)
colors = cm.gist_rainbow(np.linspace(0, 1, len(axis_coord_list)))
for axis_coords, color in zip(axis_coord_list, colors):
(y, x), (y1, x1) = axis_coords
dx = x1 - x
dy = y1 - y
line_width = _matplotlib_line_width(image)
arrow = matplotlib.patches.Arrow(x, y, dx, dy,
line_width,
color=color)
ax.add_patch(arrow)
return fig, ax
[docs]def draw_milling_axes(image, major_axis, start_lamella, end_lamella,
line_colors=['yellow', 'orange']):
"""Display plot of image with head to tail & tail to head axis overlaid.
Parameters
----------
image : Adorned image or 2D umpy image array.
major_axis : major axis of sample segmentation.
Coordinates are in row, column format.
start_lamella : float
Distance lamella should begin from head, as a fractional percentage of
the whole sample length.
end_lamella : float
Distance lamella should end from head, as a fractional percentage of
the whole sample length.
line_colors : list, optional
Coulors to display milling axes (the default is ['yellow', 'orange']).
Returns
-------
fig, ax
Matplotlib figure and axis objects.
"""
fig, ax = quick_plot(image)
results = fibsem.milling._milling_directions(
major_axis, start_lamella, end_lamella)
for axis_coords, line_color in zip(results, line_colors):
(y, x), (y1, x1) = axis_coords
dx = x1 - x
dy = y1 - y
line_width = _matplotlib_line_width(image)
arrow = matplotlib.patches.Arrow(x, y, dx, dy,
line_width,
color=line_color)
ax.add_patch(arrow)
return fig, ax
[docs]def draw_crosshairs(image, major_axis, minor_axis,
line_color=['red', 'blue']):
"""Display plot of image with major axis overlaid.
Parameters
----------
image : 2D umpy image array.
major_axis : major axis of sample segmentation.
Coordinates are in row, column format.
minor_axis : minor axis of sample segmentation.
Coordinates are in row, column format.
line_color : optional. List of colors for each axis (major and minor).
Returns
-------
fig, ax
Matplotlib figure and axis objects.
"""
fig, ax = quick_plot(image)
line_width = _matplotlib_line_width(image)
for i, axis in enumerate([major_axis, minor_axis]):
(y, x), (y1, x1) = axis
dx = x1 - x
dy = y1 - y
arrow = matplotlib.patches.Arrow(x, y, dx, dy,
line_width,
color=line_color[i])
ax.add_patch(arrow)
return fig, ax
[docs]def draw_rotated_rects(rectangle_coords, image=None,
show_center=True):
"""Display rectangle ROIs using matplotlib.
Parameters
----------
rectangle_coords : list.
List contains [center_x, center_y, width, height, depth, angle]
The input angle is in radians.
`center_x`, `center_y`, `width`, and `height` are in meters.
`depth` is ignored (not needed by matplotlib).
image : AdornedImage, optional.
Image to display, including pixel size metadata.
(the default is None, which means no background image)
show_center : bool, optional
Plot centerpoints of rectangles
(the default is False, which means centerpoints are not displayed)
Returns
-------
fig, ax
Matplotlib figure and axis objects.
"""
if image is not None:
fig, ax = quick_plot(image)
else:
fig, ax = plt.subplots(1)
colors = cm.gist_rainbow(np.linspace(0, 1, len(rectangle_coords)))
for coords, color in zip(rectangle_coords, colors):
center_x, center_y, roi_width, roi_height, depth, theta = coords
xy = _matplotlib_center_to_bottomleft(center_x,
center_y,
roi_width,
roi_height)
transformation = _matplotlib_rotation_transform(ax,
center_x,
center_y,
theta)
rect = matplotlib.patches.Rectangle(xy, roi_width, roi_height,
transform=transformation,
fill=False, color=color)
ax.add_patch(rect)
if show_center is True:
ax.plot(center_x, center_y, marker='o', markersize=3, color=color)
return fig, ax