Skip to content

Commit 4635433

Browse files
committed
Enhance Plotter class with automatic extraction and display of geometry and table results from metadata; improve visualization capabilities
1 parent b77324d commit 4635433

File tree

1 file changed

+182
-6
lines changed

1 file changed

+182
-6
lines changed

datalab_kernel/plotter.py

Lines changed: 182 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
LINESTYLES = ["-", "--", "-.", ":"]
3939
MASK_OPACITY = 0.35 # Opacity for mask overlay
4040

41+
# Metadata prefix for geometry results (consistent with DataLab's GeometryAdapter)
42+
GEOMETRY_META_PREFIX = "Geometry_"
43+
# Metadata prefix for table results (consistent with DataLab's TableAdapter)
44+
TABLE_META_PREFIX = "Table_"
45+
4146

4247
def _get_next_style(index: int) -> tuple[str, str]:
4348
"""Get color and linestyle for the next plot item.
@@ -53,6 +58,135 @@ def _get_next_style(index: int) -> tuple[str, str]:
5358
return color, linestyle
5459

5560

61+
def _extract_geometry_results_from_metadata(obj) -> list:
62+
"""Extract GeometryResult objects from object metadata.
63+
64+
DataLab stores geometry results in object metadata with keys starting with
65+
'Geometry_'. This function extracts and reconstructs those GeometryResult
66+
objects for visualization.
67+
68+
Args:
69+
obj: SignalObj or ImageObj with potential geometry results in metadata
70+
71+
Returns:
72+
List of GeometryResult objects extracted from metadata
73+
"""
74+
results = []
75+
if not hasattr(obj, "metadata") or obj.metadata is None:
76+
return results
77+
78+
# Delayed import
79+
# pylint: disable=import-outside-toplevel
80+
from sigima.objects import GeometryResult
81+
82+
for key, value in obj.metadata.items():
83+
if key.startswith(GEOMETRY_META_PREFIX) and isinstance(value, dict):
84+
try:
85+
geometry = GeometryResult.from_dict(value)
86+
results.append(geometry)
87+
except (ValueError, TypeError, KeyError):
88+
# Skip invalid entries
89+
pass
90+
91+
return results
92+
93+
94+
def _extract_table_results_from_metadata(obj) -> list:
95+
"""Extract TableResult objects from object metadata.
96+
97+
DataLab stores table results in object metadata with keys starting with
98+
'Table_'. This function extracts and reconstructs those TableResult
99+
objects for visualization.
100+
101+
Args:
102+
obj: SignalObj or ImageObj with potential table results in metadata
103+
104+
Returns:
105+
List of TableResult objects extracted from metadata
106+
"""
107+
results = []
108+
if not hasattr(obj, "metadata") or obj.metadata is None:
109+
return results
110+
111+
# Delayed import
112+
# pylint: disable=import-outside-toplevel
113+
from sigima.objects import TableResult
114+
115+
for key, value in obj.metadata.items():
116+
if key.startswith(TABLE_META_PREFIX) and isinstance(value, dict):
117+
try:
118+
table = TableResult.from_dict(value)
119+
results.append(table)
120+
except (ValueError, TypeError, KeyError):
121+
# Skip invalid entries
122+
pass
123+
124+
return results
125+
126+
127+
def _add_table_results_to_axes(ax: Axes, table_results: list) -> None:
128+
"""Add table results as text annotation to matplotlib axes.
129+
130+
Formats TableResult objects as a text box displayed in the upper-left
131+
corner of the axes, similar to DataLab's result label display.
132+
133+
Args:
134+
ax: Matplotlib axes object
135+
table_results: List of TableResult objects to display
136+
"""
137+
if not table_results:
138+
return
139+
140+
# Build text content from all table results
141+
text_lines = []
142+
for table in table_results:
143+
# Add table title as header
144+
text_lines.append(f"{table.title}:")
145+
146+
# Get headers and data
147+
headers = list(table.headers)
148+
data = table.data
149+
150+
# Format each row (typically just one row for statistics)
151+
for row in data:
152+
for header, value in zip(headers, row):
153+
# Format numeric values
154+
if isinstance(value, float):
155+
if abs(value) < 0.001 or abs(value) >= 10000:
156+
formatted = f"{value:.3g}"
157+
else:
158+
formatted = f"{value:.3f}"
159+
else:
160+
formatted = str(value)
161+
text_lines.append(f" {header}: {formatted}")
162+
163+
text_lines.append("") # Empty line between tables
164+
165+
# Remove trailing empty line
166+
if text_lines and text_lines[-1] == "":
167+
text_lines.pop()
168+
169+
text = "\n".join(text_lines)
170+
171+
# Add text box annotation in upper-left corner
172+
ax.text(
173+
0.02,
174+
0.98,
175+
text,
176+
transform=ax.transAxes,
177+
fontsize=9,
178+
verticalalignment="top",
179+
horizontalalignment="left",
180+
fontfamily="monospace",
181+
bbox={
182+
"boxstyle": "round,pad=0.5",
183+
"facecolor": "white",
184+
"edgecolor": "gray",
185+
"alpha": 0.85,
186+
},
187+
)
188+
189+
56190
def _add_single_roi_to_axes(ax: Axes, roi, obj=None) -> None:
57191
"""Add single ROI overlay to matplotlib axes.
58192
@@ -541,6 +675,15 @@ def _render_signal(self, ax: Axes) -> None:
541675
for roi in obj.roi:
542676
_add_single_roi_to_axes(ax, roi, obj)
543677

