Download applications/avoiding/avoiding_baselines/DiRecT/utils/rendering.py from GlobalMotionPlanning/GlobMP: direct link, hf CLI and curl.
- Browser
- Download file 19.4 kB
-
https://huggingface.co/GlobalMotionPlanning/GlobMP/resolve/main/applications/avoiding/avoiding_baselines/DiRecT/utils/rendering.py
- Command line
-
hf download hf://GlobalMotionPlanning/GlobMP/applications/avoiding/avoiding_baselines/DiRecT/utils/rendering.py
-
curl -L -o rendering.py https://huggingface.co/GlobalMotionPlanning/GlobMP/resolve/main/applications/avoiding/avoiding_baselines/DiRecT/utils/rendering.py
19.4 kB
| import numpy as np | |
| import matplotlib.pyplot as plt | |
| import matplotlib.patches as patches | |
| from .arrays import to_np | |
| from .video import save_video | |
| from obstacles import PILLARS, NOVEL_CIRCULAR, NOVEL_PLANAR, CONSTRAINT_SETS | |
| def save_single_trajectory_image( | |
| trajectory, filepath, constraint="", obstacle_margin=0.0, draw_obstacle_margin=False | |
| ): | |
| plotter = AvoidingTrajectoryPlotter( | |
| constraint=constraint, | |
| obstacle_margin=obstacle_margin, | |
| draw_obstacle_margin=draw_obstacle_margin, | |
| ) | |
| fig, ax = plotter.setup_figure() | |
| plotter.plot_single_trajectory(trajectory, ax, style="multiple_real") | |
| plotter.add_environment_elements(ax) | |
| plotter.apply_legend(ax) | |
| plotter.save_figure(fig, filepath) | |
| def save_trajectory_sequence( | |
| trajectories, | |
| base_path, | |
| folder_name, | |
| constraint="", | |
| obstacle_margin=0.0, | |
| draw_obstacle_margin=False, | |
| ): | |
| import os | |
| img_path = os.path.join(base_path, folder_name) | |
| os.makedirs(img_path, exist_ok=True) | |
| for step_i, trajectory in enumerate(trajectories): | |
| imgpath = os.path.join(img_path, f"{step_i}.png") | |
| save_single_trajectory_image( | |
| trajectory, imgpath, constraint, obstacle_margin, draw_obstacle_margin | |
| ) | |
| def save_prediction_overlay_plot( | |
| predicted_trajectories, | |
| real_trajectory, | |
| filepath, | |
| title=None, | |
| constraint="", | |
| obstacle_margin=0.0, | |
| draw_obstacle_margin=False, | |
| ): | |
| plotter = AvoidingTrajectoryPlotter( | |
| constraint=constraint, | |
| obstacle_margin=obstacle_margin, | |
| draw_obstacle_margin=draw_obstacle_margin, | |
| ) | |
| fig, ax = plotter.plot_overlaid_predictions_and_real( | |
| predicted_trajectories, | |
| real_trajectory, | |
| title=title, | |
| ) | |
| plotter.save_figure(fig, filepath) | |
| def plot2img(fig, remove_margins=True): | |
| from matplotlib.backends.backend_agg import FigureCanvasAgg | |
| if remove_margins: | |
| fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0) | |
| canvas = FigureCanvasAgg(fig) | |
| canvas.draw() | |
| img_as_string, (width, height) = canvas.print_to_buffer() | |
| return np.fromstring(img_as_string, dtype="uint8").reshape((height, width, 4)) | |
| class AvoidingRenderer: | |
| def __init__( | |
| self, | |
| env_name, | |
| env, | |
| constraint="", | |
| obstacle_margin=0.0, | |
| draw_obstacle_margin=False, | |
| ): | |
| self.env_name = env_name | |
| self.env = env | |
| self.constraint = constraint | |
| self.obstacle_margin = obstacle_margin | |
| self.draw_obstacle_margin = draw_obstacle_margin | |
| def renders(self, observations): | |
| plotter = AvoidingTrajectoryPlotter( | |
| constraint=self.constraint, | |
| obstacle_margin=self.obstacle_margin, | |
| draw_obstacle_margin=self.draw_obstacle_margin, | |
| ) | |
| fig, ax = plotter.setup_figure() | |
| plotter.plot_single_trajectory(observations, ax, style="multiple_real") | |
| plotter.add_environment_elements(ax) | |
| plotter.apply_legend(ax) | |
| img = plot2img(fig, remove_margins=False) | |
| plt.close(fig) | |
| return img | |
| def render_diffusion(self, savepath, diffusion_path, **video_kwargs): | |
| diffusion_path = to_np(diffusion_path) | |
| n_diffusion_steps = diffusion_path.shape[0] | |
| frame = [] | |
| for t in range(n_diffusion_steps): | |
| print(f"[ utils/renderer ] Diffusion: {t} / {n_diffusion_steps}") | |
| img = self.renders(diffusion_path[t]) | |
| frame.append(img) | |
| frame = np.stack(frame, axis=0) | |
| save_video(savepath, frame, **video_kwargs) | |
| class AvoidingTrajectoryPlotter: | |
| def __init__(self, constraint="", obstacle_margin=0.0, draw_obstacle_margin=False): | |
| self.constraint = constraint | |
| self.obstacle_margin = obstacle_margin | |
| self.draw_obstacle_margin = draw_obstacle_margin | |
| self.ax_limits = [[0.2, 0.8], [-0.3, 0.4]] | |
| self.figsize = (9, 9) | |
| self.fontsize_label = 16 | |
| self.fontsize_ticks = 14 | |
| self.fontsize_legend = 16 | |
| self._legend_flags = { | |
| "target": False, | |
| "obstacle": False, | |
| "start": False, | |
| "end": False, | |
| } | |
| def setup_figure(self): | |
| fig, ax = plt.subplots(figsize=self.figsize) | |
| ax.set_xlim(self.ax_limits[0]) | |
| ax.set_ylim(self.ax_limits[1]) | |
| ax.set_facecolor([1, 1, 0.9]) | |
| ax.set_aspect("equal", adjustable="box") | |
| fig.subplots_adjust(left=0.06, right=0.985, bottom=0.06, top=0.985) | |
| ax.xaxis.labelpad = 2 | |
| ax.yaxis.labelpad = 2 | |
| ax.tick_params(axis="both", which="major", pad=1, labelsize=self.fontsize_ticks) | |
| ax.set_xlabel("X Position", fontsize=self.fontsize_label) | |
| ax.set_ylabel("Y Position", fontsize=self.fontsize_label) | |
| ax.grid(True, alpha=0.3) | |
| return fig, ax | |
| def add_environment_elements(self, ax): | |
| target_color = [0.4, 1, 0.4] | |
| x_min, x_max = self.ax_limits[0] | |
| y_min_target = 0.35 | |
| y_max = self.ax_limits[1][1] | |
| if y_max > y_min_target: | |
| target_patch = patches.Rectangle( | |
| (x_min, y_min_target), | |
| x_max - x_min, | |
| y_max - y_min_target, | |
| facecolor=target_color, | |
| alpha=0.35, | |
| label="Target" if not self._legend_flags["target"] else "", | |
| zorder=0.5, | |
| ) | |
| ax.add_patch(target_patch) | |
| self._legend_flags["target"] = True | |
| polytopic_constraints = self._get_polytopic_constraints(ax) | |
| self._add_polytopic_constraints(ax, polytopic_constraints) | |
| self._add_target_circles(ax) | |
| def _get_polytopic_constraints(self, ax): | |
| if self.constraint == "": | |
| return [] | |
| elif self.constraint not in CONSTRAINT_SETS: | |
| raise ValueError(f"Unsupported constraint: {self.constraint}") | |
| for obs in NOVEL_CIRCULAR: | |
| center = list(obs.center) | |
| ax.add_patch( | |
| patches.Circle( | |
| center, | |
| obs.radius, | |
| color="b", | |
| alpha=0.3, | |
| label="Obstacle" if not self._legend_flags["obstacle"] else "", | |
| ) | |
| ) | |
| self._legend_flags["obstacle"] = True | |
| if self.obstacle_margin > 0 and self.draw_obstacle_margin: | |
| self._add_margin_ring(ax, center, obs.radius, self.obstacle_margin, "b") | |
| return [ | |
| [list(obs.point_a), list(obs.point_b), "below"] | |
| for obs in NOVEL_PLANAR | |
| ] | |
| def _add_polytopic_constraints(self, ax, polytopic_constraints): | |
| if self.constraint == "": | |
| return | |
| for i, constraint in enumerate(polytopic_constraints): | |
| mat = np.vstack((constraint[:2], np.zeros(2))) | |
| slope = (constraint[1][1] - constraint[0][1]) / ( | |
| constraint[1][0] - constraint[0][0] | |
| ) | |
| if slope > 0 and constraint[2] == "below": | |
| mat[2] = np.array([self.ax_limits[0][0], self.ax_limits[1][1]]) | |
| elif slope < 0 and constraint[2] == "below": | |
| mat[2] = np.array([self.ax_limits[0][1], self.ax_limits[1][1]]) | |
| elif slope > 0 and constraint[2] == "above": | |
| mat[2] = np.array([self.ax_limits[0][1], self.ax_limits[1][0]]) | |
| elif slope < 0 and constraint[2] == "above": | |
| mat[2] = np.array([self.ax_limits[0][0], self.ax_limits[1][0]]) | |
| clipped = self._clip_polygon_below_y(mat, 0.35) | |
| if len(clipped) >= 3: | |
| ax.add_patch( | |
| patches.Polygon( | |
| np.array(clipped), | |
| color="b", | |
| alpha=0.2, | |
| label="Obstacle" if not self._legend_flags["obstacle"] else "", | |
| ) | |
| ) | |
| self._legend_flags["obstacle"] = True | |
| if self.obstacle_margin > 0 and self.draw_obstacle_margin: | |
| self._add_margin_parallelogram(ax, constraint, "b") | |
| def _add_target_circles(self, ax): | |
| for idx, pillar in enumerate(PILLARS): | |
| center = list(pillar.center) | |
| radius = pillar.radius | |
| ax.add_patch( | |
| patches.Circle( | |
| center, | |
| radius, | |
| color="r", | |
| alpha=0.7, | |
| label="Obstacle" if not self._legend_flags["obstacle"] else "", | |
| ) | |
| ) | |
| self._legend_flags["obstacle"] = True | |
| if self.obstacle_margin > 0 and self.draw_obstacle_margin: | |
| if not self._is_pillar_covered_by_obstacle(center): | |
| self._add_margin_ring(ax, center, radius, self.obstacle_margin, "r") | |
| def _is_pillar_covered_by_obstacle(self, pillar_center): | |
| if self.constraint not in CONSTRAINT_SETS: | |
| return False | |
| for obs in NOVEL_CIRCULAR: | |
| distance = np.sqrt( | |
| (pillar_center[0] - obs.center[0]) ** 2 | |
| + (pillar_center[1] - obs.center[1]) ** 2 | |
| ) | |
| if distance < obs.radius: | |
| return True | |
| return False | |
| def _add_margin_ring(self, ax, center, inner_radius, margin, color): | |
| outer_radius = inner_radius + margin | |
| outer_circle = patches.Circle( | |
| center, outer_radius, color=color, alpha=0.15, zorder=1 | |
| ) | |
| ax.add_patch(outer_circle) | |
| border_circle = patches.Circle( | |
| center, | |
| outer_radius, | |
| fill=False, | |
| edgecolor=color, | |
| alpha=0.7, | |
| linewidth=1.5, | |
| linestyle="--", | |
| zorder=3, | |
| ) | |
| ax.add_patch(border_circle) | |
| def _add_margin_parallelogram(self, ax, constraint, color): | |
| p1, p2 = constraint[:2] | |
| slope = (p2[1] - p1[1]) / (p2[0] - p1[0]) | |
| intercept = p2[1] - slope * p2[0] | |
| normal_x = -slope / np.sqrt(1 + slope**2) | |
| normal_y = 1 / np.sqrt(1 + slope**2) | |
| if constraint[2] == "below": | |
| margin_offset_x = -normal_x * self.obstacle_margin | |
| margin_offset_y = -normal_y * self.obstacle_margin | |
| else: | |
| margin_offset_x = normal_x * self.obstacle_margin | |
| margin_offset_y = normal_y * self.obstacle_margin | |
| original_mat = np.vstack((constraint[:2], np.zeros(2))) | |
| if slope > 0 and constraint[2] == "below": | |
| original_mat[2] = np.array([self.ax_limits[0][0], self.ax_limits[1][1]]) | |
| elif slope < 0 and constraint[2] == "below": | |
| original_mat[2] = np.array([self.ax_limits[0][1], self.ax_limits[1][1]]) | |
| elif slope > 0 and constraint[2] == "above": | |
| original_mat[2] = np.array([self.ax_limits[0][1], self.ax_limits[1][0]]) | |
| elif slope < 0 and constraint[2] == "above": | |
| original_mat[2] = np.array([self.ax_limits[0][0], self.ax_limits[1][0]]) | |
| margin_mat = original_mat.copy() | |
| margin_mat[0] += [margin_offset_x, margin_offset_y] | |
| margin_mat[1] += [margin_offset_x, margin_offset_y] | |
| margin_vertices = np.vstack([original_mat[:2], margin_mat[:2][::-1]]) | |
| ax.add_patch( | |
| patches.Polygon( | |
| margin_vertices, | |
| color=color, | |
| alpha=0.1, | |
| ) | |
| ) | |
| def _clip_polygon_below_y(self, vertices, y_max): | |
| verts = [np.array(v, dtype=float).tolist() for v in vertices] | |
| if len(verts) == 0: | |
| return [] | |
| output = [] | |
| prev = verts[-1] | |
| prev_inside = prev[1] <= y_max | |
| for curr in verts: | |
| curr_inside = curr[1] <= y_max | |
| if curr_inside: | |
| if not prev_inside: | |
| denom = curr[1] - prev[1] | |
| if denom != 0: | |
| t = (y_max - prev[1]) / denom | |
| x_int = prev[0] + t * (curr[0] - prev[0]) | |
| output.append([x_int, y_max]) | |
| output.append(curr) | |
| else: | |
| if prev_inside: | |
| denom = curr[1] - prev[1] | |
| if denom != 0: | |
| t = (y_max - prev[1]) / denom | |
| x_int = prev[0] + t * (curr[0] - prev[0]) | |
| output.append([x_int, y_max]) | |
| prev, prev_inside = curr, curr_inside | |
| return output | |
| def plot_single_trajectory( | |
| self, trajectory, ax, style="actual", label_prefix="", show_labels=True | |
| ): | |
| x_coords = trajectory[:, 2] | |
| y_coords = trajectory[:, 3] | |
| if style == "actual": | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| "k-", | |
| linewidth=3, | |
| label=f"{label_prefix}Actual Trajectory" if show_labels else "", | |
| ) | |
| ax.plot( | |
| x_coords[0], | |
| y_coords[0], | |
| "go", | |
| markersize=10, | |
| markeredgecolor="darkgreen", | |
| markeredgewidth=1, | |
| label="Start" if not self._legend_flags["start"] else "", | |
| ) | |
| self._legend_flags["start"] = True | |
| ax.plot( | |
| x_coords[-1], | |
| y_coords[-1], | |
| "bo", | |
| markersize=10, | |
| markeredgecolor="navy", | |
| markeredgewidth=1, | |
| label="End" if not self._legend_flags["end"] else "", | |
| ) | |
| self._legend_flags["end"] = True | |
| if len(x_coords) > 2: | |
| ax.plot( | |
| x_coords[1:-1], | |
| y_coords[1:-1], | |
| "ko", | |
| markersize=3, | |
| alpha=0.7, | |
| ) | |
| elif style == "predicted": | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| "m-", | |
| linewidth=1.5, | |
| alpha=0.4, | |
| label=f"{label_prefix}Predicted Trajectories" if show_labels else "", | |
| ) | |
| ax.plot( | |
| x_coords[0], | |
| y_coords[0], | |
| "ms", | |
| markersize=5, | |
| alpha=0.5, | |
| ) | |
| ax.plot( | |
| x_coords[-1], | |
| y_coords[-1], | |
| "m^", | |
| markersize=5, | |
| alpha=0.5, | |
| ) | |
| if len(x_coords) > 2: | |
| ax.plot( | |
| x_coords[1:-1], | |
| y_coords[1:-1], | |
| "mx", | |
| markersize=3, | |
| alpha=0.4, | |
| ) | |
| elif style == "multiple_real": | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| "k-", | |
| linewidth=2, | |
| label=f"{label_prefix}Trajectory" if show_labels else "", | |
| ) | |
| ax.plot( | |
| x_coords[0], | |
| y_coords[0], | |
| "go", | |
| markersize=8, | |
| label="Start" if not self._legend_flags["start"] else "", | |
| ) | |
| self._legend_flags["start"] = True | |
| ax.plot( | |
| x_coords[-1], | |
| y_coords[-1], | |
| "bo", | |
| markersize=8, | |
| label="End" if not self._legend_flags["end"] else "", | |
| ) | |
| self._legend_flags["end"] = True | |
| if len(x_coords) > 2: | |
| ax.plot( | |
| x_coords[1:-1], | |
| y_coords[1:-1], | |
| "co", | |
| markersize=4, | |
| alpha=0.6, | |
| ) | |
| def plot_overlaid_predictions_and_real( | |
| self, | |
| predicted_trajectories, | |
| real_trajectory, | |
| title=None, | |
| ): | |
| fig, ax = self.setup_figure() | |
| for i, trajectory in enumerate(predicted_trajectories): | |
| self.plot_single_trajectory( | |
| trajectory, ax, style="predicted", show_labels=(i == 0) | |
| ) | |
| self.plot_single_trajectory(real_trajectory, ax, style="actual") | |
| if title: | |
| ax.set_title(title, fontsize=self.fontsize_label) | |
| self.add_environment_elements(ax) | |
| self.apply_legend(ax) | |
| return fig, ax | |
| def _trajectory_violates(self, trajectory): | |
| """Check if any point in the trajectory violates constraints.""" | |
| if self.constraint == "": | |
| return False | |
| from obstacles import check_violation | |
| for t in range(len(trajectory)): | |
| if check_violation(trajectory[t], self.constraint) > 0: | |
| return True | |
| return False | |
| def plot_multiple_real_trajectories(self, trajectories, title=None): | |
| fig, ax = self.setup_figure() | |
| for i, trajectory in enumerate(trajectories): | |
| x_coords = trajectory[:, 2] | |
| y_coords = trajectory[:, 3] | |
| violated = self._trajectory_violates(trajectory) | |
| color = "gray" if violated else "k" | |
| ax.plot( | |
| x_coords, | |
| y_coords, | |
| color=color, | |
| linestyle="-", | |
| linewidth=2, | |
| alpha=0.7, | |
| label="Trajectory" if i == 0 else "", | |
| ) | |
| ax.plot( | |
| x_coords[0], | |
| y_coords[0], | |
| "go", | |
| markersize=8, | |
| label="Start" if not self._legend_flags["start"] else "", | |
| ) | |
| self._legend_flags["start"] = True | |
| ax.plot( | |
| x_coords[-1], | |
| y_coords[-1], | |
| "bo", | |
| markersize=8, | |
| label="End" if not self._legend_flags["end"] else "", | |
| ) | |
| self._legend_flags["end"] = True | |
| if len(x_coords) > 2: | |
| ax.plot(x_coords[1:-1], y_coords[1:-1], "co", markersize=4, alpha=0.6) | |
| self.add_environment_elements(ax) | |
| self.apply_legend(ax) | |
| return fig, ax | |
| def save_figure(self, fig, filepath, dpi=150, bbox_inches=None): | |
| fig.savefig(filepath, dpi=dpi, bbox_inches=bbox_inches) | |
| plt.close(fig) | |
| print(f"Saved trajectory plot to: {filepath}") | |
| def apply_legend(self, ax): | |
| from matplotlib.legend_handler import HandlerTuple | |
| handles, labels = ax.get_legend_handles_labels() | |
| new_handles = [] | |
| new_labels = [] | |
| seen = set() | |
| for h, l in zip(handles, labels): | |
| if not l: | |
| continue | |
| if l.lower() == "obstacle": | |
| continue | |
| if l not in seen: | |
| seen.add(l) | |
| new_handles.append(h) | |
| new_labels.append(l) | |
| purple_proxy = patches.Rectangle((0, 0), 1, 1, facecolor="b", alpha=0.2) | |
| red_proxy = patches.Circle((0.5, 0.5), 0.5, facecolor="r", alpha=0.7) | |
| composite = (purple_proxy, red_proxy) | |
| new_handles.append(composite) | |
| new_labels.append("Obstacle") | |
| ax.legend( | |
| new_handles, | |
| new_labels, | |
| handler_map={tuple: HandlerTuple(ndivide=None)}, | |
| loc="lower left", | |
| fontsize=self.fontsize_legend, | |
| ) | |