OSCR

Spatio-Temporal Dynamics of Macroglial Cell Organization and Proximity to Blood Vessels During Postnatal Development.

Code ↔ Paper

4 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 4 matches · 1 of them tie a paragraph to a whole file, not to given lines: a weak match, whose lines are not tinted
  1. [1] § Materials and Methods › Image Analysis ↔ src/main/java/VeCell_Tools/Tools.java, lines 1002–1064 · score 0.76 · standard deviation, Analyze Skeleton, branch length, junctions, neighbour, SDI
  2. [2] § Materials and Methods › Image Analysis ↔ src/main/java/VeCell_Tools/Cellpose/CellposeTask.java, the whole file · a weak match · score 0.73 · flow threshold, stitching threshold, pretrained model, Cellpose, diameter
  3. [3] § Materials and Methods › Image Analysis ↔ src/main/java/VeCell_Tools/Cellpose/CellposeTaskSettings.java, lines 61–126 · score 0.69 · cell probability, flow threshold, stitching threshold, Cellpose
  4. [4] § Materials and Methods › Image Analysis ↔ src/main/java/VeCell_Tools/Tools.java, lines 586–642 · score 0.51 · automated thresholding, Gaussian, median, CLIJ, filter

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

Java · 1,151 lines · 46 KB · no license · 2 matches

  1. package VeCell_Tools;
  2. import VeCell_Tools.Cellpose.CellposeTaskSettings;
  3. import VeCell_Tools.Cellpose.CellposeSegmentImgPlusAdvanced;
  4. import fiji.util.gui.GenericDialogPlus;
  5. import ij.IJ;
  6. import ij.ImagePlus;
  7. import ij.ImageStack;
  8. import ij.gui.Plot;
  9. import ij.gui.PolygonRoi;
  10. import ij.gui.Roi;
  11. import ij.io.FileSaver;
  12. import ij.measure.Calibration;
  13. import ij.measure.ResultsTable;
  14. import ij.plugin.ImageCalculator;
  15. import ij.plugin.RGBStackMerge;
  16. import ij.plugin.RoiEnlarger;
  17. import ij.plugin.RoiScaler;
  18. import ij.plugin.filter.Analyzer;
  19. import ij.plugin.filter.ParticleAnalyzer;
  20. import ij.plugin.frame.RoiManager;
  21. import ij.process.AutoThresholder;
  22. import java.awt.Color;
  23. import java.awt.Font;
  24. import java.io.BufferedWriter;
  25. import java.io.File;
  26. import java.io.FileWriter;
  27. import java.io.IOException;
  28. import java.util.ArrayList;
  29. import java.util.Collections;
  30. import java.awt.Rectangle;
  31. import java.util.Arrays;
  32. import java.util.List;
  33. import java.util.stream.IntStream;
  34. import javax.swing.ImageIcon;
  35. import loci.common.services.DependencyException;
  36. import loci.common.services.ServiceException;
  37. import loci.formats.FormatException;
  38. import loci.formats.meta.IMetadata;
  39. import loci.plugins.BF;
  40. import loci.plugins.in.ImporterOptions;
  41. import loci.plugins.util.ImageProcessorReader;
  42. import mcib3d.geom.Object3D;
  43. import mcib3d.geom.Objects3DPopulation;
  44. import mcib3d.image3d.ImageHandler;
  45. import mcib3d.geom2.Object3DInt;
  46. import mcib3d.geom2.Objects3DIntPopulation;
  47. import mcib3d.geom2.measurements.Measure2Distance;
  48. import mcib3d.geom2.measurements.MeasureCentroid;
  49. import mcib3d.geom2.measurements.MeasureIntensity;
  50. import mcib3d.geom2.measurements.MeasureVolume;
  51. import mcib3d.geom2.measurementsPopulation.MeasurePopulationDistance;
  52. import mcib3d.geom2.measurementsPopulation.PairObjects3DInt;
  53. import mcib3d.image3d.ImageFloat;
  54. import mcib3d.image3d.ImageInt;
  55. import mcib3d.image3d.ImageLabeller;
  56. import mcib3d.image3d.distanceMap3d.EDT;
  57. import mcib3d.spatial.descriptors.G_Function;
  58. import mcib3d.spatial.descriptors.SpatialDescriptor;
  59. import mcib3d.spatial.sampler.SpatialModel;
  60. import mcib3d.spatial.sampler.SpatialRandomHardCore;
  61. import mcib3d.utils.ThreadUtil;
  62. import net.haesleinhuepf.clij.clearcl.ClearCLBuffer;
  63. import net.haesleinhuepf.clij2.CLIJ2;
  64. import net.haesleinhuepf.clijx.bonej.BoneJSkeletonize3D;
  65. import org.apache.commons.io.FilenameUtils;
  66. import org.apache.commons.math3.stat.StatUtils;
  67. import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
  68. import sc.fiji.analyzeSkeleton.AnalyzeSkeleton_;
  69. import sc.fiji.analyzeSkeleton.Edge;
  70. import sc.fiji.analyzeSkeleton.Graph;
  71. import sc.fiji.analyzeSkeleton.Point;
  72. import sc.fiji.analyzeSkeleton.SkeletonResult;
  73. /**
  74. * @author ORION-CIRB
  75. */
  76. public class Tools {
  77. public final ImageIcon icon = new ImageIcon(this.getClass().getResource("/Orion_icon.png"));
  78. private final String helpUrl = "https://github.com/orion-cirb/VeCell/tree/version5";
  79. public CLIJ2 clij2 = CLIJ2.getInstance();
  80. private BufferedWriter resultsGlobal;
  81. private BufferedWriter resultsDetail;
  82. private Calibration cal;
  83. private double pixelVol;
  84. String[] chDialog = new String[]{"Astrocytes", "Vessels (optional)"};
  85. public int imgSeries = 0;
  86. public int roiScaling = 9;
  87. public double roiDilation = 50; // um
  88. public String cellposeEnvDir = IJ.isWindows()? System.getProperty("user.home")+File.separator+"miniconda3"+File.separator+"envs"+File.separator+"CellPose" : "/opt/miniconda3/envs/cellpose";
  89. public final String cellposeModelDir = IJ.isWindows()? System.getProperty("user.home")+File.separator+".cellpose"+File.separator+"models"+File.separator : "";
  90. public String cellposeModel = "cyto2_sox9_p5-15-60_27-11-24";
  91. public int cellposeDiam = 20;
  92. public double cellposeStitchTh = 0.5;
  93. public boolean filterOneZ = true;
  94. public double minCellVol = 300; // um3
  95. public double maxCellVol = 3000; // um3
  96. private int nbNei = 10; // K-nearest neighbors
  97. private boolean computeGFunction = false;
  98. private int nbRandomSamples = 50;
  99. private double dog1Sigma1 = 4;
  100. private double dog1Sigma2 = 8;
  101. private String vesselThMet1 = "Triangle";
  102. private boolean dog2 = true;
  103. private double dog2Sigma1 = 7;
  104. private double dog2Sigma2 = 14;
  105. private String vesselThMet2 = "Triangle";
  106. private double maxHoleArea = 100; // µm2
  107. public double minVesselVol = 600; // um3
  108. private double minVesselLength = 10; // um
  109. /**
  110. * Display a message in the ImageJ console and status bar
  111. */
  112. public void print(String log) {
  113. System.out.println(log);
  114. IJ.showStatus(log);
  115. }
  116. /**
  117. * Flush and close an image
  118. */
  119. public void closeImage(ImagePlus img) {
  120. img.flush();
  121. img.close();
  122. }
  123. /**
  124. * Check that needed modules are installed
  125. */
  126. public boolean checkInstalledModules() {
  127. ClassLoader loader = IJ.getClassLoader();
  128. try {
  129. loader.loadClass("net.haesleinhuepf.clij2.CLIJ2");
  130. } catch (ClassNotFoundException e) {
  131. IJ.log("CLIJ not installed, please install from update site");
  132. return false;
  133. }
  134. try {
  135. loader.loadClass("mcib3d.geom2.Object3DInt");
  136. } catch (ClassNotFoundException e) {
  137. IJ.log("3D ImageJ Suite not installed, please install from update site");
  138. return false;
  139. }
  140. return true;
  141. }
  142. /**
  143. * Get extension of the first image found in the folder
  144. */
  145. public String findImageType(File imagesFolder) {
  146. String ext = "";
  147. String[] files = imagesFolder.list();
  148. for (String name : files) {
  149. String fileExt = FilenameUtils.getExtension(name);
  150. switch (fileExt) {
  151. case "nd" :
  152. ext = fileExt;
  153. break;
  154. case "nd2" :
  155. ext = fileExt;
  156. break;
  157. case "lif" :
  158. ext = fileExt;
  159. break;
  160. case "czi" :
  161. ext = fileExt;
  162. break;
  163. case "ics" :
  164. ext = fileExt;
  165. break;
  166. case "ics2" :
  167. ext = fileExt;
  168. break;
  169. case "lsm" :
  170. ext = fileExt;
  171. break;
  172. case "tif" :
  173. ext = fileExt;
  174. break;
  175. case "tiff" :
  176. ext = fileExt;
  177. break;
  178. }
  179. }
  180. return(ext);
  181. }
  182. /**
  183. * Get images with given extension in folder
  184. */
  185. public ArrayList<String> findImages(String imagesFolder, String imageExt) {
  186. File inDir = new File(imagesFolder);
  187. String[] files = inDir.list();
  188. ArrayList<String> images = new ArrayList();
  189. for (String f : files) {
  190. String fileExt = FilenameUtils.getExtension(f);
  191. if (fileExt.equals(imageExt) && !f.startsWith("."))
  192. images.add(imagesFolder + f);
  193. }
  194. Collections.sort(images);
  195. return(images);
  196. }
  197. /**
  198. * Get image calibration
  199. */
  200. public void findImageCalib(IMetadata meta) {
  201. cal = new Calibration();
  202. cal.pixelWidth = meta.getPixelsPhysicalSizeX(0).value().doubleValue();
  203. cal.pixelHeight = cal.pixelWidth;
  204. if (meta.getPixelsPhysicalSizeZ(0) != null)
  205. cal.pixelDepth = meta.getPixelsPhysicalSizeZ(0).value().doubleValue();
  206. else
  207. cal.pixelDepth = 1;
  208. cal.setUnit("microns");
  209. System.out.println("XY calibration = " + cal.pixelWidth + ", Z calibration = " + cal.pixelDepth);
  210. }
  211. /**
  212. * Get channels name and add None at the end of channels list
  213. * @throws loci.common.services.DependencyException
  214. * @throws loci.common.services.ServiceException
  215. * @throws loci.formats.FormatException
  216. * @throws java.io.IOException
  217. */
  218. public String[] findChannels(String imageName, IMetadata meta, ImageProcessorReader reader) throws DependencyException, ServiceException, FormatException, IOException {
  219. int chs = reader.getSizeC();
  220. String[] channels = new String[chs+1];
  221. String imageExt = FilenameUtils.getExtension(imageName);
  222. switch (imageExt) {
  223. case "nd" :
  224. for (int n = 0; n < chs; n++)
  225. channels[n] = (meta.getChannelName(0, n).toString().equals("")) ? Integer.toString(n) : meta.getChannelName(0, n).toString();
  226. break;
  227. case "nd2" :
  228. for (int n = 0; n < chs; n++)
  229. channels[n] = (meta.getChannelName(0, n).toString().equals("")) ? Integer.toString(n) : meta.getChannelName(0, n).toString();
  230. break;
  231. case "lif" :
  232. for (int n = 0; n < chs; n++)
  233. if (meta.getChannelID(0, n) == null || meta.getChannelName(0, n) == null)
  234. channels[n] = Integer.toString(n);
  235. else
  236. channels[n] = meta.getChannelName(0, n).toString();
  237. break;
  238. case "czi" :
  239. for (int n = 0; n < chs; n++)
  240. channels[n] = (meta.getChannelFluor(0, n).toString().equals("")) ? Integer.toString(n) : meta.getChannelFluor(0, n).toString();
  241. break;
  242. case "ics" :
  243. for (int n = 0; n < chs; n++)
  244. channels[n] = meta.getChannelEmissionWavelength(0, n).value().toString();
  245. break;
  246. case "ics2" :
  247. for (int n = 0; n < chs; n++)
  248. channels[n] = meta.getChannelEmissionWavelength(0, n).value().toString();
  249. break;
  250. default :
  251. for (int n = 0; n < chs; n++)
  252. channels[n] = Integer.toString(n);
  253. }
  254. channels[chs] = "None";
  255. return(channels);
  256. }
  257. /**
  258. * Generate dialog box
  259. */
  260. public String[] dialog(String[] chMeta) {
  261. GenericDialogPlus gd = new GenericDialogPlus("Parameters");
  262. gd.setInsets​(0, 180, 0);
  263. gd.addImage(icon);
  264. gd.addMessage("Channels", new Font("Monospace", Font.BOLD, 12), Color.blue);
  265. for (int n = 0; n < chDialog.length; n++)
  266. gd.addChoice(chDialog[n]+": ", chMeta, chMeta[n]);
  267. gd.addMessage("ROIs", new Font("Monospace", Font.BOLD, 12), Color.blue);
  268. gd.addNumericField("Scaling factor: ", roiScaling, 0);
  269. gd.addMessage("Astrocytes detection", new Font("Monospace", Font.BOLD, 12), Color.blue);
  270. gd.addStringField("Cellpose model: ", cellposeModel);
  271. gd.addToSameRow();
  272. gd.addNumericField("Cellpose diameter: ", cellposeDiam, 0);
  273. gd.addCheckbox("Delete single-slice cells", filterOneZ);
  274. gd.addNumericField("Min volume (µm3): ", minCellVol, 2);
  275. gd.addToSameRow();
  276. gd.addNumericField("Max volume (µm3): ", maxCellVol, 2);
  277. gd.addMessage("Astrocytes spatial distribution", new Font("Monospace", Font.BOLD, 12), Color.blue);
  278. gd.addNumericField("Neighbors nb: ", nbNei, 0);
  279. gd.addCheckbox("Compute G-function", computeGFunction);
  280. gd.addToSameRow();
  281. gd.addNumericField("Random samples nb: ", nbRandomSamples, 0);
  282. String[] methods = AutoThresholder.getMethods();
  283. gd.addMessage("Vessels segmentation", new Font("Monospace", Font.BOLD, 12), Color.blue);
  284. gd.addMessage("DoG filter 1", new Font("Monospace", Font.PLAIN, 12), Color.blue);
  285. gd.addNumericField("Sigma 1: ", dog1Sigma1, 2);
  286. gd.addToSameRow();
  287. gd.addNumericField("Sigma 2: ", dog1Sigma2, 2);
  288. gd.addChoice("Threshold: ", methods, vesselThMet1);
  289. gd.addMessage("DoG filter 2", new Font("Monospace", Font.PLAIN, 12), Color.blue);
  290. gd.addCheckbox("Apply filter", dog2);
  291. gd.addNumericField("Sigma 1: ", dog2Sigma1, 2);
  292. gd.addToSameRow();
  293. gd.addNumericField("Sigma 2: ", dog2Sigma2, 2);
  294. gd.addChoice("Threshold: ", methods, vesselThMet2);
  295. gd.addNumericField("Max hole area (µm2): ", maxHoleArea, 2);
  296. gd.addNumericField("Min volume (µm3): ", minVesselVol, 2);
  297. gd.addToSameRow();
  298. gd.addNumericField("Min length (µm): ", minVesselLength, 2);
  299. gd.addMessage("Image calibration", new Font("Monospace", Font.BOLD, 12), Color.blue);
  300. gd.addNumericField("XY pixel size (µm): ", cal.pixelWidth, 4);
  301. gd.addToSameRow();
  302. gd.addNumericField("Z pixel size (µm): ", cal.pixelDepth, 4);
  303. gd.addHelp(helpUrl);
  304. gd.showDialog();
  305. String[] chOrder = new String[chDialog.length];
  306. for (int n = 0; n < chOrder.length; n++)
  307. chOrder[n] = gd.getNextChoice();
  308. roiScaling = (int) gd.getNextNumber();
  309. if (roiScaling <= 0) {
  310. roiScaling = 1;
  311. print("ERROR: ROIs cannot be scaled by zero or negative values. ROI scaling factor set to 1.");
  312. }
  313. cellposeModel = gd.getNextString();
  314. cellposeDiam = (int) gd.getNextNumber();
  315. filterOneZ = gd.getNextBoolean();
  316. minCellVol = gd.getNextNumber();
  317. maxCellVol = gd.getNextNumber();
  318. nbNei = (int)gd.getNextNumber();
  319. computeGFunction = gd.getNextBoolean();
  320. nbRandomSamples = (int)gd.getNextNumber();
  321. dog1Sigma1 = (int) gd.getNextNumber();
  322. dog1Sigma2 = (int) gd.getNextNumber();
  323. vesselThMet1 = gd.getNextChoice();
  324. dog2 = gd.getNextBoolean();
  325. dog2Sigma1 = (int) gd.getNextNumber();
  326. dog2Sigma2 = (int) gd.getNextNumber();
  327. vesselThMet2 = gd.getNextChoice();
  328. maxHoleArea = gd.getNextNumber();
  329. minVesselVol = gd.getNextNumber();
  330. minVesselLength = gd.getNextNumber();
  331. cal.pixelWidth = cal.pixelHeight = gd.getNextNumber();
  332. cal.pixelDepth = gd.getNextNumber();
  333. pixelVol = cal.pixelWidth * cal.pixelHeight * cal.pixelDepth;
  334. if (gd.wasCanceled())
  335. chOrder = null;
  336. return(chOrder);
  337. }
  338. /**
  339. * Save images specific channel before sending it to QuantileBasedNormalization plugin
  340. * @throws Exception
  341. */
  342. public void saveChannel(ArrayList<String> imgFiles, int series, int channel, String normDir, String extension) throws Exception {
  343. try {
  344. for (String f : imgFiles) {
  345. String imgName = FilenameUtils.getBaseName(f);
  346. ImporterOptions options = new ImporterOptions();
  347. options.setId(f);
  348. options.setSplitChannels(true);
  349. options.setColorMode(ImporterOptions.COLOR_MODE_GRAYSCALE);
  350. options.setQuiet(true);
  351. // Open and save vessels channel
  352. ImagePlus imgVessels = openChannel(options, series, channel);
  353. IJ.saveAs(imgVessels, "Tiff", normDir+imgName+extension);
  354. closeImage(imgVessels);
  355. }
  356. } catch (Exception e) {
  357. throw e;
  358. }
  359. }
  360. public ImagePlus openChannel(ImporterOptions options, int series, int channel) throws FormatException, IOException {
  361. options.setCBegin(series, channel);
  362. options.setCEnd(series, channel);
  363. ImagePlus img = BF.openImagePlus(options)[0];
  364. return(img);
  365. }
  366. /**
  367. * Delete images specific channel after it was sent to QuantileBasedNormalization plugin
  368. * @throws Exception
  369. */
  370. public void deleteChannel(String dir, ArrayList<String> imageFiles, String extension) throws Exception {
  371. try {
  372. for (String f : imageFiles) {
  373. String rootName = FilenameUtils.getBaseName(f);
  374. new File(dir+rootName+extension).delete();
  375. }
  376. } catch (Exception e) {
  377. throw e;
  378. }
  379. }
  380. public List<Roi> loadRois(String imageDir, String imgName) {
  381. String roiName = imageDir+imgName+".zip";
  382. if (! new File(roiName).exists()) {
  383. print("ERROR: No ROI file found for image "+imgName+". Image not analyzed.");
  384. return(null);
  385. } else {
  386. RoiManager rm = new RoiManager(false);
  387. rm.runCommand("Open", roiName);
  388. List<Roi> rois = Arrays.asList(rm.getRoisAsArray());
  389. for (Roi roi : rois) {
  390. if(roi.getName().split(" ").length != 2) {
  391. print("ERROR: ROIs not correctly named in "+imgName +". Image not analyzed. Expected format: \"position layerName\" (e.g. \"0095-0429 l2\").");
  392. return(null);
  393. }
  394. }
  395. return(rois);
  396. }
  397. }
  398. /**
  399. * Scale ROIs
  400. */
  401. public List<Roi> scaleRois(List<Roi> rois, int scale, IMetadata meta, String imgName) {
  402. List<Roi> scaledRois = new ArrayList<Roi>();
  403. for (Roi roi : rois) {
  404. Roi scaledRoi = new RoiScaler().scale(roi, scale, scale, false);
  405. Rectangle rect = roi.getBounds();
  406. scaledRoi.setLocation(rect.x*scale, rect.y*scale);
  407. scaledRoi.setName(roi.getName());
  408. scaledRois.add(scaledRoi);
  409. }
  410. for (Roi roi : scaledRois) {
  411. Rectangle rect = roi.getBounds();
  412. if(rect.x < 0 || rect.x < 0 || rect.x+rect.width > meta.getPixelsSizeX(0).getValue() || rect.y+rect.height > meta.getPixelsSizeY(0).getValue()) {
  413. print("ERROR: ROIs exceed image size in "+imgName +". Image not analyzed. Check scaling factor.");
  414. return(null);
  415. }
  416. }
  417. return scaledRois;
  418. }
  419. /**
  420. * Get bounding box of multiple dilated ROIs combined together
  421. */
  422. public Roi getBoundingBox(List<Roi> rois) {
  423. Rectangle rect0 = RoiEnlarger.enlarge(rois.get(0), roiDilation/cal.pixelWidth).getBounds();
  424. int minX = rect0.x, minY = rect0.y, maxX = rect0.x+rect0.width, maxY = rect0.y+rect0.height;
  425. for (Roi roi : rois) {
  426. Rectangle rect = RoiEnlarger.enlarge(roi, roiDilation/cal.pixelWidth).getBounds();
  427. if(rect.x < minX) minX = rect.x;
  428. if(rect.y < minY) minY = rect.y;
  429. if(rect.x+rect.width > maxX) maxX = rect.x+rect.width;
  430. if(rect.y+rect.height > maxY) maxY = rect.y+rect.height;
  431. }
  432. minX = Math.max(minX, 0);
  433. minY = Math.max(minY, 0);
  434. return new Roi(minX, minY, maxX-minX, maxY-minY);
  435. }
  436. /**
  437. * Translate ROIs
  438. */
  439. public void translateRois(List<Roi> rois, Roi bBox) {
  440. Rectangle rectBBox = bBox.getBounds();
  441. for (Roi roi: rois) {
  442. Rectangle rectRoi = roi.getBounds();
  443. roi.setLocation(rectRoi.x-rectBBox.x, rectRoi.y-rectBBox.y);
  444. }
  445. }
  446. /**
  447. * Save ROIs in a zip file
  448. */
  449. public void saveRois(List<Roi> rois, String outDir, String imgName) {
  450. RoiManager rm = new RoiManager(false);
  451. for(Roi roi: rois) {
  452. String layerName = roi.getName().split(" ")[1];
  453. rm.addRoi(roi);
  454. String roiName = rm.getName(rm.getCount()-1) + " " + layerName;
  455. rm.rename(rm.getCount()-1, roiName);
  456. }
  457. rm.deselect();
  458. rm.save(outDir+imgName+"_rois.zip");
  459. rm.close();
  460. }
  461. /**
  462. * Crop stack around ROI
  463. */
  464. public ImagePlus cropImage(ImagePlus img, Roi roi) {
  465. img.setRoi(roi);
  466. ImagePlus imgCrop = img.crop("stack");
  467. img.deleteRoi();
  468. return(imgCrop);
  469. }
  470. /*
  471. * Look for all 3D cells in a Z-stack:
  472. * - apply Cellpose 2D slice by slice
  473. * - let CellPose reconstruct cells in 3D using the stitch_threshold parameter
  474. */
  475. public ImagePlus cellposeDetection(ImagePlus imgIn) throws IOException{
  476. ImagePlus img = imgIn.duplicate();
  477. // Define Cellpose settings
  478. CellposeTaskSettings settings = new CellposeTaskSettings(cellposeModel.equals("cyto")? cellposeModel : cellposeModelDir+cellposeModel, 1, cellposeDiam, cellposeEnvDir); // need to add Cellpose models folder path if own model (for Windows only, not Linux)
  479. settings.setStitchThreshold(cellposeStitchTh);
  480. settings.useGpu(true);
  481. // Run Cellpose
  482. CellposeSegmentImgPlusAdvanced cellpose = new CellposeSegmentImgPlusAdvanced(settings, img);
  483. ImagePlus imgOut = cellpose.run();
  484. if(imgOut.getNChannels() > 1)
  485. imgOut.setDimensions(1, imgOut.getNChannels(), 1);
  486. imgOut.setCalibration(cal);
  487. closeImage(img);
  488. return imgOut;
  489. }
  490. /**
  491. * Detect vessels applying a median filter + DoG filter + threshold + closing filter + median filter
  492. */
  493. public ImagePlus vesselsSegmentation(ImagePlus img) {
  494. ImagePlus imgMed = medianFilter3D(img, 4, 1);
  495. ImagePlus imgDOG = dogFilter2D(imgMed, dog1Sigma1, dog1Sigma2);
  496. ImagePlus imgBin = threshold(imgDOG, vesselThMet1);
  497. if(dog2) {
  498. ImagePlus imgDOG2 = dogFilter2D(imgMed, dog2Sigma1, dog2Sigma2);
  499. ImagePlus imgBin2 = threshold(imgDOG2, vesselThMet2);
  500. new ImageCalculator().run("Max stack", imgBin, imgBin2);
  501. closeImage(imgDOG2);
  502. closeImage(imgBin2);
  503. }
  504. ImagePlus imgClose = closingFilter3D(imgBin, 8, 1);
  505. ImagePlus imgMed2 = medianFilter3D(imgClose, 1, 1);
  506. ImagePlus imgOut = fillHoles2D(imgMed2, 0, maxHoleArea);
  507. ImageInt imgLabels = new ImageLabeller().getLabels(ImageHandler.wrap(imgOut));
  508. imgLabels.setCalibration(cal);
  509. Objects3DIntPopulation pop = new Objects3DIntPopulation(imgLabels);
  510. popFilterOneZ(pop);
  511. System.out.println(pop.getNbObjects() + " vessels detected");
  512. popFilterVol(pop, minVesselVol, Double.MAX_VALUE);
  513. System.out.println(pop.getNbObjects() + " vessels remaining after size filtering");
  514. ImageHandler imgFilter = ImageHandler.wrap(img).createSameDimensions();
  515. for(Object3DInt obj: pop.getObjects3DInt())
  516. obj.drawObject(imgFilter, 255);
  517. imgFilter.setCalibration(cal);
  518. closeImage(imgMed);
  519. closeImage(imgDOG);
  520. closeImage(imgBin);
  521. closeImage(imgClose);
  522. closeImage(imgMed2);
  523. closeImage(imgOut);
  524. closeImage(imgLabels.getImagePlus());
  525. return(imgFilter.getImagePlus());
  526. }
  527. /**
  528. * Median filter using CLIJ
  529. */
  530. public ImagePlus medianFilter3D(ImagePlus img, double sizeXY, double sizeZ) {
  531. ClearCLBuffer imgCL = clij2.push(img);
  532. ClearCLBuffer imgCLMed = clij2.create(imgCL);
  533. clij2.median3DSphere(imgCL, imgCLMed, sizeXY, sizeXY, sizeZ);
  534. clij2.release(imgCL);
  535. ImagePlus imgMed = clij2.pull(imgCLMed);
  536. clij2.release(imgCLMed);
  537. return(imgMed);
  538. }
  539. /**
  540. * Difference of Gaussians using CLIJ
  541. */
  542. public ImagePlus dogFilter2D(ImagePlus img, double size1, double size2) {
  543. ClearCLBuffer imgCL = clij2.push(img);
  544. ClearCLBuffer imgCLDOG = clij2.create(imgCL);
  545. clij2.differenceOfGaussian2D(imgCL, imgCLDOG, size1, size1, size2, size2);
  546. clij2.release(imgCL);
  547. ImagePlus imgDOG = clij2.pull(imgCLDOG);
  548. clij2.release(imgCLDOG);
  549. return(imgDOG);
  550. }
  551. /**
  552. * Automatic thresholding using CLIJ2
  553. */
  554. private ImagePlus threshold(ImagePlus img, String thMed) {
  555. ClearCLBuffer imgCL = clij2.push(img);
  556. ClearCLBuffer imgCLBin = clij2.create(imgCL);
  557. clij2.automaticThreshold(imgCL, imgCLBin, thMed);
  558. ImagePlus imgBin = clij2.pull(imgCLBin);
  559. clij2.release(imgCL);
  560. clij2.release(imgCLBin);
  561. return(imgBin);
  562. }
  563. /**
  564. * Closing filtering using CLIJ2
  565. */
  566. private ImagePlus closingFilter3D(ImagePlus img, double sizeXY, double sizeZ) {
  567. ClearCLBuffer imgCL = clij2.push(img);
  568. ClearCLBuffer imgCLMax = clij2.create(imgCL);
  569. clij2.maximum3DSphere(imgCL, imgCLMax, sizeXY, sizeXY, sizeZ);
  570. ClearCLBuffer imgCLMin = clij2.create(imgCLMax);
  571. clij2.minimum3DSphere(imgCLMax, imgCLMin, sizeXY, sizeXY, sizeZ);
  572. ImagePlus imgMin = clij2.pull(imgCLMin);
  573. clij2.release(imgCL);
  574. clij2.release(imgCLMax);
  575. clij2.release(imgCLMin);
  576. return(imgMin);
  577. }
  578. /**
  579. * Fill holes with areas between the specified min and max values
  580. */
  581. private ImagePlus fillHoles2D(ImagePlus img, double minArea, double maxArea) {
  582. ImagePlus imgFill = img.duplicate();
  583. // Invert image to detect background holes
  584. IJ.setRawThreshold(imgFill, 1, 255);
  585. IJ.run(imgFill, "Convert to Mask", "background=Dark black");
  586. IJ.run(imgFill, "Invert", "stack");
  587. // Analyze particles to detect holes within the specified area range
  588. double pixArea = cal.pixelWidth*cal.pixelHeight;
  589. ParticleAnalyzer pa = new ParticleAnalyzer(ParticleAnalyzer.ADD_TO_MANAGER, 0, new ResultsTable(), minArea/pixArea, maxArea/pixArea); // In pixels^2
  590. RoiManager rm = new RoiManager(true);
  591. pa.setRoiManager(rm);
  592. for (int s = 1; s <= imgFill.getNSlices(); s++) {
  593. imgFill.setSlice(s);
  594. pa.analyze(imgFill);
  595. // Reinvert image before filling holes
  596. IJ.run(imgFill, "Invert", "slice");
  597. // Fill the detected holes
  598. for (int i = 0; i < rm.getCount(); i++) {
  599. imgFill.setRoi(rm.getRoi(i));
  600. imgFill.setColor(Color.white);
  601. IJ.run(imgFill, "Fill", "slice");
  602. }
  603. imgFill.deleteRoi();
  604. rm.reset();
  605. }
  606. return(imgFill);
  607. }
  608. /**
  609. * Compute (inverse) 3D distance map
  610. */
  611. public ImageFloat distanceMap3D(ImagePlus img, boolean inverse) {
  612. img.setCalibration(cal);
  613. ImageFloat edt = new EDT().run(ImageHandler.wrap(img), 0, inverse, ThreadUtil.getNbCpus());
  614. return(edt);
  615. }
  616. /**
  617. * Skeletonize 3D with CLIJ2
  618. */
  619. public ImagePlus skeletonize3D(ImagePlus img) {
  620. ClearCLBuffer imgCL = clij2.push(img);
  621. ClearCLBuffer imgCLSkel = clij2.create(imgCL);
  622. new BoneJSkeletonize3D().bonejSkeletonize3D(clij2, imgCL, imgCLSkel);
  623. ImagePlus imgSkel = clij2.pull(imgCLSkel);
  624. clij2.release(imgCL);
  625. clij2.release(imgCLSkel);
  626. IJ.run(imgSkel, "8-bit","");
  627. imgSkel.setCalibration(cal);
  628. return(imgSkel);
  629. }
  630. /**
  631. * Prune skeleton branches with length smaller than threshold
  632. * https://imagej.net/plugins/analyze-skeleton/
  633. */
  634. public ImagePlus pruneSkeleton(ImagePlus image) {
  635. // Analyze skeleton
  636. AnalyzeSkeleton_ skel = new AnalyzeSkeleton_();
  637. skel.setup("", image);
  638. SkeletonResult skelResult = skel.run(AnalyzeSkeleton_.NONE, false, false, null, true, false);
  639. // Create copy of input image
  640. ImagePlus prunedImage = image.duplicate();
  641. if(skelResult.getBranches() != null) {
  642. ImageStack outStack = prunedImage.getStack();
  643. // Get graphs (one per skeleton in the image)
  644. Graph[] graphs = skelResult.getGraph();
  645. // Get list of end-points
  646. ArrayList<Point> endPoints = skelResult.getListOfEndPoints();
  647. for(Graph graph: graphs) {
  648. ArrayList<Edge> listEdges = graph.getEdges();
  649. // Go through all branches and remove branches under threshold in duplicate image
  650. for(Edge e: listEdges) {
  651. ArrayList<Point> p1 = e.getV1().getPoints();
  652. boolean v1End = endPoints.contains(p1.get(0));
  653. ArrayList<Point> p2 = e.getV2().getPoints();
  654. boolean v2End = endPoints.contains(p2.get(0));
  655. // If any of the vertices is end-point
  656. if(v1End || v2End) {
  657. if(e.getLength() < minVesselLength) { // in microns
  658. if(v1End)
  659. outStack.setVoxel(p1.get(0).x, p1.get(0).y, p1.get(0).z, 0);
  660. if(v2End)
  661. outStack.setVoxel(p2.get(0).x, p2.get(0).y, p2.get(0).z, 0);
  662. for(Point p: e.getSlabs())
  663. outStack.setVoxel(p.x, p.y, p.z, 0);
  664. }
  665. }
  666. }
  667. }
  668. }
  669. prunedImage = skeletonize3D(prunedImage);
  670. return(prunedImage);
  671. }
  672. /**
  673. * Get object in (dilated) ROI
  674. * @param dilationFactor in microns
  675. */
  676. public ImagePlus getImgInRoi(ImagePlus img, Roi roi, double dilationFactor) {
  677. ImagePlus imgClear = img.duplicate();
  678. imgClear.setRoi(dilationFactor == 0? roi : RoiEnlarger.enlarge(roi, dilationFactor/cal.pixelWidth)); // pixels
  679. IJ.run(imgClear, "Clear Outside", "stack");
  680. imgClear.setCalibration(cal);
  681. return(imgClear);
  682. }
  683. /**
  684. * Get object in (dilated) ROI
  685. */
  686. public Object3DInt getObjInRoi(ImagePlus img, Roi roi, double dilationFactor) {
  687. ImagePlus imgClear = getImgInRoi(img, roi, dilationFactor);
  688. Object3DInt obj = new Object3DInt(ImageHandler.wrap(imgClear));
  689. closeImage(imgClear);
  690. return(obj);
  691. }
  692. /**
  693. * Return population in ROI
  694. */
  695. public Objects3DIntPopulation getPopInRoi(ImagePlus img, Roi roi) throws IOException{
  696. ImagePlus imgClear = getImgInRoi(img, roi, 20);
  697. // Filter detections
  698. Objects3DIntPopulation pop = new Objects3DIntPopulation(ImageInt.wrap(imgClear));
  699. if(filterOneZ) popFilterOneZ(pop);
  700. popFilterCentroid(pop, roi);
  701. System.out.println(pop.getNbObjects() + " cells detected in ROI");
  702. popFilterVol(pop, minCellVol, maxCellVol);
  703. System.out.println(pop.getNbObjects() + " cells remaining after size filtering");
  704. pop.resetLabels();
  705. closeImage(imgClear);
  706. return(pop);
  707. }
  708. /**
  709. * Remove objects that appear in only one z-slice
  710. */
  711. private void popFilterOneZ(Objects3DIntPopulation pop) {
  712. pop.getObjects3DInt().removeIf(p -> (p.getObject3DPlanes().size() == 1));
  713. pop.resetLabels();
  714. }
  715. /**
  716. * Remove objects that do not have their centroid into a given ROI
  717. */
  718. public void popFilterCentroid(Objects3DIntPopulation pop, Roi roi) {
  719. pop.getObjects3DInt().removeIf(p -> (!roi.contains(new MeasureCentroid(p).getCentroidAsPoint().getRoundX(),
  720. new MeasureCentroid(p).getCentroidAsPoint().getRoundY())));
  721. pop.resetLabels();
  722. }
  723. /**
  724. * Remove objects with volume < min and volume > max
  725. */
  726. private void popFilterVol(Objects3DIntPopulation pop, double min, double max) {
  727. pop.getObjects3DInt().removeIf(p -> (new MeasureVolume(p).getVolumeUnit() < min) || (new MeasureVolume(p).getVolumeUnit() > max));
  728. pop.resetLabels();
  729. }
  730. /**
  731. * Remove objects with intensity < min
  732. */
  733. public void popFilterInt(Objects3DIntPopulation pop, ImagePlus img, double minInt) {
  734. ImageHandler imh = ImageHandler.wrap(img);
  735. pop.getObjects3DInt().removeIf(p -> (new MeasureIntensity(p, imh).getValueMeasurement(MeasureIntensity.INTENSITY_AVG) < minInt));
  736. pop.resetLabels();
  737. }
  738. /**
  739. * Compute distance between each cell and its closest vessel
  740. */
  741. public void computeCellsVesselsDists(Objects3DIntPopulation cellPop, ImageFloat vesselDistMapInv) {
  742. for (Object3DInt cell: cellPop.getObjects3DInt()) {
  743. double dist = vesselDistMapInv.getPixel(new MeasureCentroid​(cell).getCentroidAsPoint());
  744. cell.setCompareValue(dist);
  745. }
  746. }
  747. /**
  748. * Draw results obtained in ROI
  749. */
  750. public void drawResultsInRoi(Objects3DIntPopulation popCell, Object3DInt objVessel, Object3DInt objSkel, ImageHandler imhCell,
  751. ImageHandler imhCellDist, ImageHandler imhVessel, ImageHandler imhSkel) {
  752. // Cells
  753. popCell.drawInImage(imhCell);
  754. // Cells + vessels
  755. if (objVessel != null) {
  756. for (Object3DInt cell: popCell.getObjects3DInt()) {
  757. cell.drawObject(imhCellDist, (float)cell.getCompareValue()+10);
  758. }
  759. objVessel.drawObject(imhVessel, 255);
  760. objSkel.drawObject(imhSkel, 255);
  761. }
  762. }
  763. /**
  764. * Save results in images
  765. */
  766. public void saveCloseDrawings(ImagePlus imgCell, ImagePlus imgVessel, ImageHandler imhCell, ImageHandler imhCellDist,
  767. ImageHandler imhVessel, ImageHandler imhSkel, String outDir, String imgName) {
  768. // Cells
  769. ImagePlus[] imgColors1 = {imhCell.getImagePlus(), null, null, imgCell};
  770. ImagePlus imgObjects1 = new RGBStackMerge().mergeHyperstacks(imgColors1, true);
  771. imgObjects1.setCalibration(cal);
  772. new FileSaver(imgObjects1).saveAsTiff(outDir+imgName+"_cells.tif");
  773. closeImage(imgObjects1);
  774. // Cells + vessels
  775. if (imgVessel != null) {
  776. IJ.run(imhCellDist.getImagePlus(), "mpl-inferno", "");
  777. IJ.run(imhSkel.getImagePlus(), "Green", "");
  778. IJ.run(imhVessel.getImagePlus(), "Blue", "");
  779. ImagePlus[] imgColors2 = {imhCellDist.getImagePlus(), imhSkel.getImagePlus(), imhVessel.getImagePlus(), imgVessel};
  780. ImagePlus imgObjects2 = new RGBStackMerge().mergeHyperstacks(imgColors2, true);
  781. imgObjects2.setCalibration(cal);
  782. new FileSaver(imgObjects2).saveAsTiff(outDir+imgName+"_vessels.tif");
  783. closeImage(imgObjects2);
  784. }
  785. closeImage(imhCell.getImagePlus());
  786. closeImage(imhCellDist.getImagePlus());
  787. closeImage(imhVessel.getImagePlus());
  788. closeImage(imhSkel.getImagePlus());
  789. }
  790. /**
  791. * Write headers in results files
  792. */
  793. public void writeHeaders(String outDir, boolean computeVessel) throws IOException {
  794. // Global results
  795. FileWriter fileGlobal = new FileWriter(outDir + "globalResults.csv", false);
  796. resultsGlobal = new BufferedWriter(fileGlobal);
  797. resultsGlobal.write("Image name\tROI name\tROI area (µm²)\tROI volume (µm³)\tNb cells\tCells total volume (µm³)\t"
  798. + "Cells mean volume (µm³)\tCells volume SD (µm³)\tCells mean distance to closest neighbor (µm)\t"
  799. + "Cells distance to closest neighbor SD (µm)\tCells mean distance to "+nbNei+" closest neighbors (µm)\t"
  800. + "Cells distance to "+nbNei+" closest neighbors SD (µm)"+"\tCells mean of max distance to "+nbNei+" neighbors (µm)\t"
  801. + "Cells SD of max distance to "+nbNei+" neighbors (µm)");
  802. if (computeGFunction) resultsGlobal.write("\tCells G-function SDI\tCells G-function AUC difference");
  803. if (computeVessel) resultsGlobal.write("\tCells mean distance to closest vessel (µm)\tVessels total volume (µm³)"
  804. + "\tVessels total length (µm)\tBranches mean length (µm)\tNb branches\tNb junctions\tVessels mean diameter (µm)\tVessels diameter SD (µm)");
  805. resultsGlobal.write("\n");
  806. resultsGlobal.flush();
  807. // Detailed results
  808. FileWriter fileDetail = new FileWriter(outDir +"cellsResults.csv", false);
  809. resultsDetail = new BufferedWriter(fileDetail);
  810. resultsDetail.write("Image name\tROI name\tCell ID\tCell volume (µm³)\tCell distance to closest neighbor (µm)\t"
  811. + "Cell mean distance to "+nbNei+" closest neighbors (µm)\tCell max distance to "+nbNei+" closest neighbors (µm)");
  812. if(computeVessel) resultsDetail.write("\tCell distance to closest vessel (µm)\tClosest vessel diameter (µm)");
  813. resultsDetail.write("\n");
  814. resultsDetail.flush();
  815. }
  816. /**
  817. * Compute parameters and save them in results files
  818. * @throws java.io.IOException
  819. */
  820. public void writeResultsInRoi(Objects3DIntPopulation popCellRoi, Object3DInt objSkelRoiDil, Object3DInt objSkelRoi,
  821. ImageFloat distMap, ImagePlus imgCell, Roi roi, double vesselVol, String outDir, String imgName) throws IOException {
  822. DescriptiveStatistics cellsVolume = new DescriptiveStatistics();
  823. DescriptiveStatistics cellsClosestVesselDist = new DescriptiveStatistics();
  824. DescriptiveStatistics cellsClosestNeighborDist = new DescriptiveStatistics();
  825. DescriptiveStatistics cellsNeighborsMeanDist = new DescriptiveStatistics();
  826. DescriptiveStatistics cellsNeighborsMaxDist = new DescriptiveStatistics();
  827. // CELLS INDIVIDUAL STATISTICS
  828. print("Computing cells individual statistics...");
  829. MeasurePopulationDistance allCellsDists = new MeasurePopulationDistance​(popCellRoi, popCellRoi, Double.POSITIVE_INFINITY, "DistCenterCenterUnit");
  830. for (Object3DInt cell: popCellRoi.getObjects3DInt()) {
  831. double cellVol = new MeasureVolume(cell).getVolumeUnit();
  832. cellsVolume.addValue(cellVol);
  833. resultsDetail.write(imgName+"\t"+roi.getName()+"\t"+cell.getLabel()+"\t"+cellVol);
  834. resultsDetail.flush();
  835. if(popCellRoi.getNbObjects() == 1) {
  836. resultsDetail.write("\t"+Double.NaN+"\t"+Double.NaN+"\t"+Double.NaN);
  837. resultsDetail.flush();
  838. } else {
  839. List<PairObjects3DInt> cellCellsDists = allCellsDists.getPairsObject1(cell.getLabel(), true);
  840. double closestNeighborDist = cellCellsDists.get(1).getPairValue();
  841. cellsClosestNeighborDist.addValue(closestNeighborDist);
  842. DescriptiveStatistics cellNeighborsDists = new DescriptiveStatistics();
  843. for (int d=1; d <= Math.min(nbNei, popCellRoi.getNbObjects()-1); d++)
  844. cellNeighborsDists.addValue(cellCellsDists.get(d).getPairValue());
  845. double closestNeighborsMeanDist = cellNeighborsDists.getMean();
  846. cellsNeighborsMeanDist.addValue(closestNeighborsMeanDist);
  847. double closestNeighborsMaxDist = cellNeighborsDists.getMax();
  848. cellsNeighborsMaxDist.addValue(closestNeighborsMaxDist);
  849. resultsDetail.write("\t"+closestNeighborDist+"\t"+closestNeighborsMeanDist+"\t"+closestNeighborsMaxDist);
  850. resultsDetail.flush();
  851. }
  852. cellsClosestVesselDist.addValue(cell.getCompareValue());
  853. if (objSkelRoiDil != null) {
  854. double diam = 2*distMap.getPixel(new Measure2Distance(cell, objSkelRoiDil).getBorder2Pix());
  855. resultsDetail.write("\t"+cell.getCompareValue()+"\t"+diam);
  856. resultsDetail.flush();
  857. }
  858. resultsDetail.write("\n");
  859. resultsDetail.flush();
  860. }
  861. // CELLS GLOBAL STATISTICS
  862. print("Computing cells global statistics...");
  863. double[] roiParams = roiParams(roi, imgCell);
  864. resultsGlobal.write(imgName+"\t"+roi.getName()+"\t"+roiParams[0]+"\t"+roiParams[1]+"\t"+popCellRoi.getNbObjects()+"\t"+
  865. cellsVolume.getSum()+"\t"+cellsVolume.getMean()+"\t"+cellsVolume.getStandardDeviation()+"\t"+
  866. cellsClosestNeighborDist.getMean()+"\t"+cellsClosestNeighborDist.getStandardDeviation()+"\t"+
  867. cellsNeighborsMeanDist.getMean()+"\t"+cellsNeighborsMeanDist.getStandardDeviation()+"\t"+
  868. cellsNeighborsMaxDist.getMean()+"\t"+cellsNeighborsMaxDist.getStandardDeviation());
  869. if (computeGFunction) {
  870. if(popCellRoi.getNbObjects() <= 1) {
  871. resultsGlobal.write("\t"+Double.NaN+"\t"+Double.NaN);
  872. resultsGlobal.flush();
  873. } else {
  874. System.out.println("Computing G-function-related spatial distribution index...");
  875. Object3DInt mask = roiMask(imgCell, roi);
  876. double minDist = Math.pow(3*minCellVol/(4*Math.PI*pixelVol), 1/3) * 2; // min distance = 2 * min cell radius (in pixels)
  877. String plotName = outDir + imgName + "_" + roi.getName() + "_gfunction.tif";
  878. double[] res = computeSdiG(popCellRoi, mask, imgCell, minDist, nbRandomSamples, plotName);
  879. resultsGlobal.write("\t"+res[0]+"\t"+res[1]);
  880. resultsGlobal.flush();
  881. }
  882. }
  883. // VESSELS STATISTICS
  884. if(objSkelRoi == null) {
  885. resultsGlobal.write("\t0\t0\t0\t0\t0\t0\t0\t0");
  886. resultsGlobal.flush();
  887. } else {
  888. print("Computing vessels statistics...");
  889. ImageHandler imhSkel = ImageHandler.wrap(imgCell).createSameDimensions();
  890. objSkelRoi.drawObject(imhSkel, 255);
  891. IJ.run(imhSkel.getImagePlus(), "8-bit","");
  892. AnalyzeSkeleton_ analyzeSkeleton = new AnalyzeSkeleton_();
  893. analyzeSkeleton.setup("", imhSkel.getImagePlus());
  894. SkeletonResult skelResult = analyzeSkeleton.run(AnalyzeSkeleton_.NONE, false, false, null, true, false);
  895. closeImage(imhSkel.getImagePlus());
  896. if(skelResult.getBranches() == null) {
  897. resultsGlobal.write("\t0\t0\t0\t0\t0\t0\t0\t0");
  898. resultsGlobal.flush();
  899. } else {
  900. double[] branchLengths = skelResult.getAverageBranchLength();
  901. int[] branchNumbers = skelResult.getBranches();
  902. double totalLength = 0;
  903. for (int i = 0; i < branchNumbers.length; i++)
  904. totalLength += branchNumbers[i] * branchLengths[i];
  905. DescriptiveStatistics diams = new DescriptiveStatistics();
  906. for (Point pt: skelResult.getListOfSlabVoxels())
  907. diams.addValue(2*distMap.getPixel(pt.x, pt.y, pt.z));
  908. resultsGlobal.write("\t"+cellsClosestVesselDist.getMean()+"\t"+vesselVol+"\t"+totalLength+"\t"+StatUtils.mean(branchLengths)+"\t"+
  909. IntStream.of(branchNumbers).sum()+"\t"+IntStream.of(skelResult.getJunctions()).sum()+"\t"+
  910. diams.getMean()+"\t"+diams.getStandardDeviation());
  911. resultsGlobal.flush();
  912. }
  913. }
  914. resultsGlobal.write("\n");
  915. resultsGlobal.flush();
  916. }
  917. /**
  918. * Compute ROI area and volume
  919. */
  920. public double[] roiParams(Roi roi, ImagePlus img) {
  921. PolygonRoi poly = new PolygonRoi(roi.getFloatPolygon(), Roi.FREEROI);
  922. poly.setLocation(0, 0);
  923. img.setRoi(poly);
  924. ResultsTable rt = new ResultsTable();
  925. Analyzer analyzer = new Analyzer(img, Analyzer.AREA, rt);
  926. analyzer.measure();
  927. double area = rt.getValue("Area", 0);
  928. double vol = area * img.getNSlices() * cal.pixelDepth;
  929. double[] params = {area, vol};
  930. return(params);
  931. }
  932. // Get ROI as a 3D object
  933. public Object3DInt roiMask(ImagePlus img, Roi roi) {
  934. ImagePlus imgMask = img.duplicate();
  935. imgMask.setRoi(roi);
  936. for (int n = 1; n <= imgMask.getNSlices(); n++) {
  937. imgMask.setSlice(n);
  938. IJ.run(imgMask, "Fill", "stack");
  939. IJ.run(imgMask, "Clear Outside", "stack");
  940. }
  941. imgMask.setCalibration(cal);
  942. Object3DInt mask = new Object3DInt​(ImageHandler.wrap(imgMask));
  943. closeImage(imgMask);
  944. return(mask);
  945. }
  946. /**
  947. * Compute G-function-related Spatial Distribution Index of cells population in a ROI
  948. * https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1000853
  949. */
  950. public double[] computeSdiG(Objects3DIntPopulation popInt, Object3DInt roiInt, ImagePlus img, double distHardCore, int numRandomSamples, String plotName) {
  951. // Convert Object3DInt & Objects3DIntPopulation objects into Object3D & Objects3DPopulation objects
  952. ImageHandler imhRoi = ImageHandler.wrap(img).createSameDimensions();
  953. roiInt.drawObject(imhRoi, 1);
  954. Object3D roi = new Objects3DPopulation(imhRoi).getObject(0);
  955. closeImage(imhRoi.getImagePlus());
  956. ImageHandler imhPop = ImageHandler.wrap(img).createSameDimensions();
  957. popInt.drawInImage(imhPop);
  958. Objects3DPopulation pop = new Objects3DPopulation(imhPop);
  959. closeImage(imhPop.getImagePlus());
  960. // Define spatial descriptor and model
  961. SpatialDescriptor spatialDesc = new G_Function();
  962. SpatialModel spatialModel = new SpatialRandomHardCore(pop.getNbObjects(), distHardCore, roi); // average diameter of a cell in pixels
  963. SpatialStatistics spatialStatistics = new SpatialStatistics(spatialDesc, spatialModel, numRandomSamples, pop); // nb of samples (randomized organizations simulated to compare with the spatial organization of the cells)
  964. spatialStatistics.setEnvelope(0.05); // 2.5-97.5% envelope error
  965. spatialStatistics.setVerbose(false);
  966. double sdiG = spatialStatistics.getSdi();
  967. double areaG = spatialStatistics.getAUCDifference();
  968. Plot plotG = spatialStatistics.getPlot();
  969. plotG.draw();
  970. plotG.addLabel(0.05, 0.1, "SDI = " + String.format("%.5f", sdiG));
  971. plotG.addLabel(0.05, 0.15, "Area = " + String.format("%.5f", areaG));
  972. ImagePlus imgPlot = plotG.getImagePlus();
  973. FileSaver plotSave = new FileSaver(imgPlot);
  974. plotSave.saveAsTiff(plotName);
  975. closeImage(imgPlot);
  976. double[] results = {sdiG, areaG};
  977. return(results);
  978. }
  979. /**
  980. * Close results files
  981. */
  982. public void closeResults() throws IOException {
  983. resultsGlobal.close();
  984. resultsDetail.close();
  985. }
  986. }

