OSCR

Brain ventricle morphology markers in predicting shunt surgery outcome in idiopathic normal-pressure hydrocephalus.

Code ↔ Paper

3 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 3 matches
  1. [1] § Methods › Proposed framework › Feature extraction › Local features ↔ feature_extraction.py, lines 73–188 · score 0.96 · farthest distance, open3d, search_radius_vector_3d, point clouds, point density, computation
  2. [2] § Methods › Proposed framework › Feature extraction › Global features ↔ feature_extraction.py, lines 346–412 · score 0.90 · convex hull volume, PyVista, volume ratio, area ratio, AsE, CHSA
  3. [3] § Methods › Proposed framework › Feature extraction › Curvature features ↔ feature_extraction.py, lines 346–412 · score 0.75 · PyVista, CMax, CMin, Gaussian, GC, MC

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 · 431 lines · 22 KB · CC0-1.0 · 3 matches

  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. # In[ ]:
  4. # Imports
  5. import os
  6. import time
  7. from datetime import datetime
  8. import shutil
  9. import numpy as np
  10. import pandas as pd
  11. import nibabel as nib
  12. import trimesh
  13. import pyvista as pv
  14. import pymeshfix as mf
  15. from pymeshfix._meshfix import PyTMesh
  16. import vedo
  17. from vedo import *
  18. import open3d as o3d
  19. from open3d import *
  20. # In[ ]:
  21. # Correct Mesh
  22. def correct_mesh(iso, smooth=0, taubin=False):
  23. # Extract vertices and faces
  24. vertices = iso.vertices
  25. faces = iso.cells
  26. # Clean vertices and faces
  27. vertices, faces = mf.clean_from_arrays(vertices, faces)
  28. # Load PyTMesh
  29. mfix = PyTMesh(False) # False removes extra verbose output
  30. # Create array
  31. mfix.load_array(vertices, faces)
  32. # Fix mesh
  33. # Fills all the holes having at at most 'nbe' boundary edges. If
  34. # 'refine' is true, adds inner vertices to reproduce the sampling
  35. # density of the surroundings. Returns number of holes patched. If
  36. # 'nbe' is 0 (default), all the holes are patched.
  37. mfix.fill_small_boundaries(nbe=0, refine=True)
  38. # Return vertices and faces
  39. vert, faces = mfix.return_arrays()
  40. triangles = np.empty((faces.shape[0], 4), dtype=faces.dtype)
  41. triangles[:, -3:] = faces
  42. triangles[:, 0] = 3
  43. # Create mesh
  44. mesh = pv.PolyData(vert, triangles)
  45. # Apply Smoothing if Assigned
  46. if smooth > 0:
  47. if taubin:
  48. # Smooth mesh
  49. mesh = mesh.smooth_taubin(n_iter=smooth, non_manifold_smoothing=True)
  50. else:
  51. mesh = mesh.smooth(n_iter=smooth)
  52. return mesh
  53. # In[ ]:
  54. # 12 features derived from shape measurements
  55. def shape_measurments(pv_mesh, r=1.0): # set search radius [1,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.05]
  56. # Load the point cloud and normals
  57. point_cloud = np.asarray(pv_mesh.points)
  58. pcd = o3d.geometry.PointCloud()
  59. pcd.points = o3d.utility.Vector3dVector(point_cloud)
  60. # Remove Outliers ### EXPERIMENTAL ###########################
  61. # Downsample the Point Cloud with a Voxel of 0.01
  62. voxel_down_pcd = pcd.voxel_down_sample(voxel_size=0.01)
  63. # Statistical Oulier Removal
  64. pcd, ind = voxel_down_pcd.remove_statistical_outlier(nb_neighbors=100,
  65. std_ratio=2.0)
  66. ### END OF EXPERIMENTA #######################################
  67. pcd.compute_convex_hull()
  68. pcd.estimate_normals()
  69. pcd.orient_normals_consistent_tangent_plane(1)
  70. mesh_verts = np.asarray(pcd.points)
  71. mesh_norms = np.asarray(pcd.normals)
  72. # # Transform the point cloud to the a PointCloud() object used by open3d
  73. pcd.points = utility.Vector3dVector(mesh_verts)
  74. pcd.normals = utility.Vector3dVector(mesh_norms)
  75. # Calculate the KDTree
  76. pcd_tree = geometry.KDTreeFlann(pcd)
  77. allVerts = np.ones([len(mesh_verts),1])
  78. # how many scales will be used
  79. numScales = 1
  80. # Place holder for set of features
  81. feature_set = []
  82. i=0
  83. # go through all the points
  84. while i<len(mesh_verts):
  85. pointScaleFeatures = []
  86. for j in [r]:
  87. # Constant for Stability
  88. constant = 0.0
  89. # for each radius area scale find neighbours
  90. [k_small, idx_small, distances_small] = pcd_tree.search_radius_vector_3d(pcd.points[i], j) # radius search set radius
  91. currNdx = np.array(idx_small)
  92. # if there are less than 2 neightbours just add 0s
  93. if (len(currNdx) <=2):
  94. linearity= 0 + constant
  95. planarity=0 + constant
  96. sphericity=0 + constant
  97. omnivariance=0 + constant
  98. anisotropy=0 + constant
  99. eigenentropy=0 + constant
  100. sumOFEigs=0 + constant
  101. changeOfCurvature=0 + constant
  102. farthestDist = distances_small[len(distances_small)-1]/j
  103. pointDensity = k_small/j
  104. heightStd=0 + constant
  105. heightMax=0 + constant
  106. shapeDist_curr = np.zeros(50)
  107. # if there are enouigh neighbours then continue with computation
  108. else:
  109. # get heighbourhood points and normals
  110. nearestNeighbors_normals = mesh_norms[currNdx]
  111. nearestNeighbors_verts = mesh_verts[currNdx]
  112. # calculate the covariance matrix, and eigenvalues
  113. cov_mat = np.cov([nearestNeighbors_verts[:,0],nearestNeighbors_verts[:,1],nearestNeighbors_verts[:,2]])
  114. eig_val_cov, eig_vec_cov = np.linalg.eigh(cov_mat)
  115. idx = eig_val_cov.argsort()[::-1]
  116. eig_val_cov = eig_val_cov[idx]
  117. # calculate the first 12 features derived from shape measurements
  118. linearity = (eig_val_cov[0] - eig_val_cov[1])/eig_val_cov[0]
  119. planarity = (eig_val_cov[1] - eig_val_cov[2])/eig_val_cov[0]
  120. sphericity = eig_val_cov[2]/eig_val_cov[0]
  121. omnivariance =(eig_val_cov[0]*eig_val_cov[1]*eig_val_cov[2]) **(1./3.)
  122. anisotropy = (eig_val_cov[0] - eig_val_cov[2])/eig_val_cov[0]
  123. eigenentropy = -(( eig_val_cov[0] * np.log(eig_val_cov[0])) + ( eig_val_cov[1] * np.log(eig_val_cov[1])) + ( eig_val_cov[2] * np.log(eig_val_cov[2])))
  124. sumOFEigs = eig_val_cov[0]+ eig_val_cov[1]+eig_val_cov[2]
  125. changeOfCurvature = eig_val_cov[2]/(eig_val_cov[0]+ eig_val_cov[1]+eig_val_cov[2])
  126. farthestDist = distances_small[len(distances_small)-1]/j
  127. pointDensity = k_small/j
  128. heightMax = np.abs(np.dot(nearestNeighbors_normals.mean(axis=0),nearestNeighbors_verts.T)).max() - np.abs(np.dot(nearestNeighbors_normals.mean(axis=0),nearestNeighbors_verts.T)).min()
  129. heightStd = np.abs(np.dot(nearestNeighbors_normals.mean(axis=0),nearestNeighbors_verts.T)).std()
  130. # Added to the other features and a feature vector is created
  131. pointScaleFeatures.extend([linearity,planarity,sphericity,omnivariance,anisotropy,eigenentropy,sumOFEigs,changeOfCurvature,farthestDist,pointDensity,heightMax,heightStd])
  132. # The feature vector for each point is checked for NaN values and then concatenated
  133. where_are_NaNs = np.isnan(pointScaleFeatures)
  134. pointScaleFeatures = np.array(pointScaleFeatures)
  135. pointScaleFeatures[where_are_NaNs] = 0
  136. pointScaleFeatures = pointScaleFeatures.tolist()
  137. feature_set.append(pointScaleFeatures)
  138. i+=1
  139. # DataFrame of averaged features
  140. col_names = ['linearity','planarity','sphericity','omnivariance','anisotropy','eigenentropy','sumOFEigs','changeOfCurvature','farthestDist','pointDensity','heightStd', 'heightMax']
  141. feature_df = pd.DataFrame(feature_set, columns = col_names) # TO GET ENTIRE DATA FOR PLOTTTING
  142. # Get values
  143. final_df = feature_df.mean()
  144. return final_df
  145. # In[ ]:
  146. # Compute Mesh Features
  147. # Container for All Data
  148. all_data_list = []
  149. # Setup Parameters
  150. structure=[24,25] # flag surface of interest
  151. ndecim = 6000 # mesh granularity
  152. # Smooth Parameters
  153. lamb = 1.0
  154. itr = 5
  155. # Scans to Exclude
  156. corrupt_scan_id = [] # subject IDs to exclude
  157. # Directory to mine
  158. main_dir = '' # input path to directory of interest
  159. vent_size = [] # non processed mesh size
  160. # Starting Time
  161. program_starts = time.time()
  162. # ID's directory
  163. id_dirs = os.listdir(main_dir)
  164. # Sub-directory
  165. sub_dirs = [os.path.join(main_dir, sub_id) for sub_id in id_dirs if not 'DS_Store' in sub_id and not 'Mesh_Params' in sub_id]
  166. for sub_dir in sub_dirs:
  167. ses_dir = os.listdir(os.path.join(sub_dir))
  168. for ses in ses_dir[:2]:
  169. if not 'DS_Store' in ses:
  170. scan_dir = os.listdir(os.path.join(sub_dir, ses))
  171. synthseg_files = [file for file in scan_dir if 'sub' in file] # cneuro
  172. for synthseg_file in synthseg_files:
  173. # Define source path
  174. src_path = os.path.join(sub_dir, ses, synthseg_file)
  175. # Path details
  176. file_path = src_path
  177. # Find a base name
  178. basename = os.path.basename(file_path)
  179. # Get ID
  180. sub_id = basename.split('_')[0] # cNeuro
  181. if sub_id not in corrupt_scan_id:
  182. ### MESH PROCESSING ###########################################################################
  183. # Load File
  184. nifti_file = nib.load(file_path) # load file from the path
  185. segmentation_array = nifti_file.get_fdata() # convert nifti segmentation file into numpy array
  186. # Filter brain structure of interest and set a scene for mesh
  187. scene = np.where(
  188. (segmentation_array!=0) & # remove background
  189. np.isin(segmentation_array.astype(int), structure), # flag structure of interest
  190. 1, 0) # assign values
  191. # Create volume using Vedo surfnets and use dual approach to create isosurface
  192. # Extract volume
  193. vol = Volume(scene.astype(int),spacing =nifti_file.header.get_zooms()) # Extract volume and correct strectching along all axis
  194. # Create mesh from mask
  195. iso = vol.isosurface_discrete([1,]) # discreate
  196. ### CLEAN ###########################################################################
  197. # Create Clean Mesh
  198. mesh_obj = correct_mesh(iso) # smooth=s, taubin=taubin_state
  199. # Pyvista Mesh
  200. pv_mesh = pv.wrap(mesh_obj)
  201. # Extract Faces
  202. pv_faces = pv_mesh.faces
  203. # Extract Vertices
  204. pv_vertices = pv_mesh.points
  205. # Back to Vedo Mesh
  206. iso = vedo.Mesh([pv_vertices, pv_faces])
  207. ### SUBDIVIDE ###########################################################################
  208. if pv_vertices.shape[0] < ndecim:
  209. # Extract Vertices
  210. vedo_vertices = iso.vertices
  211. # Extract Faces
  212. vedo_faces = iso.cells
  213. # Subdivide Mesh
  214. tri_vertices, tri_faces = trimesh.remesh.subdivide_loop(vedo_vertices, np.array(vedo_faces).astype(int), iterations=1)
  215. # Tri Mesh
  216. tri_iso = trimesh.Trimesh(tri_vertices, tri_faces)
  217. # Convert to Vedo Mesh
  218. iso = vedo.trimesh2vedo(tri_iso)
  219. ### SMOOTH ###########################################################################
  220. # Correct Mesh
  221. pv_mesh = pv.wrap(correct_mesh(iso))
  222. # Convert to Tri Mesh
  223. pv_mesh_faces = pv_mesh.faces.reshape((pv_mesh.n_cells, 4))[:, 1:]
  224. # Convert to Tri Mesh
  225. tri_iso = trimesh.Trimesh(pv_mesh.points, pv_mesh_faces)
  226. # Convert to Vedo Mesh
  227. iso = vedo.trimesh2vedo(tri_iso)
  228. # ### DECIMATE ###########################################################################
  229. # Decimate Pro
  230. iso = iso.decimate_pro(
  231. n=ndecim,
  232. # preserve_topology=False,
  233. # preserve_boundaries=False,
  234. splitting=True
  235. )
  236. ### VEDO Smooth ###########################################################################
  237. # Correct Mesh
  238. pv_mesh = pv.wrap(correct_mesh(iso))
  239. # Convert to Tri Mesh
  240. pv_mesh_faces = pv_mesh.faces.reshape((pv_mesh.n_cells, 4))[:, 1:]
  241. # Convert to Tri Mesh
  242. tri_mesh = trimesh.Trimesh(pv_mesh.points, pv_mesh_faces)
  243. # Smooth Mesh
  244. trimesh.smoothing.filter_laplacian(tri_mesh, lamb=lamb, iterations=itr,)
  245. #### TEST
  246. # Convert to Vedo Mesh
  247. iso = vedo.trimesh2vedo(tri_mesh)
  248. # Correct Mesh
  249. pv_mesh = pv.wrap(correct_mesh(iso))
  250. # Convert to Tri Mesh
  251. pv_mesh_faces = pv_mesh.faces.reshape((pv_mesh.n_cells, 4))[:, 1:]
  252. # Convert to Tri Mesh
  253. tri_mesh = trimesh.Trimesh(pv_mesh.points, pv_mesh_faces)
  254. ### COMPUTE FEATURES ###########################################################################
  255. # Register Setup Parameters
  256. d = ndecim
  257. setup = f"s{lamb}_i{itr}_d{d}"
  258. # Compute Curvature Features
  259. # Tri mesh
  260. IMC = tri_mesh.integral_mean_curvature # Integral Mean Curvature
  261. # Compute Global Features
  262. # Tri mesh
  263. gf_tri_mesh = tri_mesh
  264. SA = gf_tri_mesh.area
  265. V = gf_tri_mesh.volume
  266. SAVR = SA/V
  267. # Capture Asphericity
  268. # Generate points
  269. pts = pv_mesh.points
  270. # Find the best fitting ellipsoid to the points
  271. elli = pca_ellipsoid(pts, pvalue=0.95) # https://vedo.embl.es/docs/vedo/pointcloud.html#pca_ellipsoid
  272. AS = elli.asphericity() # asphericity
  273. ASE = elli.asphericity_error() # error on asphericity
  274. # Capture Convexity and Geometry Features
  275. # Tri mesh
  276. cx_tri_mesh = tri_mesh
  277. # Compute Shape volume to Convex Hull volume ratio
  278. CxVR = V / cx_tri_mesh.convex_hull.volume # Volume devided by convex hull volume
  279. # Compute Shape area to Convex Hull area ratio
  280. CxSAR = SA / cx_tri_mesh.convex_hull.area
  281. # Compute Convex Hull volume
  282. CxV = cx_tri_mesh.convex_hull.volume
  283. # Compute Convex Hull area
  284. CxSA = cx_tri_mesh.convex_hull.area
  285. # Compute Convex Hull area to volume ratio
  286. CxSAVR = CxSA / CxV
  287. # Save Mesh
  288. ses_path = os.path.join(sub_dir, ses) # session dir path
  289. mesh_path = os.path.join(ses_path, f'{sub_id}_mesh.stl') # mesh destination path
  290. pv_mesh.save(mesh_path)
  291. # Features: ['linearity','planarity','sphericity','omnivariance','anisotropy','eigenentropy','sumOFEigs','changeOfCurvature','farthestDist','pointDensity','heightStd', 'heightMax']
  292. shape_m = shape_measurments(pv_mesh)
  293. L = shape_m['linearity']
  294. P = shape_m['planarity']
  295. SP = shape_m['sphericity']
  296. O = shape_m['omnivariance']
  297. AT = shape_m['anisotropy']
  298. ET = shape_m['eigenentropy']
  299. ES = shape_m['sumOFEigs']
  300. CC = shape_m['changeOfCurvature']
  301. FD = shape_m['farthestDist']
  302. PD = shape_m['pointDensity']
  303. Hmax = shape_m['heightMax']
  304. Hsd = shape_m['heightStd']
  305. # PyVista Curvature Mean
  306. GGC = np.mean(pv_mesh.curvature('gaussian'))
  307. GMC = np.mean(pv_mesh.curvature('mean'))
  308. Cmax = np.mean(pv_mesh.curvature('maximum'))
  309. Cmin = np.mean(pv_mesh.curvature('minimum'))
  310. # Create feature DataFrame
  311. # Measurment Names
  312. var_names = ['SA', 'V', 'SAVR', 'IMC', 'As', 'AsE',
  313. 'GC', 'MC', 'CMax', 'CMin',
  314. 'L', 'P', 'S', 'O', 'A', 'EE', 'SE', 'CC', 'PD', 'FD', 'HMax', 'HSD',
  315. 'CHV', 'CHSA', 'CHSAVR', 'CHSAR', 'CHVR',]
  316. # Measurments
  317. measurments = [SA,V,SAVR,IMC,AS,ASE,
  318. GGC,GMC,Cmax,Cmin,
  319. L,P,SP,O,AT,ET,ES,CC,FD,PD,Hmax,Hsd,
  320. CxV, CxSA,CxSAVR,CxSAR,CxVR,]
  321. col_names = ['MEASURMENT', 'VALUE']
  322. measurment_DF = pd.DataFrame(zip(var_names, measurments), columns=col_names)
  323. print(measurment_DF)
  324. # Extract Feature Values
  325. f_values = measurments
  326. # Add ID
  327. f_values.append(sub_id)
  328. # Add Setup
  329. f_values.append(setup)
  330. # Add Row to All Data List
  331. all_data_list.append(f_values)
  332. # Time per Loop
  333. now = time.time()
  334. print("It took {} seconds to process {} setup {}".format(now - program_starts,sub_id,setup))
  335. # Create Final DataFrame
  336. all_data_cols = ['SA', 'V', 'SAVR', 'IMC', 'As', 'AsE',
  337. 'GC', 'MC', 'CMax', 'CMin',
  338. 'L', 'P', 'S', 'O', 'A', 'EE', 'SE', 'CC', 'PD', 'FD', 'HMax', 'HSD',
  339. 'CHV', 'CHSA', 'CHSAVR', 'CHSAR', 'CHVR',
  340. 'ID', 'SETUP']
  341. all_data_df = pd.DataFrame(all_data_list, columns = all_data_cols)
  342. # Date Params
  343. year = datetime.now().year
  344. month = datetime.now().month
  345. day = datetime.now().day
  346. feature_data_dst = os.path.join(main_dir, f'feature_df_{day}-{month}-{year}.csv') # feature data dst
  347. # Save Data
  348. all_data_df.to_csv(feature_data_dst)