678+
# Auto-extract and display geometry results from object metadata
679+
metadata_results = _extract_geometry_results_from_metadata(obj)
680+
for result in metadata_results:
681+
_add_geometry_to_axes(ax, result)
682+
683+
# Auto-extract and display table results (statistics) from metadata
684+
table_results = _extract_table_results_from_metadata(obj)
685+
_add_table_results_to_axes(ax, table_results)
686+
544687
def _render_image(self, ax: Axes, fig) -> None:
545688
"""Render image data to axes.
546689
@@ -592,15 +735,26 @@ def _render_image(self, ax: Axes, fig) -> None:
592735
for roi in obj.roi:
593736
_add_single_roi_to_axes(ax, roi, obj)
594737

595-
# Overlay geometry results
738+
# Overlay geometry results from explicit parameter or from metadata
739+
results_to_display = []
596740
if self._results is not None:
597741
result_list = (
598742
self._results
599743
if isinstance(self._results, (list, tuple))
600744
else [self._results]
601745
)
602-
for result in result_list:
603-
_add_geometry_to_axes(ax, result)
746+
results_to_display.extend(result_list)
747+
748+
# Auto-extract geometry results from object metadata
749+
metadata_results = _extract_geometry_results_from_metadata(obj)
750+
results_to_display.extend(metadata_results)
751+
752+
for result in results_to_display:
753+
_add_geometry_to_axes(ax, result)
754+
755+
# Auto-extract and display table results (statistics) from metadata
756+
table_results = _extract_table_results_from_metadata(obj)
757+
_add_table_results_to_axes(ax, table_results)
604758

605759
def __repr__(self) -> str:
606760
"""Return string representation."""
@@ -720,6 +874,15 @@ def _render_to_png(self) -> bytes:
720874
else None,
721875
)
722876

877+
# Auto-extract and display geometry results from object metadata
878+
metadata_results = _extract_geometry_results_from_metadata(obj)
879+
for result in metadata_results:
880+
_add_geometry_to_axes(ax, result)
881+
882+
# Auto-extract and display table results (statistics) from metadata
883+
table_results = _extract_table_results_from_metadata(obj)
884+
_add_table_results_to_axes(ax, table_results)
885+
723886
elif isinstance(data_or_obj, tuple) and len(data_or_obj) == 2:
724887
# Tuple of (x, y) arrays
725888
xdata, ydata = data_or_obj
@@ -963,13 +1126,26 @@ def _render_to_png(self) -> bytes:
9631126
for roi in img.roi:
9641127
_add_single_roi_to_axes(ax, roi, img)
9651128

966-
# Overlay geometry results
1129+
# Collect geometry results: explicit + from metadata
1130+
results_to_display = []
9671131
if result is not None:
9681132
result_list_item = (
9691133
result if isinstance(result, (list, tuple)) else [result]
9701134
)
971-
for res in result_list_item:
972-
_add_geometry_to_axes(ax, res)
1135+
results_to_display.extend(result_list_item)
1136+
1137+
# Auto-extract geometry results from object metadata
1138+
if is_image_obj:
1139+
metadata_results = _extract_geometry_results_from_metadata(img)
1140+
results_to_display.extend(metadata_results)
1141+
1142+
for res in results_to_display:
1143+
_add_geometry_to_axes(ax, res)
1144+
1145+
# Auto-extract and display table results (statistics) from metadata
1146+
if is_image_obj:
1147+
table_results = _extract_table_results_from_metadata(img)
1148+
_add_table_results_to_axes(ax, table_results)
9731149

9741150
# Hide unused subplots
9751151
for idx in range(n_images, len(axes_flat)):

0 commit comments

Comments
 (0)