Tools.java at commit a84b038, no license · at the source

Overview

  1. Center for Interdisciplinary Research in Biology (CIRB), Collège de France, CNRS, INSERM, Université Paris Sciences et Lettres (PSL), Paris, France
Dates: received 10 December 2025; accepted 2 July 2026; published online 28 July 2026; in print August 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1002/jdn.70161 · PMID 42517543 · PMCID PMC13411183 · OpenAlex W7171572316
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), mouse (organism)
Methods: Statistics, Machine learning, fMRI & imaging
Keywords: astrocyte, blood vessel, cell density, cell distribution, cortical development, oligodendrocyte
MeSH: Blood Vessels*, Neuroglia*, Somatosensory Cortex*, Age Factors, Animals, Animals, Newborn, Astrocytes, Female, Gene Expression Regulation, Developmental, Male, Mice, Mice, Inbred C57BL, Neurodevelopment, SOX9 Transcription Factor, SOXE Transcription Factors (* major topic)
Topic: Neurogenesis and neuroplasticity mechanisms (Developmental Neuroscience, Neuroscience), according to OpenAlex
Funding: Université PSL; France Sclérose en Plaques; ANR (ANR-23-CE16-0030); Fondation pour la Recherche Médicale (EQU202303016292)
Citations: not cited yet (Europe PMC); 52 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

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