feature_extraction.py at commit 568b2ab, under CC0-1.0 · at the source

Overview

Authors: Andrius Penkauskas1, Eemeli Kuukkanen2, Anssi Lipponen3, Juhana Hakumäki4, Juha Koikkalainen5, Jyrki Lotjonen5, Lauri Erkkilä4, Jussi Tohka6, Pauli Miettinen1, Ville Leinonen2
  1. School of Computing, University of Eastern Finland, Kuopio, 70211 Finland
  2. Faculty of Health Sciences, University of Eastern Finland, Kuopio, 70211 Finland
  3. Institute of Biomedicine, University of Eastern Finland, Kuopio, 70211 Finland
  4. Institute of Clinical Medicine–Radiology, University of Eastern Finland, Kuopio, 70211 Finland
  5. Combinostics Ltd., Tampere, 33100 Finland
  6. A.I. Virtanen Institute for Molecular Sciences, University of Eastern Finland, Kuopio, 70211 Finland
Institutions: University of Eastern Finland (Finland)
Journal: Fluids and barriers of the CNS, volume 23, issue 1, article 56
Dates: received 6 May 2025; accepted 2 March 2026; published online 9 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1186/s12987-026-00788-4 · PMID 41957814 · PMCID PMC13063911 · OpenAlex W4413523702
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), human (organism), other condition (population), clinical / translational (subfield)
Methods: Connectivity, Machine learning, Statistics, fMRI & imaging
MeSH: Cerebrospinal Fluid Shunts*, Hydrocephalus, Normal Pressure*, Lateral Ventricles*, Aged, Aged, 80 and over, Female, Humans, Imaging, Three-Dimensional, Machine Learning, Magnetic Resonance Imaging, Male, Middle Aged, Predictive Learning Models, Treatment Outcome (* major topic)
Topic: Cerebrospinal fluid and hydrocephalus (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: H2020 Marie Skłodowska-Curie Actions (No101034307); Research Council of Finland (339767); Flagship of Advanced Mathematics for Sensing Imaging and Modeling (358944); PRIMAL (346934)
Citations: cited by 1 paper (Europe PMC); 46 references in the paper

Abstract

Background: Idiopathic normal pressure hydrocephalus (iNPH) is characterized by a clinical triad of symptoms: abnormal gait, memory problems, and urinary incontinence. Neuroimaging plays a crucial role in diagnosing iNPH. However, current radiological markers, though indicative, are not definitive, suggesting the limited capacity of these indices to capture mechanisms associated with iNPH and the reversibility of the symptoms.

Aims: This study aims to (1) determine the geometric features of the lateral ventricles, (2) develop a quantitative method for three-dimensional analysis, and (3) test the ability to predict response to shunt surgery. By examining these features as potential diagnostic markers, this research seeks to enhance the understanding of morphometric characteristics in iNPH, thereby paving the way for improved patient selection for surgical intervention.

Methods: Our study contained 170 patients (95 shunt responders and 75 non-responders) from the Kuopio NPH registry. Our inclusion criteria required pre-surgery and one-year post-surgery symptom assessments alongside preoperative anatomical magnetic resonance imaging (MRI). Volumetric brain segmentations were performed using cNeuro software on T1-MRI images, followed by the generation of 3D lateral ventricle meshes for geometric feature extraction. The classification task employed the LogitNet machine learning model to analyze 27 geometric features. Model performance evaluation utilized repeated nested cross-validation (10 rounds) with five inner folds for parameter tuning and five outer folds for model evaluation. Additionally, we generated a ranking of feature importance based on the LogitNet L1 regularization coefficients.

Results: Our analysis revealed that LogitNet achieved an AUC of 0.661 (SD = 0.066) performance across 10 rounds of cross-validation in predicting the shunt surgery response. The most prominent feature contributing to the model’s prediction was asphericity.

Conclusion: Our analysis suggests that the proposed set of features, especially asphericity, effectively captures valuable information linked to the reversibility of iNPH.

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 3 matches between paragraphs and lines of code.

AP-environment/3D-Mesh-Feature-Extraction

License: CC0-1.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 568b2ab9f1ce1921bb01ad24e6b1807631527d0a, 7 May 2025
Languages: Python (1)
Size: 3 files, 1 script
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NiBabel (1 file), NumPy (1 file), pandas (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
3 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;
  • 1 script, each with its path and the digest of its content;
  • 3 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

The feature extraction pipeline is available online at (https://github.com/AP-environment/3D-Mesh-Feature-Extraction). Research data cannot be shared publicly due to the risk of violating patient privacy.

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, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 10 authors, 14 MeSH terms, 4 funders, 42 references.

Cite

This paper

Penkauskas, A., Kuukkanen, E., Lipponen, A., Hakumäki, J., Koikkalainen, J., Lotjonen, J., Erkkilä, L., Tohka, J., Miettinen, P., & Leinonen, V. (2026). Brain ventricle morphology markers in predicting shunt surgery outcome in idiopathic normal-pressure hydrocephalus. Fluids and barriers of the CNS, 23(1), 56. https://doi.org/10.1186/s12987-026-00788-4

BibTeX

@article{penkauskas2026brain,
author = {Penkauskas, Andrius and Kuukkanen, Eemeli and Lipponen, Anssi and Hakumäki, Juhana and Koikkalainen, Juha and Lotjonen, Jyrki and Erkkilä, Lauri and Tohka, Jussi and Miettinen, Pauli and Leinonen, Ville},
title = {{Brain ventricle morphology markers in predicting shunt surgery outcome in idiopathic normal-pressure hydrocephalus}},
journal = {Fluids and barriers of the CNS},
year = {2026},
month = apr,
volume = {23},
number = {1},
pages = {56},
publisher = {BMC},
issn = {2045-8118},
doi = {10.1186/s12987-026-00788-4},
url = {https://doi.org/10.1186/s12987-026-00788-4},
pmid = {41957814},
pmcid = {PMC13063911}
}

RIS

TY - JOUR
AU - Penkauskas, Andrius
AU - Kuukkanen, Eemeli
AU - Lipponen, Anssi
AU - Hakumäki, Juhana
AU - Koikkalainen, Juha
AU - Lotjonen, Jyrki
AU - Erkkilä, Lauri
AU - Tohka, Jussi
AU - Miettinen, Pauli
AU - Leinonen, Ville
TI - Brain ventricle morphology markers in predicting shunt surgery outcome in idiopathic normal-pressure hydrocephalus
T2 - Fluids and barriers of the CNS
J2 - Fluids Barriers CNS
PY - 2026
DA - 2026/04/09
VL - 23
IS - 1
SP - 56
SN - 2045-8118
PB - BMC
DO - 10.1186/s12987-026-00788-4
UR - https://doi.org/10.1186/s12987-026-00788-4
LA - en
ER -

CSL-JSON

{
"id": "10.1186/s12987-026-00788-4",
"type": "article-journal",
"title": "Brain ventricle morphology markers in predicting shunt surgery outcome in idiopathic normal-pressure hydrocephalus",
"container-title": "Fluids and barriers of the CNS",
"author": [
{
"family": "Penkauskas",
"given": "Andrius"
},
{
"family": "Kuukkanen",
"given": "Eemeli"
},
{
"family": "Lipponen",
"given": "Anssi"
},
{
"family": "Hakumäki",
"given": "Juhana"
},
{
"family": "Koikkalainen",
"given": "Juha"
},
{
"family": "Lotjonen",
"given": "Jyrki"
},
{
"family": "Erkkilä",
"given": "Lauri"
},
{
"family": "Tohka",
"given": "Jussi"
},
{
"family": "Miettinen",
"given": "Pauli"
},
{
"family": "Leinonen",
"given": "Ville"
}
],
"container-title-short": "Fluids Barriers CNS",
"volume": "23",
"issue": "1",
"page": "56",
"DOI": "10.1186/s12987-026-00788-4",
"PMID": "41957814",
"PMCID": "PMC13063911",
"ISSN": "2045-8118",
"publisher": "BMC",
"URL": "https://doi.org/10.1186/s12987-026-00788-4",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
9
]
]
}
}

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.1093/braincomms/fcag300
Assessing CT-based volumetric analysis via deep learning for idiopathic normal pressure hydrocephalus.
Journal: Brain communications
In common: structural MRI / diffusion, other condition, 6 references
[2] doi:10.1371/journal.pmed.1004809 [code]
Brain morphology in Anorexia Nervosa and its subtypes: A multi-cohort study of individual participant data.
Journal: PLoS medicine
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, other condition, 1 reference
[3] doi:10.1002/epi4.70336 [code]
Quantifying extra-lesional interhemispheric cortical asymmetry in focal cortical dysplasia type II.
Journal: Epilepsia open
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, clinical / translational, 1 reference
[4] doi:10.1371/journal.pbio.3003856 [code]
Aging and metabolism contribute separately to brain-body health.
Journal: PLoS biology
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, clinical / translational, 1 reference
[5] doi:10.1038/s41598-026-47942-4 [code]
CT-based radiomic markers to predict late-onset seizures after traumatic brain injury.
Journal: Scientific reports
In common: NiBabel, pandas, NumPy, clinical / translational, other condition, 1 reference
[6] doi:10.1093/brain/awag032 [code]
A rostral prefrontal mediolateral gradient predicts creativity in frontotemporal dementia.
Journal: Brain : a journal of neurology
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, 1 reference
[7] doi:10.1093/schbul/sbag045 [code]
Cortical Iron and Mesostriatal Dopamine Function in Schizophrenia: A Positron Emission Tomography and Magnetic Resonance Imaging Study.
Journal: Schizophrenia bulletin
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, 1 reference
[8] doi:10.1038/s41598-026-56778-x [code]
Chiari malformation type 1 is associated with a smaller fourth ventricle volume - a multi-cohort replication study.
Journal: Scientific reports
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, 1 reference
[9] doi:10.1162/imag.a.1252 [code]
Does the brain's E:I balance really shape long-range temporal correlations? Lessons learned from 3T MRI.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, 1 reference
[10] doi:10.1016/j.nicl.2026.104001 [code]
Effect of vascular lesion preprocessing on Brain Intensity AbNormality Classification Algorithm (BIANCA) white matter hyperintensity segmentation.
Journal: NeuroImage. Clinical
In common: NiBabel, pandas, NumPy, structural MRI / diffusion, 1 reference

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.