3838LINESTYLES = ["-" , "--" , "-." , ":" ]
3939MASK_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
4247def _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+
56190def _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