orion-cirb/VeCell

License: none: the authors keep all their rights
State: the link answers, verified on 26 September 2026
Evidence: files inventoried
Commit: a84b0387457d39c2cd91b448712089fbbf9201c0, 10 December 2025
Languages: Java (8), Jupyter (2)
Size: 23 files, 10 scripts
Software Heritage: not archived
Found in: the text, “Image Analysis”
Holds: README, 2 notebooks
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Cellpose (2 files), Matplotlib (2 files), NumPy (2 files), pandas (2 files)
Availability: 1 check, the latest on 26 September 2026: the link answers
  • 26 September 2026: the link answers
11 files

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;
  • 10 scripts, each with its path and the digest of its content;
  • 4 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

No dataset and no data link were found in the paper.

Data availability statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • it says that the data are available on request

Read it in the paper: doi.org/10.1002/jdn.70161.

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 3, 28 September 2026

  • Publisher: n/a → Wiley

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 6 keywords, 15 MeSH terms, 4 funders, 51 references.

Cite

This paper

Guille, N., Monnet, H., Hourcade, T., Mailly, P., Cohen‐Salmon, M., & Boulay, A. (2026). Spatio-Temporal Dynamics of Macroglial Cell Organization and Proximity to Blood Vessels During Postnatal Development. International journal of developmental neuroscience : the official journal of the International Society for Developmental Neuroscience, 86(5), e70161. https://doi.org/10.1002/jdn.70161

