def draw_correspondence_edges(ax: plt.Axes, traj_1: trajectory.PosePath3D, traj_2: trajectory.PosePath3D, plot_mode: PlotMode, style: str = '-', color: str = "black", alpha: float = 1.) -> None: """ Draw edges between corresponding poses of two trajectories. Trajectories must be synced, i.e. having the same number of poses. :param ax: plot axis :param traj_{1,2}: trajectory.PosePath3D or trajectory.PoseTrajectory3D :param plot_mode: PlotMode value :param style: matplotlib line style :param color: matplotlib color :param alpha: alpha value for transparency """ if not traj_1.num_poses == traj_2.num_poses: raise PlotException( "trajectories must have same length to draw pose correspondences" " - try to synchronize them first") n = traj_1.num_poses interweaved_positions = np.empty((n * 2, 3)) interweaved_positions[0::2, :] = traj_1.positions_xyz interweaved_positions[1::2, :] = traj_2.positions_xyz colors = np.array(n * [color]) markers = colored_line_collection(interweaved_positions, colors, plot_mode, step=2, alpha=alpha, linestyles=style) ax.add_collection(markers)
def _plot_on_axis(self, ax: plt.Axes, **collection_options: Any) -> mpl_collections.Collection: # Step-1: Convert value_map to a list of polygons to plot. polygon_list = self._get_polygon_units() collection: mpl_collections.Collection = mcoll.PolyCollection( [c.polygon for c in polygon_list], cmap=self._config['colormap'], **collection_options ) collection.set_clim(self._config.get('vmin'), self._config.get('vmax')) collection.set_array(np.array([c.value for c in polygon_list])) # Step-2: Plot the polygons ax.add_collection(collection) collection.update_scalarmappable() # Step-3: Write annotation texts if self._config.get('annotation_map') or self._config.get('annotation_format'): self._write_annotations([(c.center, c.annot) for c in polygon_list], collection, ax) ax.set(xlabel='column', ylabel='row') # Step-4: Draw colorbar if applicable if self._config.get('plot_colorbar'): self._plot_colorbar(collection, ax) # Step-5: Set min/max limits of x/y axis on the plot. rows = set([q.row for qubits in self._value_map.keys() for q in qubits]) cols = set([q.col for qubits in self._value_map.keys() for q in qubits]) min_row, max_row = min(rows), max(rows) min_col, max_col = min(cols), max(cols) min_xtick = np.floor(min_col) max_xtick = np.ceil(max_col) ax.set_xticks(np.arange(min_xtick, max_xtick + 1)) min_ytick = np.floor(min_row) max_ytick = np.ceil(max_row) ax.set_yticks(np.arange(min_ytick, max_ytick + 1)) ax.set_xlim((min_xtick - 0.6, max_xtick + 0.6)) ax.set_ylim((max_ytick + 0.6, min_ytick - 0.6)) # Step-6: Set title if self._config.get("title"): ax.set_title(self._config["title"], fontweight='bold') return collection
def draw_edges(graph: Graph, pos: dict, edges_list: list = None, edges_color: Union[str, list] = 'k', axes: plt.Axes = None) -> None: """ Draw graph edges. :param graph: graph :param pos: node -> position (x,y) dictionary :param edges_list: edges to draw :param edges_color: nodes color, scalar or sequence (matplotlib compatible) :param axes: axes to draw the graph at """ if axes is None: axes = plt.gca() edge_pos = np.asarray([(pos[e[0]], pos[e[1]]) for e in (edges_list or graph.edges)]) if isinstance(edges_color, str): edges_color = (edges_color, ) edges_color = tuple([colorConverter.to_rgba(c) for c in edges_color]) edges_collection = LineCollection(edge_pos, colors=edges_color, transOffset=axes.transData) edges_collection.set_zorder(1) # Edges go behind nodes. axes.add_collection(edges_collection)
def _draw_edges(ax: plt.Axes, pos: np.ndarray, g: np.ndarray, gcolor: str, galpha: float, glinewidths: float) -> None: lc = LineCollection(zip(pos[g.row], pos[g.col]), linewidths=0.25, zorder=0, color=gcolor, alpha=galpha) ax.add_collection(lc)
def traj_colormap(ax: plt.Axes, traj: trajectory.PosePath3D, array: ListOrArray, plot_mode: PlotMode, min_map: float, max_map: float, title: str = "", fig: typing.Optional[mpl.figure.Figure] = None) -> None: """ color map a path/trajectory in xyz coordinates according to an array of values :param ax: plot axis :param traj: trajectory.PosePath3D or trajectory.PoseTrajectory3D object :param array: Nx1 array of values used for color mapping :param plot_mode: PlotMode :param min_map: lower bound value for color mapping :param max_map: upper bound value for color mapping :param title: plot title :param fig: plot figure. Obtained with plt.gcf() if none is specified """ pos = traj.positions_xyz norm = mpl.colors.Normalize(vmin=min_map, vmax=max_map, clip=True) mapper = cm.ScalarMappable( norm=norm, cmap=SETTINGS.plot_trajectory_cmap) # cm.*_r is reversed cmap mapper.set_array(array) colors = [mapper.to_rgba(a) for a in array] line_collection = colored_line_collection(pos, colors, plot_mode) ax.add_collection(line_collection) ax.autoscale_view(True, True, True) if plot_mode == PlotMode.xyz: ax.set_zlim(np.amin(traj.positions_xyz[:, 2]), np.amax(traj.positions_xyz[:, 2])) if SETTINGS.plot_xyz_realistic: set_aspect_equal_3d(ax) if fig is None: fig = plt.gcf() cbar = fig.colorbar( mapper, ticks=[min_map, (max_map - (max_map - min_map) / 2), max_map]) cbar.ax.set_yticklabels([ "{0:0.3f}".format(min_map), "{0:0.3f}".format(max_map - (max_map - min_map) / 2), "{0:0.3f}".format(max_map) ]) if title: ax.legend(frameon=True) ax.set_title(title)
def draw_graph_edges(figure: plt.Figure, axis: plt.Axes, graph: nx.Graph): """draws graph edges from node to node, colored by weight Parameters ---------- figure: plt.Figure a matplotlib Figure axis: plt.Axes a matplotlib Axes, part of Figure graphs: nx.Graph a networkx graph, assumed to have edges formed like graph.add_edge((0, 1), (0, 2), weight=1.234) Notes ----- modifes figure and axis in-place """ weights = np.array([i["weight"] for i in graph.edges.values()]) # graph is (row, col), transpose to get (x, y) segments = [] for edge in graph.edges: segments.append([edge[0][::-1], edge[1][::-1]]) line_coll = LineCollection(segments, linestyle='solid', cmap="plasma", linewidths=0.3) line_coll.set_array(weights) vals = np.concatenate(line_coll.get_segments()) mnvals = vals.min(axis=0) mxvals = vals.max(axis=0) ppvals = vals.ptp(axis=0) buffx = 0.02 * ppvals[0] buffy = 0.02 * ppvals[1] line_coll.set_linewidth(0.3 * 512 / ppvals[0]) axis.add_collection(line_coll) axis.set_xlim(mnvals[0] - buffx, mxvals[0] + buffx) axis.set_ylim(mnvals[1] - buffy, mxvals[1] + buffy) # invert yaxis for image-like orientation axis.invert_yaxis() axis.set_aspect("equal") divider = make_axes_locatable(axis) cax = divider.append_axes("right", size="5%", pad=0.05) figure.colorbar(line_coll, ax=axis, cax=cax) axis.set_title("PCC neighbor graph")
def hexplot( ax: plt.Axes, grid: np.ndarray, data: np.ndarray, hex_size: float=11.5, cmap: str='viridis' ) -> plt.Axes: """ Plot grid and data on a hexagon grid. Useful for SOMs. Parameters ---------- ax : Axes to plot on. grid : Array of (x, y) tuples. data : Array of len(grid) with datapoint. hex_size : Radius in points determining the hexagon size. cmap : Colormap to use for colouring. Returns ------- ax : Axes with hexagon plot. """ # Create hexagons collection = RegularPolyCollection( numsides=6, sizes=(2 * np.pi * hex_size ** 2,), edgecolors=(0, 0, 0, 0), transOffset=ax.transData, offsets=grid, array=data, cmap=plt.get_cmap(cmap) ) # Scale the plot properly ax.add_collection(collection, autolim=True) ax.set_xlim(grid[:, 0].min() - 0.75, grid[:, 0].max() + 0.75) ax.set_ylim(grid[:, 1].min() - 0.75, grid[:, 1].max() + 0.75) ax.axis('off') return ax
def hexplot( ax: plt.Axes, grid: np.ndarray, data: np.ndarray, hex_size: float=11.5, cmap: str='viridis' ) -> plt.Axes: ''' Plot grid and data on a hexagon grid. Useful for SOMs. Parameters ---------- ax : Axes to plot on. grid : Array of (x, y) tuples. data : Array of len(grid) with datapoint. hex_size : Radius in points determining the hexagon size. cmap : Colormap to use for colouring. Returns ------- ax : Axes with hexagon plot. ''' # Create hexagons collection = RegularPolyCollection( numsides=6, sizes=(2 * np.pi * hex_size ** 2,), edgecolors=(0, 0, 0, 0), transOffset=ax.transData, offsets=grid, array=data, cmap=plt.get_cmap(cmap) ) # Scale the plot properly ax.add_collection(collection, autolim=True) ax.set_xlim(grid[:, 0].min() - 0.75, grid[:, 0].max() + 0.75) ax.set_ylim(grid[:, 1].min() - 0.75, grid[:, 1].max() + 0.75) ax.axis('off') return ax
def show(self, subplot: plt.Axes, data: np.ndarray, **kwargs): # data shape should be (num_joints, 3) # swap y and z for matplotlib dc = data.copy() dc[:, 2], dc[:, 1] = data[:, 1], data[:, 2] data = dc x = data[:, 0] y = data[:, 1] z = data[:, 2] if self._scatter is None: self._scatter = subplot.scatter(x, y, z) else: # Update 3D points self._scatter.set_offsets(data[:, :2]) if hasattr(self._scatter, "_offsets3d"): self._scatter._offsets3d = x, y, z self._scatter.stale = True else: self._scatter.set_alpha(1.) self._scatter.set_3d_properties(z, "z") if self.graph is not None: # plot lines between scatter plot dots edges = np.asarray(self.graph.edges) p = data[edges[:, 0]] q = data[edges[:, 1]] ls = np.hstack([p, q]) ls = ls.reshape((-1, 2, 3)) if self._lines is None or self._lines not in subplot.collections: self._lines = Line3DCollection(ls, linewidths=2, colors="dimgray") subplot.add_collection(self._lines) else: self._lines.set_segments(ls) set_3d_aspect_ratio_equal(subplot)
def plot_triv(fig: plt.Figure, ax: plt.Axes, A: Triangle, B: Triangle, dist: bool = True): _A, _B = get_triv(A, B) _A.plot(fig, ax, "black") _B.plot(fig, ax, "blue") ax.scatter([0, 1], [0, 0], c="black") ax.annotate("$(0, 0)$", [-0.01, -0.02]) ax.annotate("$(1, 0)$", [1, -0.02]) _A.plot(fig, ax, color="black") _A_offsets = np.array([ [0.07, 0.02], [-0.05, 0.03], [-0.02, -0.04], ]) _B.plot(fig, ax, color="blue") _B_offsets = np.array([ [0.05, 0.0125], [-0.03, 0.01], [-0.02, -0.04], ]) ax.annotate("$t$", _A.points[2] + np.array([-0.03, -0.005])) ax.annotate("$t^\prime$", _B.points[2] + np.array([-0.025, 0.01])) for i in range(3): ax.annotate(f"$\\alpha_{i}$", _A.points[i] + _A_offsets[i]) ax.annotate(f"$\\beta_{i}$", _B.points[i] + _B_offsets[i], color="blue") if dist: ax.add_collection( LineCollection([[_A.points[2], _B.points[2]]], color="black", linestyles="--")) ax.scatter(*_A.points[2], c="black") ax.scatter(*_B.points[2], c="black")
def drawRects(ax: plt.Axes, data, facecolor=None, alpha: float = None, edgecolor=None, linewidth: float = None, profile: dict = None, autolim=True) -> None: """ Draw multiple rectangles Args: ax: the plot axes data: either a 2D array of shape (num. rectangles, 4), or a list of tuples (x0, y0, x1, y1), where each row is a rectangle color: the face color edgecolor: the color of the edges alpha: alpha value for the rectangle (both facecolor and edgecolor) label: if given, a label is plotted at the center of the rectangle profile: the profile used, or None for default autolim: autoscale view """ facecolor = _fallbackColor(facecolor, profile, key='facecolor') edgecolor = _fallbackColor(edgecolor, profile, key='edgecolor') linewidth = _fallback(linewidth, profile, 'linewidth') rects = [] for coords in data: x0, y0, x1, y1 = coords rect = Rectangle((x0, y0), x1 - x0, y1 - y0) rects.append(rect) coll = PatchCollection(rects, linewidth=linewidth, alpha=alpha, edgecolor=edgecolor, facecolor=facecolor) ax.add_collection(coll, autolim=True) if autolim: ax.autoscale_view()
def draw_path(self, ax: plt.Axes, path, properties: Properties, z: int): pattern = self.pattern(properties) lineweight = self.lineweight(properties) color = properties.color if len(pattern) < 2: vertices, codes = _get_path_patch_data(path) patch = PathPatch(Path(vertices, codes), linewidth=lineweight, color=color, fill=False, zorder=z) ax.add_patch(patch) else: renderer = EzdxfLineTypeRenderer(pattern) segments = renderer.line_segments( path.flattening(self._max_distance, segments=16)) lines = LineCollection([((s.x, s.y), (e.x, e.y)) for s, e in segments], linewidths=lineweight, color=color, zorder=z) lines.set_capstyle('butt') ax.add_collection(lines)
def plot(self, ax: plt.Axes, fd): for i1, c1 in enumerate(fd.cities): for i2, c2 in enumerate(fd.cities): if c1 is c2: continue p1 = np.array([c1.x, c1.y]) p2 = np.array([c2.x, c2.y]) norm = p2 - p1 norm[0], norm[1] = -norm[1], norm[0] norm /= np.linalg.norm(norm) p1 += norm * 1 p2 += norm * 1 npts = 100 x = np.linspace(p1[0], p2[0], npts) y = np.linspace(p1[1], p2[1], npts) g = np.linspace(0, 1, npts) points = np.array([x, y]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) nc = plt.Normalize(g.min(), g.max()) lc = LineCollection(segments, cmap='copper', norm=nc) lc.set_array(g) lc.set_linewidth(self.get_amount(c1, c2) * 10) lc.set_zorder(0) ax.add_collection(lc)
def draw_line(self, ax: plt.Axes, start: Vector, end: Vector, properties: Properties, z: int): pattern = self.pattern(properties) lineweight = self.lineweight(properties) color = properties.color if len(pattern) < 2: ax.add_line( Line2D( (start.x, end.x), (start.y, end.y), linewidth=lineweight, color=color, zorder=z, )) else: renderer = EzdxfLineTypeRenderer(pattern) lines = LineCollection( [((s.x, s.y), (e.x, e.y)) for s, e in renderer.line_segment(start, end)], linewidths=lineweight, color=color, zorder=z) lines.set_capstyle('butt') ax.add_collection(lines)
def show_image(im: Union[np.ndarray, Tensor], axis: plt.Axes = None, fig: plt.Figure = None, title: Optional[str] = None, color_map: str = "inferno", stack_depth: int = 0) -> Optional[plt.Figure]: """Plots a given image onto an axis. The repeated invocation of this function will cause figure plot overlap. If `im` is 2D and the length of second dimension are 4 or 5, it will be viewed as bounding box data (x0, y0, w, h, <label>). ```python boxes = np.array([[0, 0, 10, 20, "apple"], [10, 20, 30, 50, "dog"], [40, 70, 200, 200, "cat"], [0, 0, 0, 0, "not_shown"], [0, 0, -10, -20, "not_shown2"]]) img = np.zeros((150, 150)) fig, axis = plt.subplots(1, 1) fe.util.show_image(img, fig=fig, axis=axis) # need to plot image first fe.util.show_image(boxes, fig=fig, axis=axis) ``` Users can also directly plot text ```python fig, axis = plt.subplots(1, 1) fe.util.show_image("apple", fig=fig, axis=axis) ``` Args: axis: The matplotlib axis to plot on, or None for a new plot. fig: A reference to the figure to plot on, or None if new plot. im: The image (width X height) / bounding box / text to display. title: A title for the image. color_map: Which colormap to use for greyscale images. stack_depth: Multiple images can be drawn onto the same axis. When stack depth is greater than zero, the `im` will be alpha blended on top of a given axis. Returns: plotted figure. It will be the same object as user have provided in the argument. """ if axis is None: fig, axis = plt.subplots(1, 1) axis.axis('off') # Compute width of axis for text font size bbox = axis.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) width, height = bbox.width * fig.dpi, bbox.height * fig.dpi space = min(width, height) if not hasattr(im, 'shape') or len(im.shape) < 2: # text data im = to_number(im) if hasattr(im, 'shape') and len(im.shape) == 1: im = im[0] im = im.item() if isinstance(im, bytes): im = im.decode('utf8') text = "{}".format(im) axis.text(0.5, 0.5, im, ha='center', transform=axis.transAxes, va='center', wrap=False, family='monospace', fontsize=min(45, space // len(text))) elif len(im.shape) == 2 and (im.shape[1] == 4 or im.shape[1] == 5): # Bounding Box Data. Should be (x0, y0, w, h, <label>) boxes = [] im = to_number(im) color = ["m", "r", "c", "g", "y", "b"][stack_depth % 6] for box in im: # Unpack the box, which may or may not have a label x0 = float(box[0]) y0 = float(box[1]) width = float(box[2]) height = float(box[3]) label = None if len(box) < 5 else str(box[4]) # Don't draw empty boxes, or invalid box if width <= 0 or height <= 0: continue r = Rectangle((x0, y0), width=width, height=height, fill=False, edgecolor=color, linewidth=3) boxes.append(r) if label: axis.text(r.get_x() + 3, r.get_y() + 3, label, ha='left', va='top', color=color, fontsize=max(8, min(14, width // len(label))), fontweight='bold', family='monospace') pc = PatchCollection(boxes, match_original=True) axis.add_collection(pc) else: if isinstance(im, torch.Tensor) and len(im.shape) > 2: # Move channel first to channel last channels = list(range(len(im.shape))) channels.append(channels.pop(0)) im = im.permute(*channels) # image data im = to_number(im) im_max = np.max(im) im_min = np.min(im) if np.issubdtype(im.dtype, np.integer): # im is already in int format im = im.astype(np.uint8) elif 0 <= im_min <= im_max <= 1: # im is [0,1] im = (im * 255).astype(np.uint8) elif -0.5 <= im_min < 0 < im_max <= 0.5: # im is [-0.5, 0.5] im = ((im + 0.5) * 255).astype(np.uint8) elif -1 <= im_min < 0 < im_max <= 1: # im is [-1, 1] im = ((im + 1) * 127.5).astype(np.uint8) else: # im is in some arbitrary range, probably due to the Normalize Op ma = abs( np.max(im, axis=tuple([i for i in range(len(im.shape) - 1)]) if len(im.shape) > 2 else None)) mi = abs( np.min(im, axis=tuple([i for i in range(len(im.shape) - 1)]) if len(im.shape) > 2 else None)) im = (((im + mi) / (ma + mi)) * 255).astype(np.uint8) # matplotlib doesn't support (x,y,1) images, so convert them to (x,y) if len(im.shape) == 3 and im.shape[2] == 1: im = np.reshape(im, (im.shape[0], im.shape[1])) alpha = 1 if stack_depth == 0 else 0.3 if len(im.shape) == 2: axis.imshow(im, cmap=plt.get_cmap(name=color_map), alpha=alpha) else: axis.imshow(im, alpha=alpha) if title is not None: axis.set_title(title, fontsize=min(20, 1 + width // len(title)), family='monospace') return fig
def cancellation_progression(cnclp: CnclProg, model_output: int = None, *, color_cycle: Sequence = None, ax: plt.Axes = None, figsize=(8.0, 6.0), title: str = None, xlabel="Estimated cancellation", ylabel="True cancellation", legend=True): if model_output is None: test_array = next(iter(next(iter(cnclp.values())).values())) if test_array.ndim != 2: n_model_outputs = test_array.shape[2] if n_model_outputs != 1: raise ValueError("When plotting cancellation progressions, the model_output argument can only be " "omitted when there is only one model output in the progression. However, this " f"progression stores {n_model_outputs} model outputs. So, please supply model_output.") else: model_output = 0 # Create figure if necessary. if ax is None: ax = plt.figure(figsize=figsize).gca() # Set title, xlabel, and ylabel if applicable. if title is not None: ax.set_title(title) if xlabel is not None: ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) if color_cycle is None: color_cycle = _DEFAULT_COLOR_CYCLE for (om, om_cnclp), base_color in zip(cnclp.items(), cycle(color_cycle)): color_hue = rgb_to_hsv(to_rgb(base_color))[0] for (frag, frag_cnclp), color_value in zip(om_cnclp.items(), np.linspace(1, 0.7, len(om_cnclp))): if model_output is not None: frag_cnclp = frag_cnclp[model_output] if not np.all(np.isnan(frag_cnclp[:, 1])): third_col_max = int(frag_cnclp[0, 2]) max_sat_color = hsv_to_rgb([color_hue, 1.0, color_value]) min_sat_color = hsv_to_rgb([color_hue, 0.2, color_value]) # Use linspace in the wrong order and then reverse the result, such that when # 'third_col_max' is 1, the cmap contains the max saturation and not the # min saturation color as its only color. cmap = ListedColormap(np.linspace(max_sat_color, min_sat_color, third_col_max)[::-1]) segments = list(zip(frag_cnclp[:-1, :2], frag_cnclp[1:, :2])) lc = LineCollection(segments, cmap=cmap, norm=plt.Normalize(1, third_col_max), label=f"{type(om).__name__[0]}~{frag}", color=max_sat_color, zorder=1) lc.set_array(frag_cnclp[1:, 2]) ax.add_collection(lc) # Necessary because 'ax.add_collection()' doesn't adjust xlim and ylim automatically. ax.autoscale_view() # Draw the diagonal dotted line. diag = [max(ax.get_xlim()[0], ax.get_ylim()[0]), min(ax.get_xlim()[1], ax.get_ylim()[1])] ax.plot(diag, diag, ls="dotted", color="gray", zorder=0) if legend: ax.legend(loc="upper left", bbox_to_anchor=(1.01, 0, 1, 1))