You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Find the offending file in the output. If processing halts, re-run analysis with topostats --core 1 process.
Describe the bug.
Include the configuration file.
Copy of the log-file from running with topostats --log-level debug <command>.
The exact command that failed. This is what you typed at the command line, including any options.
TopoStats version, this is reported by topostats --version
Operating System and Python Version
Describe the bug
When padding is too small, the mask smoothing at the start of disordered tracing trace_dna() causes the mask to touch the border of the image.
Original mask:
Smoothed mask:
This issue is solvable by simply increasing the grain_crop_padding to, say, 10.
I don't think we should spend time trying to prevent this occurring, rather I think we should make it clear in the logs when this error occurs that it can be solved by increasing the padding.
Here is the output currently when the error is thrown:
[Thu, 13 Feb 2025 09:58:54] [ERROR ] [topostats] [20221019_Ni_Topo_20ng_Shelterin_40nM_5min.0_00060] : Disordered tracing of grain 1 failed. Consider raising an issue on GitHub. Error:
Traceback (most recent call last):
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 367, in trace_image_disordered
disordered_trace_images: dict | None = disordered_trace_grain(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 654, in disordered_trace_grain
disorderedtrace.trace_dna()
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 127, in trace_dna
).get_skeleton()
^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 70, in get_skeleton
return self._get_skeletonize()
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 90, in _get_skeletonize
return self._skeletonize_topostats(image=self.image, mask=self.mask, height_bias=self.height_bias).astype(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 186, in _skeletonize_topostats
return topostatsSkeletonize(image, mask, height_bias).do_skeletonising()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 246, in do_skeletonising
self._do_skeletonising_iteration()
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 269, in _do_skeletonising_iteration
if self._delete_pixel_subit1(point):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 326, in _delete_pixel_subit1
self.p7, self.p8, self.p9, self.p6, self.p2, self.p5, self.p4, self.p3 = self.get_local_pixels_binary(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 546, in get_local_pixels_binary
return np.delete(local_pixels, 4)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/miniconda3/envs/topoly/lib/python3.11/site-packages/numpy/lib/function_base.py", line 5330, in delete
raise IndexError(
IndexError: index 4 is out of bounds for axis 0 with size 0
Proposed solution:
Add a check just after the smoothing step in trace_dna() and fail early:
Copy of the log-file from running with topostats --log-level debug <command>
[Thu, 13 Feb 2025 09:58:54] [ERROR ] [topostats] [20221019_Ni_Topo_20ng_Shelterin_40nM_5min.0_00060] : Disordered tracing of grain 1 failed. Consider raising an issue on GitHub. Error:
Traceback (most recent call last):
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 367, in trace_image_disordered
disordered_trace_images: dict | None = disordered_trace_grain(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 654, in disordered_trace_grain
disorderedtrace.trace_dna()
File "/Users/laura/TopoStats/topostats/tracing/disordered_tracing.py", line 127, in trace_dna
).get_skeleton()
^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 70, in get_skeleton
return self._get_skeletonize()
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 90, in _get_skeletonize
return self._skeletonize_topostats(image=self.image, mask=self.mask, height_bias=self.height_bias).astype(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 186, in _skeletonize_topostats
return topostatsSkeletonize(image, mask, height_bias).do_skeletonising()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 246, in do_skeletonising
self._do_skeletonising_iteration()
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 269, in _do_skeletonising_iteration
if self._delete_pixel_subit1(point):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 326, in _delete_pixel_subit1
self.p7, self.p8, self.p9, self.p6, self.p2, self.p5, self.p4, self.p3 = self.get_local_pixels_binary(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/TopoStats/topostats/tracing/skeletonize.py", line 546, in get_local_pixels_binary
return np.delete(local_pixels, 4)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/laura/miniconda3/envs/topoly/lib/python3.11/site-packages/numpy/lib/function_base.py", line 5330, in delete
raise IndexError(
IndexError: index 4 is out of bounds for axis 0 with size 0
Include the configuration file
# Config file generated 2025-02-11 19:02:51# # For more information on configuration and how to use it:# https://afm-spm.github.io/TopoStats/main/configuration.htmlbase_dir: ./data # Directory in which to search for data filesoutput_dir: ./output # Directory to output results tolog_level: info # Verbosity of output. Options: warning, error, info, debugcores: 1# Number of CPU cores to utilise for processing multiple files simultaneously.file_ext: .spm # File extension of the data files.loading:
channel: Height # Channel to pull data from in the data files.filter:
run: true # Options : true, falserow_alignment_quantile: 0.5# lower values may improve flattening of larger featuresthreshold_method: std_dev # Options : otsu, std_dev, absoluteotsu_threshold_multiplier: 1.0threshold_std_dev:
below: 10.0# Threshold for data below the image backgroundabove: 1.0# Threshold for data above the image backgroundthreshold_absolute:
below: -1.0# Threshold for data below the image backgroundabove: 1.0# Threshold for data above the image backgroundgaussian_size: 1.0121397464510862# Gaussian blur intensity in pxgaussian_mode: nearest # Mode for Gaussian blurring. Options : nearest, reflect, constant, mirror, wrap# Scar remvoal parameters. Be careful with editing these as making the algorithm too sensitive may# result in ruining legitimate data.remove_scars:
run: falseremoval_iterations: 2# Number of times to run scar removal.threshold_low: 0.250# lower values make scar removal more sensitivethreshold_high: 0.666# lower values make scar removal more sensitivemax_scar_width: 4# Maximum thickness of scars in pixels.min_scar_length: 16# Minimum length of scars in pixels.grains:
run: true # Options : true, false# Thresholding by heightgrain_crop_padding: 1# Padding to apply to grains. Needs to be at least 1, more padding may help with unets.threshold_method: absolute # Options : std_dev, otsu, absolute, unetotsu_threshold_multiplier: 1.0threshold_std_dev:
below: 10.0# Threshold for grains below the image backgroundabove: 1.0# Threshold for grains above the image backgroundthreshold_absolute:
below: -1.0# Threshold for grains below the image backgroundabove: 1.0# Threshold for grains above the image backgrounddirection: above # Options: above, below, both (defines whether to look for grains above or below thresholds or both)# Thresholding by areasmallest_grain_size_nm2: 50# Size in nm^2 of tiny grains/blobs (noise) to remove, must be > 0.0absolute_area_threshold:
above: [300, 30000] # above surface [Low, High] in nm^2 (also takes null)below: [null, null] # below surface [Low, High] in nm^2 (also takes null)remove_edge_intersecting_grains: true # Whether or not to remove grains that touch the image borderunet_config:
model_path: /Users/sylvi/topo_data/topo-test-unets/debugging-laura/new_shelterin_model.h5 # Path to a trained U-Net modelupper_norm_bound: 8.0# Upper bound for normalisation of input data. This should be slightly higher than the maximum desired / expected height of grains.lower_norm_bound: -2.0# Lower bound for normalisation of input data. This should be slightly lower than the minimum desired / expected height of the background.vetting:
class_conversion_size_thresholds: null # Class conversion size thresholds, list of tuples of 3 integers and 2 integers, ie list[tuple[tuple[int, int, int], tuple[int, int]]] eg [[[1, 2, 3], [5, 10]]] for each region of class 1 to convert to 2 if smaller than 5 nm^2 and to class 3 if larger than 10 nm^2.class_region_number_thresholds: null # Class region number thresholds, list of lists, ie [[class, low, high],] eg [[1, 2, 4], [2, 1, 1]] for class 1 to have 2-4 regions and class 2 to have 1 region. Can use None to not set an upper/lower bound.class_size_thresholds: null # Class size thresholds (nm^2), list of tuples of 3 integers, ie [[class, low, high],] eg [[1, 100, 1000], [2, 1000, None]] for class 1 to have 100-1000 nm^2 and class 2 to have 1000-any nm^2. Can use None to not set an upper/lower bound.nearby_conversion_classes_to_convert: null # Class conversion for nearby regions, list of tuples of two-integer tuples, eg [[[1, 2], [3, 4]]] to convert class 1 to 2 and 3 to 4 for small touching regionsclass_touching_threshold: 5# Number of dilation steps to use for detecting touching regionskeep_largest_labelled_regions_classes: [1] # Classes to keep the only largest regions for, list of integers eg [1, 2] to keep only the largest regions of class 1 and 2class_connection_point_thresholds: null # Class connection point thresholds, [[[class_1, class_2], [min, max]]] eg [[[1, 2], [1, 1]]] for class 1 to have 1 connection point with class 2grainstats:
run: true # Options : true, falseedge_detection_method: binary_erosion # Options: canny, binary erosion. Do not change this unless you are sure of what this will do.cropped_size: -1# Length (in nm) of square cropped images (can take -1 for grain-sized box)extract_height_profile: true # Extract height profiles along maximum feret of moleculesdisordered_tracing:
run: true # Options : true, falseclass_index: 1# The class index to trace. This is the class index of the grains.min_skeleton_size: 10# Minimum number of pixels in a skeleton for it to be retained.mask_smoothing_params:
gaussian_sigma: 2# Gaussian smoothing parameter 'sigma' in pixels.dilation_iterations: 2# Number of dilation iterations to use for grain smoothing.holearea_min_max: [0, null] # Range (min, max) of a hole area in nm to refill in the smoothed masks.skeletonisation_params:
method: topostats # Options : zhang | lee | thin | topostatsheight_bias: 0.6# Percentage of lowest pixels to remove each skeletonisation iteration. 1 equates to zhang.pruning_params:
method: topostats # Method to clean branches of the skeleton. Options : topostatsmax_length: 10.0# Maximum length in nm to remove a branch containing an endpoint.height_threshold: # The height to remove branches below.method_values: mid # The method to obtain a branch's height for pruning. Options : min | median | mid.method_outlier: mean_abs # The method to prune branches based on height. Options : abs | mean_abs | iqr.nodestats:
run: true # Options : true, falsenode_joining_length: 7.0# The distance in nanometres over which to join nearby crossing points.node_extend_dist: 14.0# The distance in nanometres over which to join nearby odd-branched nodes.branch_pairing_length: 20.0# The length in nanometres from the crossing point to pair and trace, obtaining FWHM's.pair_odd_branches: false # Whether to try and pair odd-branched nodes. Options: true and false.ordered_tracing:
run: trueordering_method: nodestats # The method of ordering the disordered traces.splining:
run: true # Options : true, falsemethod: "rolling_window"# Options : "spline", "rolling_window"rolling_window_size: 20.0e-9# size in nm of the rolling window.spline_step_size: 7.0e-9# The sampling rate of the spline in metres.spline_linear_smoothing: 5.0# The amount of smoothing to apply to linear features.spline_circular_smoothing: 5.0# The amount of smoothing to apply to circular features.spline_degree: 3# The polynomial degree of the spline.curvature:
run: true # Options : true, falsecolourmap_normalisation_bounds: [-0.5, 0.5] # Radians per nm to normalise the colourmap to.plotting:
run: true # Options : true, falsestyle: topostats.mplstyle # Options : topostats.mplstyle or path to a matplotlibrc params filesavefig_format: null # Options : null, png, svg or pdf. tif is also available although no metadata will be saved. (defaults to png) See https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.savefig.htmlsavefig_dpi: 200# Options : null (defaults to the value in topostats/plotting_dictionary.yaml), see https://afm-spm.github.io/TopoStats/main/configuration.html#further-customisation and https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.savefig.htmlpixel_interpolation: null # Options : https://matplotlib.org/stable/gallery/images_contours_and_fields/interpolation_methods.htmlimage_set: all # Options : all, corezrange: [-2, 8] # low and high height range for core images (can take [null, null]). low <= highcolorbar: true # Options : true, falseaxes: true # Options : true, false (due to off being a bool when parsed)num_ticks: [null, null] # Number of ticks to have along the x and y axes. Options : null (auto) or integer > 1cmap: null # Colormap/colourmap to use (default is 'nanoscope' which is used if null, other options are 'afmhot', 'viridis' etc.)mask_cmap: blue_purple_green # Options : blu, jet_r and any in matplotlibhistogram_log_axis: false # Options : true, falsesummary_stats:
run: true # Whether to make summary plots for output dataconfig: null
Checklist
topostats --core 1 process
.topostats --log-level debug <command>
.topostats --version
Describe the bug
When padding is too small, the mask smoothing at the start of disordered tracing
trace_dna()
causes the mask to touch the border of the image.Original mask:
Smoothed mask:
This issue is solvable by simply increasing the
grain_crop_padding
to, say, 10.I don't think we should spend time trying to prevent this occurring, rather I think we should make it clear in the logs when this error occurs that it can be solved by increasing the padding.
Here is the output currently when the error is thrown:
Proposed solution:
trace_dna()
and fail early:Copy of the log-file from running with
topostats --log-level debug <command>
Include the configuration file
To Reproduce
Files here: https://drive.google.com/drive/folders/1vKTyqujkT_Iv49X-VNDpgyX5LnlDxPL8
Run topostats with above config on those files
topostats -c my-config.yaml process
TopoStats Version
Git main branch
Python Version
3.11
Operating System
MacOS M1/M2 (post-2021)
Python Packages
The text was updated successfully, but these errors were encountered: