OSCR

SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.

Code ↔ Paper

12 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 12 matches
  1. [1] § Methods › Selection of segmentation methods for comparison ↔ SynAPSeg/Segmentation/classical_methods_optimizer.py, lines 96–137 · score 0.86 · Gaussian smoothing, watershed segmentation, distance transform, binary mask, minimum distance, optimized
  2. [2] § Methods › Selection of segmentation methods for comparison ↔ SynAPSeg/utils/utils_image_processing.py, lines 1031–1080 · score 0.82 · Gaussian smoothing, distance transform, binary mask, minimum distance, Otsu, watershed
  3. [3] § Methods › SynAPSeg design ↔ SynAPSeg/IO/project.py, lines 1–54 · score 0.71 · raw microscopy, generated segmentation, segmentation parameters, configuration, metadata, ROIs
  4. [4] § Methods › Confocal imaging and data analysis using SynAPSeg ↔ SynAPSeg/Quantification/plugins/roi_handling.py, lines 612–646 · score 0.61 · ray casting, atlas regions, polygons, centroid, Quantification
  5. [5] § Methods › Confocal imaging and data analysis using SynAPSeg ↔ SynAPSeg/Quantification/plugins/roi_handling.py, lines 338–432 · score 0.60 · Overlap3D, ROI assignment, ROI masks, quantification
  6. [6] § Methods › Confocal imaging and data analysis using SynAPSeg ↔ SynAPSeg/IO/writers.py, lines 7–98 · score 0.60 · OUTPUT_IMAGE_PYRAMID, QuPath, ABBA, compatibility, transform
  7. [7] § Methods › SynAPSeg design ↔ SynAPSeg/Quantification/plugins/object_detection.py, lines 83–217 · score 0.60 · ROI assignments, ROI handling, configuration, channels, metadata, ID
  8. [8] § Methods › Evaluation of segmentation methods ↔ SynAPSeg/Segmentation/classical_methods_optimizer.py, lines 42–94 · score 0.59 · matched predicted, ground truth, Union, Intersection, IoU, overlap
  9. [9] § Results › Analysis of PSD95 puncta on dendrites of CA1 PV Interneurons in 3- and 12-month mice ↔ SynAPSeg/Segmentation/Pipeline.py, lines 204–270 · score 0.55 · segmentation pipeline, confocal images, StarDist model, cohort, PV, SynAPSeg
  10. [10] § Methods › Confocal imaging and data analysis using SynAPSeg ↔ SynAPSeg/Quantification/plugins/roi_handling.py, lines 338–432 · score 0.55 · Overlap3D, ROI assignment, spacing, voxel, ROIs, quantification
  11. [11] § Methods › Training data preparation ↔ SynAPSeg/models/base.py, lines 19–98 · score 0.51 · maximum intensity projections, dynamic, preprocessed, volumes, slices, dimensions
  12. [12] § Methods › Benchmark dataset generation and evaluation ↔ SynAPSeg/models/plugins/Neurseg.py, lines 16–100 · score 0.51 · IoU scores, F1 scores, recall, metrics, threshold, model

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 739 lines · 33 KB · BSD-3-Clause · 3 matches

  1. #!/usr/bin/env python3
  2. from typing import List, Dict, Tuple, Optional, Any
  3. import pandas as pd
  4. import numpy as np
  5. from SynAPSeg.Quantification import BasePipelineStage
  6. from SynAPSeg.utils import utils_image_processing as uip
  7. from SynAPSeg.utils import utils_plotting as up
  8. from SynAPSeg.utils import utils_colocalization as uc
  9. from SynAPSeg.utils import utils_colocalization_3D
  10. from SynAPSeg.config.constants import STANDARD_FORMAT, DISPLAY_FORMAT
  11. __plugin_group__ = 'quantification'
  12. __plugin__ = 'ROIHandlingStage'
  13. __parameters__ = 'roi_handling.yaml'
  14. __stage_key__ = 'roi_handling' # this is name of plugin
  15. class ROIHandlingStage(BasePipelineStage):
  16. """
  17. ROIHandlingStage summarizes the morphological/intesnsity properties of an roi and prepares
  18. it for use to localize objects within roi subregions
  19. Expects the input data dictionary to contain:
  20. "rois": a list of ROI arrays (only 1 supported as of now) or a geojson feature collection.
  21. A normalized intensity image under the key specified by INTENSITY_IMAGE_NAME.
  22. Uses configuration parameters:
  23. REMAP_ROIS: dictionary for remapping ROI values (or null).
  24. ROI_REMOVE_SMALL_OBJ_SIZE: threshold for removing small ROI objects.
  25. REID_ROIS: boolean; if True, reassign ROI labels.
  26. PLOT_RPDF_EXTRACTION: boolean; if True, plot overlays for debugging.
  27. INTENSITY_IMAGE_NAME: key name in data for intensity image.
  28. img_fmt: format string (e.g., "YXC") for the intensity image.
  29. Updates the data dictionary with:
  30. "labeled_mask": a mask obtained from polygon conversion of the ROI.
  31. "polygons_per_label": the polygon representation of the ROI.
  32. "roi_df": a dataframe summarizing ROI properties.
  33. """
  34. __runOrderPreferences__ = {'before': ['object_detection'], 'after': []}
  35. __compileOrderPreferences__ = {'before': [], 'after': ['object_detection']}
  36. def init_outputs(self):
  37. # name: container, key in data: name
  38. return [{
  39. 'container_name': 'all_roi_dfs',
  40. 'container': [],
  41. 'data_key': 'roi_df'
  42. }]
  43. def __call__(self, roi, intensity_image, img_fmt, INTENSITY_IMAGE_NAME=None, REID_ROIS=False, ALLOW_ROI_IMG_SHAPE_MISMATCH=True, **config_kwargs):
  44. """
  45. OUTDATED - NOT FUNCTIONAL
  46. user friendly wrapper function for useing outside pipeline's automatic context
  47. pulled out commonly used kwargs, but more are described in execute or default config parameters
  48. # TODO this needs to be updated since adding of 3d roi assignment methods
  49. Returns
  50. dict with keys: ['rois', 'mip_raw', 'labeled_mask', 'polygons_per_label', 'roi_df']
  51. """
  52. INTENSITY_IMAGE_NAME = INTENSITY_IMAGE_NAME or 'mip_raw'
  53. return self.run(
  54. data={
  55. 'rois':[roi],
  56. INTENSITY_IMAGE_NAME:intensity_image,
  57. },
  58. config = {
  59. 'INTENSITY_IMAGE_NAME':INTENSITY_IMAGE_NAME,
  60. 'img_fmt': img_fmt,
  61. 'REID_ROIS':REID_ROIS,
  62. 'ALLOW_ROI_IMG_SHAPE_MISMATCH': ALLOW_ROI_IMG_SHAPE_MISMATCH,
  63. **config_kwargs
  64. }
  65. )
  66. def _execute(self, data: dict, config: dict) -> dict:
  67. stage_config = self.get_stage_config(config, __stage_key__)
  68. # skip
  69. if ("rois" not in data):
  70. self.logger.warning(f" skipping... \n\trois not in data.keys() but roi handling stage in quant pipeline.\n\tensure this behavior is desired, remove roi handling from pipeline.stages, or declare rois in config filemap ")
  71. return data
  72. # get rois from data
  73. #############################################################################################
  74. if (not isinstance(data['rois'], list)) or (len(data['rois'])==0):
  75. return self.raise_exit_flag(f"Input data does not contain 'rois'\ngot:{data.get('rois')}\nensure quant_config FILE_MAP['ROIS'] is set correctly\n", data)
  76. rois = data["rois"] # either list[np.ndarray] or geojsonPolyCollection
  77. # Parameters from config - all can be handled safely if not provided
  78. #############################################################################################
  79. intensity_image_key = config.get("INTENSITY_IMAGE_NAME", None)
  80. img_fmt = config.get("img_fmt", "ZYX")
  81. rois_formats = config.get("ROIS_FORMATS", ["YX"])
  82. roi_types = config.get("ROI_TYPES", ["mask"]) # must be mask or polygon
  83. remap_rois = stage_config.get("REMAP_ROIS", None)
  84. size_range = uip._sanitize_size_range(stage_config.get("ROI_OBJECTS_SIZE_RANGE"))
  85. reid_rois = stage_config.get("REID_ROIS", False)
  86. ALLOW_ROI_IMG_SHAPE_MISMATCH = stage_config.get("ALLOW_ROI_IMG_SHAPE_MISMATCH", False) # whether to raise error if roi format != img format
  87. HANDLE_SHAPE_MISMATCH_KEY = stage_config.get("HANDLE_ROI_IMG_SHAPE_MISMATCH", "img") # transform format to this object, must be either ["img" or "roi"]
  88. plot_extraction = stage_config.get("PLOT_RPDF_EXTRACTION", False)
  89. # get region prop args
  90. PX_SIZES = config.get("PX_SIZES") # PX_SIZE_XY = config.get("PX_SIZE_XY", 1)
  91. rps_to_get = stage_config.get('RPS_TO_GET')
  92. ADDITIONAL_PROPS = stage_config.get("ROI_ADDITIONAL_PROPS")
  93. GET_OBJECT_COORDS = stage_config.get("ROI_GET_OBJECT_COORDS") or False
  94. EXTRA_PROPERTIES = stage_config.get("ROI_EXTRA_PROPERTIES")
  95. self.logger.debug((
  96. f"roi_handing stage config:\n"
  97. f" img_fmt: {img_fmt} | rois_formats: {rois_formats} | roi_types: {roi_types}\n"
  98. f" PX_SIZES: {PX_SIZES}\n PROPERTIES: {rps_to_get}\n Extra:{EXTRA_PROPERTIES}\n ADDITIONAL_PROPS: {ADDITIONAL_PROPS}\n"
  99. ))
  100. # take first roi for now - TODO build support for multiple ROIs
  101. #############################################################################################
  102. roi_array = rois[0] # can be np.array or polyCollection
  103. roi_type = roi_types[0]
  104. roi_fmt = rois_formats[0]
  105. IS_3D = 'Z' in roi_fmt
  106. # ROI preprocessing
  107. #############################################################################################
  108. NEED_ROI_AS_ARRAY = False # TODO make proper knob - if False, saves alot of time if we don't need roi pixel intensities
  109. # handle roi polygons
  110. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  111. if roi_type == 'polygon':
  112. # for standard processing - functions require polygons as shapely objects as dict mapped it's respective label
  113. from SynAPSeg.Plugins.ABBA.core_regionPoly import polyCollection
  114. polys: polyCollection = roi_array
  115. polygons_per_label = {
  116. i+1:[p.to_shapely()] for i,p in enumerate(polys.polygons)
  117. }
  118. # Convert polygons to labeled mask
  119. if NEED_ROI_AS_ARRAY:
  120. baseshape = data[intensity_image_key].shape
  121. _shape = tuple([baseshape[img_fmt.index(dim)] for dim in 'YX'])
  122. self.logger.debug(f"Converting polygons to array using intensity img shape: {_shape}.")
  123. labeled_mask = uc.create_labeled_mask(polygons_per_label, _shape)
  124. self.logger.debug("Converted polygons to labeled ROI mask.")
  125. else:
  126. labeled_mask = None
  127. else:
  128. labeled_mask = roi_array
  129. # handle roi array
  130. # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  131. if roi_type == 'mask':
  132. if remap_rois is not None:
  133. roi_array = uip.map_arr_values(roi_array, remap_rois)
  134. self.logger.debug("Applied remapping of ROI IDs.")
  135. # filter by size
  136. from SynAPSeg.utils.utils_image_processing import _sanitize_size_range, filter_area_objects
  137. size_range = _sanitize_size_range(size_range)
  138. if size_range is not None:
  139. nlbls = len(uip.unique_nonzero(roi_array))
  140. roi_array = filter_area_objects(roi_array, size_range, preserve_labels=True)
  141. self.logger.debug(f"Removed {nlbls - len(uip.unique_nonzero(roi_array))} ROI objects using size range: {size_range}.")
  142. # Reassign ROI labels
  143. if reid_rois:
  144. roi_array = uip.relabel(roi_array)
  145. self.logger.debug("Relabeled ROI objects.")
  146. # Convert to polygons - only 2d input (or CYX) supported here
  147. if roi_fmt=='YX':
  148. polygons_per_label = uc.semantic_to_polygons_rasterio(roi_array)
  149. self.logger.debug("Converted ROI array to polygon representation.")
  150. elif roi_fmt == 'CYX': # if has ch axis, YX p.p.l are nested inside dict with ch indicies as keys
  151. polygons_per_label = {
  152. ch_i: uc.semantic_to_polygons_rasterio(a)
  153. for ch_i, a in
  154. enumerate(uip.unpack_array_axis(roi_array, roi_fmt.index('C')))
  155. }
  156. self.logger.debug("Converted ROI array to polygon representation - channels are keys.")
  157. else:
  158. polygons_per_label = None
  159. self.logger.debug(f"Skipping conversion of non-2D array to polygon representation (unsupported for ROI with format: {roi_fmt}).")
  160. # get intensity img if provided
  161. #############################################################################################
  162. if intensity_image_key is None: # if not provided make an empty array with same shape as roi
  163. self.logger.info(f"intensity_image_key not provided, making an empty array with same shape as roi")
  164. intensity_img = np.zeros_like(labeled_mask, dtype='uint8')
  165. img_fmt = roi_fmt
  166. elif intensity_image_key not in data:
  167. raise ValueError(f"Intensity image key '{intensity_image_key}' was provided but not found in data.")
  168. else:
  169. intensity_img = data[intensity_image_key]
  170. assert intensity_img.ndim == len(img_fmt), f"intensity_img.ndim != len(img_fmt), got: {intensity_img.ndim} != {len(img_fmt)}"
  171. # handle intensity image formating
  172. if roi_type == 'mask' or NEED_ROI_AS_ARRAY:
  173. # handle intensity image <-> ROI format matching
  174. #############################################################################################
  175. # only transform if spaital axes mismatch
  176. spatial_axes_img = [c for i, c in enumerate(img_fmt) if c in "ZYX"]
  177. spatial_axes_mask = [c for i, c in enumerate(roi_fmt) if c in "ZYX"]
  178. _spaital_axes_mismatch = (set(spatial_axes_img) != set(spatial_axes_mask))
  179. self.logger.debug(
  180. f"intensity_img shape: {intensity_img.shape}, labeled_mask shape: {labeled_mask.shape}\n"
  181. f"img-roi spaital axes match: {not _spaital_axes_mismatch}\n\tspatial_axes img:{set(spatial_axes_img)}, roi:{set(spatial_axes_mask)}\n"
  182. f"\timg_fmt: {img_fmt}, roi_fmt: {roi_fmt}"
  183. )
  184. # handle img-ROI shape mismatch - e.g. insert z dim to mask if in intensity_img but not in mask - think ROI areas might be off then
  185. # while will also handle case where image is transformed to match roi format the data will not be updated with the new format
  186. if (labeled_mask.shape != intensity_img.shape): # and _spaital_axes_mismatch: <-- this prevents reshaping along e.g. C axis
  187. if not ALLOW_ROI_IMG_SHAPE_MISMATCH:
  188. raise ValueError(f"ROI and intensity_img shape mismatch, got: ({labeled_mask.ndim} != {intensity_img.ndim}) & ALLOW_ROI_IMG_SHAPE_MISMATCH={ALLOW_ROI_IMG_SHAPE_MISMATCH}")
  189. self.logger.warning(f"shape mismatch between labeled_mask and intensity_img (key={HANDLE_SHAPE_MISMATCH_KEY}) - attempting to handle")
  190. target_fmt = img_fmt if HANDLE_SHAPE_MISMATCH_KEY == "img" else roi_fmt
  191. transformed = uip.morph_to_target_shape(
  192. # get current and target formats and shapes
  193. arr = labeled_mask if HANDLE_SHAPE_MISMATCH_KEY == "img" else intensity_img,
  194. current_fmt = roi_fmt if HANDLE_SHAPE_MISMATCH_KEY == "img" else img_fmt,
  195. target_shape = intensity_img.shape if HANDLE_SHAPE_MISMATCH_KEY == "img" else labeled_mask.shape,
  196. target_fmt = target_fmt
  197. )
  198. # update current object to the transformed object
  199. if HANDLE_SHAPE_MISMATCH_KEY == "img":
  200. labeled_mask = transformed
  201. roi_fmt = target_fmt
  202. else:
  203. intensity_img = transformed
  204. img_fmt = target_fmt
  205. self.logger.debug(f"handled roi intensity_img shape mismatch (key={HANDLE_SHAPE_MISMATCH_KEY}): labeled_mask shape: {labeled_mask.shape}, intensity_img shape: {intensity_img.shape}")
  206. # Optional debug plotting
  207. #############################################################################################
  208. if plot_extraction:
  209. if polygons_per_label is None:
  210. self.logger.error(f"cannot plot_extraction because polygons_per_label is None.")
  211. else:
  212. show_ch = 0
  213. show_ppl = polygons_per_label[show_ch] if roi_fmt=='CYX' else polygons_per_label
  214. uc.plot_polygons_over_image(roi_array, show_ppl)
  215. if intensity_image_key in data:
  216. intensity_img = data[intensity_image_key]
  217. composite_img = uip.transform_axes(intensity_img, img_fmt, STANDARD_FORMAT)
  218. composite_img = uip.reduce_dimensions(composite_img, STANDARD_FORMAT, project_dims=uip.subtract_dimstr(STANDARD_FORMAT, DISPLAY_FORMAT))
  219. composite_img = up.create_composite_image_with_colormaps(
  220. composite_img, ['blue', 'green', 'red', 'magenta']
  221. )
  222. uc.plot_polygons_over_image(composite_img, show_ppl)
  223. # Summarize ROI properties
  224. #############################################################################################
  225. self.logger.info('running ROI feature extraction...')
  226. if roi_type == 'mask':
  227. roi_df = uc.summarize_roi_array_properties(
  228. intensity_img,
  229. labeled_mask,
  230. img_fmt,
  231. roi_fmt,
  232. coerce_roi_fmt=False, # this is already handled more sophisticatedly above
  233. PX_SIZES=PX_SIZES,
  234. rps_to_get = rps_to_get,
  235. get_object_coords=GET_OBJECT_COORDS,
  236. additional_props=ADDITIONAL_PROPS,
  237. extra_properties=EXTRA_PROPERTIES,
  238. )
  239. else:
  240. roi_df = uc.summarize_roi_properties(
  241. polygons_per_label,
  242. intensity_img,
  243. labeled_mask,
  244. img_fmt,
  245. PX_SIZES=PX_SIZES,
  246. )
  247. self.logger.debug("Summarized ROI properties into a dataframe.")
  248. # Update data
  249. data["labeled_mask"] = labeled_mask
  250. data["polygons_per_label"] = polygons_per_label
  251. data["roi_df"] = roi_df
  252. return data
  253. def _compile(self, data: dict, config) -> dict:
  254. """
  255. Compile the data into a summary dataframe.
  256. """
  257. if 'roi_df' in data:
  258. data['roi_df'] = (data['roi_df'].assign(
  259. **data['assign_md_attrs'],
  260. **data['extracted_fn_groups'],
  261. ))
  262. # append roi info to summary
  263. if data['summary_df'] is not None:
  264. grouping_cols = [c for c in data['grouping_cols'] if (c in data['roi_df'].columns and not all(pd.isnull(data['roi_df'][c])))]
  265. summary_df = pd.merge(left=data['summary_df'], right=data['roi_df'], on=grouping_cols, how='left')
  266. summary_df['count_per_um'] = summary_df['count'] / summary_df['roi_area_um']
  267. data['summary_df'] = summary_df
  268. return data
  269. class ROIAssigner:
  270. """Handles assignment of objects to ROIs using various methods."""
  271. SUPPORTED_METHODS = ['Centroid', 'Coords', 'Masking', 'Distance'] + ['Overlap3D', 'Distance3D']
  272. @staticmethod
  273. def assign_rois_to_rpdf(
  274. rpdf: pd.DataFrame,
  275. polygons_per_label: Optional[Dict[Any, List] | Dict[int, Dict[Any, List]]] = None,
  276. roi: Optional[np.ndarray]=None,
  277. roi_assignment_methods: Optional[List[str]] = None,
  278. lbls: Optional[np.ndarray] = None,
  279. voxel_size: Optional[list[float]] = None,
  280. lbls_fmt: Optional[str] = None,
  281. roi_fmt: Optional[str] = None,
  282. logger: Optional[Any] = None,
  283. ) -> pd.DataFrame:
  284. """
  285. Assign ROIs to objects in the dataframe using specified methods.
  286. Note: optionality of input args depends on method used.
  287. Args:
  288. rpdf: DataFrame containing object data with 'coords' and 'centroid' columns
  289. polygons_per_label: Dictionary mapping labels to polygon lists, not used for 3D
  290. if roi array is CYX fmt, YX p.p.l are nested inside dict with ch indicies as keys
  291. roi: ROI mask array. Required for masking and 3D methods
  292. roi_assignment_methods: List of methods to apply. Defaults to ['Coords']
  293. lbls: labeled objects array. Required for 3D
  294. voxel_size: image voxel size (spacing). Used in distance 3d. default [1,1,1]
  295. Returns:
  296. DataFrame with added ROI assignment columns
  297. Raises:
  298. ValueError: If unsupported methods are specified or method application fails
  299. """
  300. # TODO: add support for multiple ROIs - in config need to represent as list of dicts
  301. # with keys for data_key to get roi_array, roi_type (mask or polygon), and roi_assignment_methods
  302. method_handlers = {
  303. 'Coords': ROIAssigner._apply_coords_method,
  304. 'Distance': ROIAssigner._apply_distance_method,
  305. 'Centroid': ROIAssigner._apply_centroid_method,
  306. 'Masking': ROIAssigner._apply_masking_method,
  307. 'Overlap3D': ROIAssigner._apply_overlap3d_method,
  308. 'Distance3D': ROIAssigner._apply_distance3d_method,
  309. }
  310. if roi_assignment_methods is None:
  311. roi_assignment_methods = ['Coords']
  312. elif isinstance(roi_assignment_methods, str):
  313. roi_assignment_methods = [roi_assignment_methods]
  314. if logger is not None:
  315. logger.info(f"Assigning objects to rois using (ROI_ASSIGNMENT_METHODS: {roi_assignment_methods})... ")
  316. # Validate methods
  317. ROIAssigner._validate_methods(roi_assignment_methods)
  318. # Create a copy to avoid modifying the original
  319. result_df = rpdf.copy()
  320. # handle roi reshaping
  321. if all([el is not None for el in [roi, lbls, roi_fmt, lbls_fmt]]):
  322. if roi.shape != lbls.shape:
  323. roi = uip.morph_to_target_shape(roi, roi_fmt, lbls.shape, lbls_fmt)
  324. roi_fmt = lbls_fmt
  325. # Pre-compute commonly used data. TODO also using to pass method-specific params (e.g. voxel_size) but would be better to handle separately
  326. computed_data = ROIAssigner._precompute_data(
  327. result_df, roi_assignment_methods, lbls, roi, voxel_size, lbls_fmt, roi_fmt
  328. )
  329. # Apply each method
  330. applied_methods = []
  331. for method in roi_assignment_methods:
  332. try:
  333. method_handlers[method](result_df, polygons_per_label, roi, computed_data)
  334. applied_methods.append(method)
  335. if logger is not None:
  336. logger.info(f"Successfully applied ROI_ASSIGNMENT_METHOD: {method}")
  337. except Exception as e:
  338. raise ValueError(f"Failed to apply {method} method: {str(e)}\n") from e
  339. # Verify all methods were applied
  340. if len(roi_assignment_methods) != len(applied_methods):
  341. raise ValueError(
  342. f"Not all requested ROI assignment methods were applied. "
  343. f"Requested: {roi_assignment_methods}, applied: {applied_methods}"
  344. )
  345. computed_data_info = ''
  346. for k,v in computed_data.items():
  347. fval = f"shape:{v.shape}" if isinstance(v, np.ndarray) else f"len({len(v)})" if isinstance(v, list) else str(v)
  348. computed_data_info += f"{k}: {fval}\n"
  349. logger.debug(f"Computed data: \n{computed_data_info}")
  350. return result_df
  351. @staticmethod
  352. def _validate_methods(roi_assignment_methods: List[str]) -> None:
  353. """Validate that all requested methods are supported."""
  354. unsupported = set(roi_assignment_methods) - set(ROIAssigner.SUPPORTED_METHODS)
  355. if unsupported:
  356. raise ValueError(
  357. f"Unsupported ROI assignment methods: {unsupported}. "
  358. f"Supported: {ROIAssigner.SUPPORTED_METHODS}"
  359. )
  360. @staticmethod
  361. def _precompute_data(
  362. df: pd.DataFrame,
  363. methods: List[str],
  364. lbls: Optional[np.ndarray] = None,
  365. roi: Optional[np.ndarray]=None,
  366. voxel_size: Optional[list[float]] = None,
  367. lbls_fmt: Optional[str] = None,
  368. roi_fmt: Optional[str] = None,
  369. ) -> Dict[str, Any]:
  370. """Pre-compute data needed by multiple methods to avoid redundant calculations."""
  371. computed: dict = {}
  372. # add fmt info
  373. computed['lbls_fmt'] = lbls_fmt
  374. computed['roi_fmt'] = roi_fmt
  375. computed['roi_num_channels'] = None
  376. if isinstance(roi,np.ndarray) and isinstance(roi_fmt, str) and ('C' in roi_fmt):
  377. computed['roi_num_channels'] = roi.shape[roi_fmt.index('C')]
  378. # Centroid data needed by Distance, Centroid, and Masking methods
  379. centroid_methods = {'Distance', 'Centroid', 'Masking'}
  380. if any(method in centroid_methods for method in methods):
  381. centroids_list = df['centroid'].to_list()
  382. computed['centroids_list'] = centroids_list
  383. # For Distance and Centroid methods (need coordinate reversal)
  384. if any(method in {'Distance', 'Centroid'} for method in methods):
  385. computed['object_centroids'] = np.array([
  386. np.array(c[::-1]) for c in centroids_list
  387. ])
  388. # For Masking method (need integer coordinates)
  389. if 'Masking' in methods:
  390. computed['masking_coords'] = np.rint(np.array(centroids_list)).astype(int)
  391. # Coords data (only needed by Coords method)
  392. if 'Coords' in methods:
  393. coords_list = df['coords'].to_list()
  394. computed['coords_geometric'] = uc.convert_coordinates_image_to_geometric(coords_list)
  395. computed['sorted_coords'] = [
  396. uc.sort_coordinates_by_distance(coords)[0] # sort coords by distance from centroid so roi assignment is biased towards objects center
  397. for coords in computed['coords_geometric']
  398. ]
  399. # 3D methods - TODO: add accepting inputs for distance radius/voxel_size
  400. methods_3d = ['Distance3D', 'Overlap3D']
  401. computed['voxel_size'] = voxel_size # currently only used by 3d distance
  402. if any(method in methods_3d for method in methods):
  403. if lbls is None:
  404. raise ValueError('3D methods require original labels array used to construct rpdf')
  405. # basic validation checks
  406. if lbls is None or roi is None:
  407. raise ValueError("3D label arrays not found in computed_data")
  408. if lbls.shape != roi.shape:
  409. raise ValueError(f"lbls.shape != roi.shape, {lbls.shape, roi.shape}")
  410. if 'CZYX' != lbls_fmt or lbls.ndim != 4: # cleaner if just enforce standard format
  411. raise ValueError(f"{lbls_fmt}, {lbls.shape}")
  412. if 'label' not in df.columns:
  413. raise ValueError('`label` column required')
  414. if 'colocal_id' not in df.columns:
  415. df['colocal_id'] = 0
  416. # we assume colocal_ids map directly to image channel indicies
  417. # but this could be more explicitly handled, if have access to imgdb object
  418. ch_axis = lbls_fmt.index('C')
  419. if set(df['colocal_id'].unique()) != set(range(lbls.shape[ch_axis])):
  420. raise ValueError(f"{set(df['colocal_id'].unique())} != {set(range(lbls.shape[ch_axis]))}")
  421. computed['lbls'] = lbls
  422. computed['ch_axis'] = ch_axis
  423. return computed
  424. @staticmethod
  425. def _apply_coords_method(
  426. df: pd.DataFrame,
  427. polygons_per_label: Dict,
  428. roi: np.ndarray,
  429. computed_data: Dict
  430. ) -> None:
  431. """
  432. Apply the Coords method for ROI assignment.
  433. Creates cols in df:
  434. roi_i_byCoords, roi_polyi_byCoords
  435. """
  436. assert polygons_per_label is not None, "polygons_per_label is None.\n Note if using a 3D ROI this is expected, so instead use a 3D assignment method like `Overlap3D`"
  437. # initialize the output columns in the df
  438. df['roi_i_byCoords'] = np.nan
  439. df['roi_polyi_byCoords'] = np.nan
  440. if computed_data['roi_num_channels'] is None:
  441. df['roi_i_byCoords'], df['roi_polyi_byCoords'] = \
  442. uc.assign_labels_to_object_indices(
  443. computed_data['sorted_coords'], polygons_per_label
  444. )
  445. # if roi array has multiple channels
  446. # slice subset of detections in this channel (colocal_id) and localize to roi_array for each channel
  447. else:
  448. # create temp col in df with sorted_coords, facilitates mapping df row's coords to colocal_ids
  449. df['sorted_coords'] = computed_data['sorted_coords']
  450. for ch_i, ppl in polygons_per_label.items():
  451. _mask = df['colocal_id']==ch_i
  452. if len(df.loc[_mask]) == 0:
  453. print(f"No detections found for colocal_id {ch_i}")
  454. continue
  455. df.loc[_mask, 'roi_i_byCoords'], df.loc[_mask, 'roi_polyi_byCoords'] = \
  456. uc.assign_labels_to_object_indices(
  457. df.loc[_mask, 'sorted_coords'].to_list(), ppl
  458. )
  459. df.drop(['sorted_coords'], axis=1, inplace=True)
  460. @staticmethod
  461. def _apply_distance_method(
  462. df: pd.DataFrame,
  463. polygons_per_label: Dict,
  464. roi: np.ndarray,
  465. computed_data: Dict
  466. ) -> None:
  467. """Apply the Distance method for ROI assignment."""
  468. nearest_labels, nearest_poly_indices, distances = uc.compute_distances_to_rois(
  469. polygons_per_label, computed_data['object_centroids']
  470. )
  471. df['roi_i_byDistance'] = nearest_labels
  472. df['roi_polyi_byDistance'] = nearest_poly_indices
  473. df['roi_distance'] = distances
  474. @staticmethod
  475. def _apply_centroid_method(
  476. df: pd.DataFrame,
  477. polygons_per_label: Dict,
  478. roi: np.ndarray,
  479. computed_data: Dict
  480. ) -> None:
  481. """Apply the Centroid method for ROI assignment."""
  482. assigned_ids_centroid, poly_subindices_centroid = uc.assign_labels(
  483. computed_data['object_centroids'], polygons_per_label
  484. )
  485. df['roi_i_byCentroid'] = assigned_ids_centroid
  486. df['roi_polyi_byCentroid'] = poly_subindices_centroid
  487. @staticmethod
  488. def _apply_masking_method(
  489. df: pd.DataFrame,
  490. polygons_per_label: Dict,
  491. roi: np.ndarray,
  492. computed_data: Dict
  493. ) -> None:
  494. """ returns label for each centroid by indexing into the roi array """
  495. coords = computed_data['masking_coords']
  496. assigned_ids_masking = roi[coords[:, 0], coords[:, 1]]
  497. df['roi_i_byMasking'] = assigned_ids_masking
  498. df['roi_polyi_byMasking'] = np.nan
  499. @staticmethod
  500. def _apply_atlas_method(
  501. df: pd.DataFrame,
  502. polygons_per_label: Dict,
  503. roi: np.ndarray,
  504. computed_data: Dict
  505. ) -> None:
  506. """Apply ray casting for centroid in polygon hierarchy for atlas regions. """
  507. from SynAPSeg.Plugins.ABBA import core_regionPoly as rp
  508. centroids = computed_data['object_centroids']
  509. constrained_regionPolys = polygons_per_label
  510. rpdf = df
  511. roi_nb_singles, roi_nb_multis, roi_infos = rp.separate_polytypes(constrained_regionPolys)
  512. roi_pp_result = rp.nb_process_polygons(roi_nb_singles, roi_nb_multis, centroids)
  513. # extract roi_i from assigned poly - indexing into info and using reg_id as roi_i
  514. roi_reg_ids = [roi_infos[roi_poly_i]['reg_id'] for roi_poly_i in roi_pp_result]
  515. assert len(roi_reg_ids) == len(rpdf)
  516. # TODO don't think this will update df in place
  517. rpdf = (
  518. pd.DataFrame(list(np.array(roi_infos + [{k: np.nan for k in roi_infos[0]}])[roi_pp_result]))
  519. .assign(
  520. centroid_i=np.arange(len(centroids)),
  521. roi_i=1, # TODO handle multiple rois
  522. # **(cfg["assign_rpdf_attributes"] or {}),
  523. )
  524. .merge(rpdf, left_on="centroid_i", right_index=True, how="left")
  525. )
  526. # but this might, not sure if all required info is assigned since roi_infos isn't set
  527. df['roi_i_byAtlas'] = 1
  528. df['roi_polyi_byAtlas'] = roi_pp_result
  529. df['reg_id'] = roi_reg_ids
  530. @staticmethod
  531. def _apply_overlap3d_method(df, polygons_per_label, roi, computed_data):
  532. """Apply 3D overlap-based assignment."""
  533. # Extract 3D arrays from computed_data
  534. lbls = computed_data['lbls']
  535. lbls_fmt = computed_data['lbls_fmt']
  536. ch_axis = computed_data['ch_axis']
  537. # prep outputdf
  538. df['roi_i_byOverlap3D'] = np.nan
  539. df['roi_polyi_byOverlap3D'] = np.nan
  540. # apply method over channels
  541. for ch_i in range(lbls.shape[ch_axis]):
  542. # slice channel
  543. indexer = uip.nd_slice(lbls, ch_axis, ch_i)
  544. _lbls = uip.safe_squeeze(lbls[indexer], 3)
  545. _roi = uip.safe_squeeze(roi[indexer], 3)
  546. # Compute overlap, returning mapping of object label -> roi_i
  547. mapping, stats = utils_colocalization_3D.map_synapses_to_dendrites_overlap(_lbls, _roi)
  548. # insert output at this colocal id - assumes channels map to colocal id
  549. dfInds = df['colocal_id']==ch_i
  550. df.loc[dfInds, 'roi_i_byOverlap3D'] = df.loc[dfInds, 'label'].map(mapping)
  551. @staticmethod
  552. def _apply_distance3d_method(df, polygons_per_label, roi, computed_data):
  553. """Apply 3D distance-based assignment."""
  554. # Extract 3D arrays and parameters
  555. lbls = computed_data['lbls']
  556. lbls_fmt = computed_data['lbls_fmt']
  557. ch_axis = computed_data['ch_axis']
  558. radius = computed_data.get('distance_radius', np.inf)
  559. voxel_size = computed_data.get('voxel_size', [1,1,1])
  560. # prep outputdf
  561. df['roi_i_byDistance3D'] = np.nan
  562. df['roi_polyi_byDistance3D'] = np.nan
  563. df['roi_distance3D'] = np.nan
  564. # apply method over channels
  565. for ch_i in range(lbls.shape[ch_axis]):
  566. # slice channel
  567. indexer = uip.nd_slice(lbls, ch_axis, ch_i)
  568. _lbls = uip.safe_squeeze(lbls[indexer], 3)
  569. _roi = uip.safe_squeeze(roi[indexer], 3)
  570. # Compute mapping
  571. mapping, stats = utils_colocalization_3D.map_synapses_to_dendrites_distance(
  572. _lbls, _roi, radius=radius, voxel_size=voxel_size
  573. )
  574. # extract distances (for all objects, even if dist > thresh)
  575. distances = {lbl: _stats['min_dist'] for lbl, _stats in stats['mindist_mapping'].items()}
  576. # insert output at this colocal id - assumes channels map to colocal id
  577. dfInds = df['colocal_id']==ch_i
  578. df.loc[dfInds, 'roi_i_byDistance3D'] = df.loc[dfInds, 'label'].map(mapping)
  579. df.loc[dfInds, 'roi_distance3D'] = df.loc[dfInds, 'label'].map(distances)
  580. if __name__ == '__main__':
  581. # Demonstration of ROIHandlingStage with dummy inputs.
  582. dummy_roi = np.random.randint(0, 3, size=(256, 256))
  583. dummy_rois = [dummy_roi]
  584. dummy_intensity = np.random.rand(256, 256, 3)
  585. data = {
  586. "rois": dummy_rois,
  587. "mip_raw": dummy_intensity,
  588. "img_fmt": "YXC"
  589. }
  590. dummy_config = {
  591. "REMAP_ROIS": None,
  592. "ROI_REMOVE_SMALL_OBJ_SIZE": 100,
  593. "REID_ROIS": False,
  594. "PLOT_RPDF_EXTRACTION": False,
  595. "INTENSITY_IMAGE_NAME": "mip_raw",
  596. "img_fmt": "YXC"
  597. }
  598. stage = ROIHandlingStage()
  599. updated_data = stage.run(data, dummy_config)
  600. print("ROI Handling Log:")
  601. print(updated_data["roi_handling_log"])
  602. print("ROI DataFrame head:")
  603. print(updated_data["roi_df"].head())

roi_handling.py at commit 36e18d6, under BSD-3-Clause · at the source

Overview

Authors: Pascal Schamber1, Sahana Darbhamulla1, Molly Boyer1, Madison Pelletier1, Helene Hartman1, Olivia Friedman1, Shiyu Zhang1, Allison Blais1, Seyun Oh1, Haining Zhong2, Alexei M Bygrave1
  1. Department of Neuroscience, Tufts University, Boston, Massachusetts, United States of America
  2. Vollum Institute, Oregon Health and Science University, Portland, Oregon, United States of America
Institutions: Tufts University (United States); Oregon Health & Science University (United States); Vollum Institute (United States)
Journal: PLoS computational biology, volume 22, issue 7, article e1014571
Dates: received 17 March 2026; accepted 13 July 2026; published online 29 July 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1371/journal.pcbi.1014571 · PMID 42525706 · PMCID PMC13432752 · OpenAlex W7171674952
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism)
Methods: Statistics, Machine learning, Evoked potentials, fMRI & imaging, Connectivity
MeSH: Deep Learning*, Image Processing, Computer-Assisted*, Synapses*, Animals, Computational Biology, Disks Large Homolog 4 Protein, Hippocampus, Humans, Interneurons (* major topic)
Topic: Neuroscience and Neuropharmacology Research (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: NIMH NIH HHS (RF1 MH130784, RF1 MH120119, R00 MH124920); National Institute of Mental Health (RF1MH120119, RF1MH130784, R00MH124920)
Citations: not cited yet (Europe PMC); 83 references in the paper

Abstract

Synapses are the fundamental units of neural computation, yet quantifying their organization across circuit-level scales remains a critical bottleneck in neuroscience. While advances in fluorescent labeling and imaging can generate vast datasets, analysis is often the limiting factor. Several deep learning-based tools have been proposed to ameliorate these issues. However, existing applications primarily focus on dendritic spines and lack robust solutions for segmenting synaptic puncta in dense tissue preparations. To address this, we introduce SynAPSeg, which encompasses an open-source framework for deep learning-based analysis and, to the best of our knowledge, the first large-scale, publicly available instance segmentation dataset specifically curated for synaptic puncta. We use this dataset to train deep learning models that reach the performance of human experts across a unique benchmark dataset. SynAPSeg integrates these models into an interactive interface, with support for multi-dimensional data, enabling fully automated segmentation and quantification pipelines alongside an annotation module for refinement and validation. We demonstrate the framework’s scalability by performing the first comprehensive mapping of nearly 4 million excitatory postsynaptic PSD95 puncta within inhibitory interneurons across the dorsal hippocampus, revealing regional differences in synapse properties. Finally, we show SynAPSeg’s utility for 3D quantification by applying these models to study aging-associated synaptic changes in CA1 parvalbumin (PV)-positive inhibitory neurons. Through this approach, we uncover a reduction in PSD95 density along PV dendrites in the aged CA1, indicating reduced glutamatergic recruitment of PV neurons which could contribute to age-related cognitive decline. Collectively, these results demonstrate that SynAPSeg provides a scalable solution for comprehensively studying synaptic architecture in health and disease.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 12 matches between paragraphs and lines of code.

pascalschamber/SynAPSeg

License: BSD-3-Clause
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 36e18d6b1e92cb1aed64d0ae1db37152db103721, 9 September 2026
Languages: Python (129)
Size: 189 files, 129 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, environment (pyproject.toml), tests
Not found: CITATION.cff, continuous integration, documentation
Tools: NumPy (55 files), pandas (33 files), Matplotlib (17 files), SciPy (15 files), scikit-image (11 files), tifffile (11 files), seaborn (10 files), napari (8 files), statsmodels (6 files), Numba (4 files), imageio (3 files), Keras (3 files), Pillow (3 files), Pingouin (3 files), TensorFlow (3 files), Cellpose (2 files), OpenCV (2 files), PyTorch (2 files), scikit-learn (2 files), NetworkX (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
131 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 129 scripts, each with its path and the digest of its content;
  • 12 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Data availability

The training datasets, benchmark datasets, and the custom trained models have deposited at the following DOI: https://doi.org/10.5281/zenodo.18988899. All code is open-source and available at our GitHub repository https://github.com/pascalschamber/SynAPSeg.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 11 authors, 9 MeSH terms, 2 funders, 79 references.

Cite

This paper

Schamber, P., Darbhamulla, S., Boyer, M., Pelletier, M., Hartman, H., Friedman, O., Zhang, S., Blais, A., Oh, S., Zhong, H., & Bygrave, A. M. (2026). SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification. PLoS computational biology, 22(7), e1014571. https://doi.org/10.1371/journal.pcbi.1014571

BibTeX

@article{schamber2026synapseg,
author = {Schamber, Pascal and Darbhamulla, Sahana and Boyer, Molly and Pelletier, Madison and Hartman, Helene and Friedman, Olivia and Zhang, Shiyu and Blais, Allison and Oh, Seyun and Zhong, Haining and Bygrave, Alexei M},
title = {{SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification}},
journal = {PLoS computational biology},
year = {2026},
month = jul,
volume = {22},
number = {7},
pages = {e1014571},
publisher = {PLOS},
issn = {1553-734X},
doi = {10.1371/journal.pcbi.1014571},
url = {https://doi.org/10.1371/journal.pcbi.1014571},
pmid = {42525706},
pmcid = {PMC13432752}
}

RIS

TY - JOUR
AU - Schamber, Pascal
AU - Darbhamulla, Sahana
AU - Boyer, Molly
AU - Pelletier, Madison
AU - Hartman, Helene
AU - Friedman, Olivia
AU - Zhang, Shiyu
AU - Blais, Allison
AU - Oh, Seyun
AU - Zhong, Haining
AU - Bygrave, Alexei M
TI - SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification
T2 - PLoS computational biology
J2 - PLoS Comput Biol
PY - 2026
DA - 2026/07/29
VL - 22
IS - 7
SP - e1014571
SN - 1553-734X
PB - PLOS
DO - 10.1371/journal.pcbi.1014571
UR - https://doi.org/10.1371/journal.pcbi.1014571
LA - en
ER -

CSL-JSON

{
"id": "10.1371/journal.pcbi.1014571",
"type": "article-journal",
"title": "SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification",
"container-title": "PLoS computational biology",
"author": [
{
"family": "Schamber",
"given": "Pascal"
},
{
"family": "Darbhamulla",
"given": "Sahana"
},
{
"family": "Boyer",
"given": "Molly"
},
{
"family": "Pelletier",
"given": "Madison"
},
{
"family": "Hartman",
"given": "Helene"
},
{
"family": "Friedman",
"given": "Olivia"
},
{
"family": "Zhang",
"given": "Shiyu"
},
{
"family": "Blais",
"given": "Allison"
},
{
"family": "Oh",
"given": "Seyun"
},
{
"family": "Zhong",
"given": "Haining"
},
{
"family": "Bygrave",
"given": "Alexei M"
}
],
"container-title-short": "PLoS Comput Biol",
"volume": "22",
"issue": "7",
"page": "e1014571",
"DOI": "10.1371/journal.pcbi.1014571",
"PMID": "42525706",
"PMCID": "PMC13432752",
"ISSN": "1553-734X",
"publisher": "PLOS",
"URL": "https://doi.org/10.1371/journal.pcbi.1014571",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
29
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi: [code]
Real-time closed-loop feedback system for mouse mesoscale cortical signal and movement control
Journal: eLife
In common: napari, imageio, Pingouin, 14 other tools, 1 reference
[2] doi:10.1016/j.xcrm.2026.102766 [code]
A longitudinal single-cell and spatial multiomic atlas of pediatric high-grade glioma.
Journal: Cell reports. Medicine
In common: imageio, tifffile, Keras, 12 other tools, 2 references
[3] doi:10.1093/neuonc/noag128 [code]
Spatially-resolved single-cell imaging of melanoma brain metastases identifies localized immune patterns predictive of immune checkpoint blockade response.
Journal: Neuro-oncology
In common: Cellpose, napari, imageio, 9 other tools, 1 reference
[4] doi:10.1038/s42003-026-10063-9 [code]
Conserved Kir channel mechanisms governing intrinsic excitability in human and rodent parvalbumin neurons.
Journal: Communications biology
In common: Cellpose, napari, tifffile, 8 other tools, 4 references
[5] doi:10.1038/s41467-026-72057-9 [code]
Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.
Journal: Nature communications
In common: Keras, Numba, TensorFlow, 11 other tools, 2 references
[6] doi:10.1038/s41593-026-02388-9 [code]
Hippocampal CA3 connectomics reveals a gradient of mossy fiber inputs and selective feedforward inhibition onto pyramidal cells.
Journal: Nature neuroscience
In common: imageio, NetworkX, OpenCV, 8 other tools, 4 references
[7] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: Keras, Numba, TensorFlow, 12 other tools
[8] doi:10.1038/s41598-026-57519-w [code]
Automated segmentation of neurons and spinal cord structures in immunofluorescence images using SpineDL.
Journal: Scientific reports
In common: imageio, tifffile, Numba, 10 other tools, 1 reference
[9] doi:10.1002/ana.78203 [code]
AI-Driven Mapping of Seizure Spread Patterns.
Journal: Annals of neurology
In common: Pingouin, Keras, TensorFlow, 11 other tools, 1 reference
[10] doi:10.1016/j.bpj.2026.06.005 [code]
Curvature-based machine-learning method for automated segmentation of dendritic spines.
Journal: Biophysical journal
In common: Keras, TensorFlow, NetworkX, 6 other tools, 5 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.