class MolmoActVisualizer:
"""Visualization utilities for MolmoAct outputs"""
def __init__(self, figsize: Tuple[int, int] = (12, 8)):
self.figsize = figsize
self.colours = plt.cm.viridis(np.linspace(0, 1, 10))
def plot_trace(
self,
picture: Picture.Picture,
hint: Listing[List[int]],
title: str = "Visible Reasoning Hint",
save_path: Non-obligatory[str] = None
) -> None:
"""Plot visible hint overlaid on picture"""
fig, ax = plt.subplots(figsize=self.figsize)
img_array = np.array(picture)
ax.imshow(img_array)
if hint and len(hint) > 0:
h, w = img_array.form[:2]
trace_array = np.array(hint)
x_coords = trace_array[:, 0] * w / 256
y_coords = trace_array[:, 1] * h / 256
ax.plot(x_coords, y_coords, 'w-', linewidth=2, alpha=0.7)
ax.plot(x_coords, y_coords, 'c-', linewidth=1, alpha=0.9)
for i, (x, y) in enumerate(zip(x_coords, y_coords)):
color_idx = int(i * 9 / max(len(x_coords) - 1, 1))
ax.scatter(x, y, c=[self.colors[color_idx]], s=100,
edgecolors="white", linewidths=2, zorder=5)
ax.annotate(f'{i+1}', (x, y), textcoords="offset factors",
xytext=(5, 5), fontsize=10, shade="white",
fontweight="daring")
ax.scatter(x_coords[0], y_coords[0], c="lime", s=200,
marker="o", edgecolors="white", linewidths=3,
zorder=6, label="Begin")
ax.scatter(x_coords[-1], y_coords[-1], c="pink", s=200,
marker="X", edgecolors="white", linewidths=3,
zorder=6, label="Finish")
ax.set_title(title, fontsize=14, fontweight="daring")
ax.axis('off')
ax.legend(loc="higher proper")
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150, bbox_inches="tight")
print(f"đź’ľ Saved visualization to {save_path}")
plt.present()
def plot_action(
self,
motion: Listing[float],
action_labels: Non-obligatory[List[str]] = None,
title: str = "Predicted Robotic Motion",
save_path: Non-obligatory[str] = None
) -> None:
"""Plot motion values as a bar chart"""
if action_labels is None:
action_labels = [
'Δx (forward)', 'Δy (left)', 'Δz (up)',
'Rx (roll)', 'Ry (pitch)', 'Rz (yaw)',
'Gripper'
]
fig, ax = plt.subplots(figsize=(10, 5))
colours = ['#3498db', '#3498db', '#3498db',
'#e74c3c', '#e74c3c', '#e74c3c',
'#2ecc71']
x = np.arange(len(motion))
bars = ax.bar(x, motion, shade=colours, edgecolor="white", linewidth=1.5)
for bar, val in zip(bars, motion):
top = bar.get_height()
ax.annotate(f'{val:.3f}',
xy=(bar.get_x() + bar.get_width() / 2, top),
xytext=(0, 3 if top >= 0 else -12),
textcoords="offset factors",
ha="middle", va="backside" if top >= 0 else 'high',
fontsize=9, fontweight="daring")
ax.set_xticks(x)
ax.set_xticklabels(action_labels, rotation=45, ha="proper")
ax.set_ylabel('Worth', fontsize=12)
ax.set_title(title, fontsize=14, fontweight="daring")
ax.axhline(y=0, shade="grey", linestyle="--", alpha=0.5)
ax.grid(axis="y", alpha=0.3)
from matplotlib.patches import Patch
legend_elements = [
Patch(facecolor="#3498db", label="Position"),
Patch(facecolor="#e74c3c", label="Rotation"),
Patch(facecolor="#2ecc71", label="Gripper")
]
ax.legend(handles=legend_elements, loc="higher proper")
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150, bbox_inches="tight")
plt.present()