BibTeX

@article{guille2026spatio,
author = {Guille, Naomie and Monnet, Héloïse and Hourcade, Tristan and Mailly, Philippe and Cohen‐Salmon, Martine and Boulay, Anne‐Cécile},
title = {{Spatio-Temporal Dynamics of Macroglial Cell Organization and Proximity to Blood Vessels During Postnatal Development}},
journal = {International journal of developmental neuroscience : the official journal of the International Society for Developmental Neuroscience},
year = {2026},
month = aug,
volume = {86},
number = {5},
pages = {e70161},
publisher = {Wiley},
issn = {0736-5748},
doi = {10.1002/jdn.70161},
url = {https://doi.org/10.1002/jdn.70161},
pmid = {42517543},
pmcid = {PMC13411183}
}

RIS

TY - JOUR
AU - Guille, Naomie
AU - Monnet, Héloïse
AU - Hourcade, Tristan
AU - Mailly, Philippe
AU - Cohen‐Salmon, Martine
AU - Boulay, Anne‐Cécile
TI - Spatio-Temporal Dynamics of Macroglial Cell Organization and Proximity to Blood Vessels During Postnatal Development
T2 - International journal of developmental neuroscience : the official journal of the International Society for Developmental Neuroscience
J2 - Int J Dev Neurosci
PY - 2026
DA - 2026/08/01
VL - 86
IS - 5
SP - e70161
SN - 0736-5748
PB - Wiley
DO - 10.1002/jdn.70161
UR - https://doi.org/10.1002/jdn.70161
LA - en
ER -

CSL-JSON

{
"id": "10.1002/jdn.70161",
"type": "article-journal",
"title": "Spatio-Temporal Dynamics of Macroglial Cell Organization and Proximity to Blood Vessels During Postnatal Development",
"container-title": "International journal of developmental neuroscience : the official journal of the International Society for Developmental Neuroscience",
"author": [
{
"family": "Guille",
"given": "Naomie"
},
{
"family": "Monnet",
"given": "Héloïse"
},
{
"family": "Hourcade",
"given": "Tristan"
},
{
"family": "Mailly",
"given": "Philippe"
},
{
"family": "Cohen‐Salmon",
"given": "Martine"
},
{
"family": "Boulay",
"given": "Anne‐Cécile"
}
],
"container-title-short": "Int J Dev Neurosci",
"volume": "86",
"issue": "5",
"page": "e70161",
"DOI": "10.1002/jdn.70161",
"PMID": "42517543",
"PMCID": "PMC13411183",
"ISSN": "0736-5748",
"publisher": "Wiley",
"URL": "https://doi.org/10.1002/jdn.70161",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
1
]
]
}
}

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:10.26508/lsa.202503546 [code]
Interactions of LINE-1 ORF1p with proteins and chromatin suggest a role in neuronal physiology.
Journal: Life science alliance
In common: pandas, NumPy, mouse, 3 references, author Héloïse Monnet
[2] doi:10.1371/journal.pcbi.1014571 [code]
SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.
Journal: PLoS computational biology
In common: Cellpose, pandas, Matplotlib, 1 other tool, 2 references
[3] 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, pandas, Matplotlib, 1 other tool, mouse, 1 reference
[4] doi:10.1126/sciadv.aec4911 [code]
Dendritic shaft constrictions shape synaptic integration in neurons.
Journal: Science advances
In common: pandas, Matplotlib, NumPy, mouse, 3 references
[5] doi:10.1002/advs.202522762 [code]
Enhancing Maturation of Human Neuromuscular Organoids via Electrical Stimulation.
Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)
In common: Cellpose, pandas, Matplotlib, 1 other tool, 1 reference
[6] doi:10.1038/s41467-026-73416-2 [code]
Defective ventral neurogenesis due to midfetal Chd8 mutation drives autistic-like behavior in mice.
Journal: Nature communications
In common: pandas, Matplotlib, NumPy, mouse, 2 references
[7] doi:10.3389/fcell.2026.1880548 [code]
A pipeline for cell migration analysis in live-cell imaging data from human iPSC-derived forebrain assembloids.
Journal: Frontiers in cell and developmental biology
In common: 4 references
[8] doi:10.1016/j.isci.2026.117318
Dynamic interactions with neuroblasts promote oligodendrocyte progenitor migration to injured cortex.
Journal: iScience
In common: 4 references
[9] doi:10.1038/s41467-026-72130-3 [code]
Retinoic acid drives cell fate specification, maturation and retinal regionality in human retinal organoids.
Journal: Nature communications
In common: Cellpose, Matplotlib, NumPy, 1 reference
[10] doi:10.1523/eneuro.0386-25.2026 [code]
Astrocyte-Derived PTPRZ1 Regulates Excitatory Synapse Density in the Mouse Cortex.
Journal: eNeuro
In common: mouse, 3 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.