@@ -100,14 +100,20 @@ def canny_edges(
100100 roi_mask : np .ndarray | None = None ,
101101 preset : str | None = None ,
102102 pixel_size_nm : float | None = None ,
103+ pixel_size_x_nm : float | None = None ,
104+ pixel_size_y_nm : float | None = None ,
103105 source_channel : str | None = None ,
104106) -> EdgeDetectionResult :
105107 """Detect edges with the Canny algorithm (``skimage.feature.canny``).
106108
107109 Parameters
108110 ----------
109111 sigma:
110- Gaussian smoothing width in pixels.
112+ Gaussian smoothing width **in pixels**. ``skimage.feature.canny`` only
113+ accepts a scalar sigma, so the smoothing is isotropic *in pixel space*.
114+ On anisotropic scans (``pixel_size_x_nm != pixel_size_y_nm``) it is
115+ therefore not isotropic in physical space; the recorded ``sigma_x_nm`` /
116+ ``sigma_y_nm`` express the per-axis physical extent for provenance.
111117 threshold_mode:
112118 ``"percentile"`` (default) interprets *low*/*high* as percentiles
113119 (0–100) of the gradient magnitude — robust across STM channels whose
@@ -120,6 +126,9 @@ def canny_edges(
120126 preset:
121127 Name in :data:`CANNY_PRESETS`; when given it overrides *sigma*/*low*/
122128 *high*.
129+ pixel_size_x_nm, pixel_size_y_nm:
130+ Optional physical pixel spacings, recorded for provenance only (the
131+ smoothing remains pixel-space). Fall back to *pixel_size_nm*.
123132 """
124133 from skimage .feature import canny as _canny
125134
@@ -174,6 +183,13 @@ def canny_edges(
174183 "preset" : preset ,
175184 "source_channel" : source_channel ,
176185 }
186+ # Per-axis physical extent of the (pixel-space) Gaussian, for provenance.
187+ dx = pixel_size_x_nm or pixel_size_nm
188+ dy = pixel_size_y_nm or pixel_size_nm
189+ if dx :
190+ params ["sigma_x_nm" ] = float (sigma ) * float (dx )
191+ if dy :
192+ params ["sigma_y_nm" ] = float (sigma ) * float (dy )
177193 if pixel_size_nm is not None :
178194 params ["sigma_nm" ] = float (sigma ) * float (pixel_size_nm )
179195
@@ -270,6 +286,9 @@ def gradient_filter(
270286 if roi is not None :
271287 chosen = np .where (roi , chosen , 0.0 )
272288 magnitude = np .where (roi , magnitude , 0.0 )
289+ # Keep the returned orientation field ROI-bounded too, so downstream
290+ # consumers never see out-of-ROI gradient directions.
291+ orientation = np .where (roi , orientation , 0.0 )
273292
274293 if normalize and output != "orientation" :
275294 peak = float (np .nanmax (np .abs (chosen ))) if chosen .size else 0.0
0 commit comments