ggseg.extra 1.9.9.9130
This section becomes the 2.0.0 release notes at submission. It is the first CRAN release of ggseg.extra, the atlas-building package of the ggsegverse, renamed and restructured from ‘ggsegExtra’ for the ggsegverse 2.0 architecture. Every pipeline now returns a single ggseg_atlas object holding 2D polygons and 3D meshes together, and geometry shaping has moved out of atlas creation into cheap post-creation verbs you can retune without rebuilding. Twelve create_*() entry points cover cortical, subcortical, cerebellar, whole-brain and white-matter tract atlases, built from FreeSurfer annotations and labels, GIFTI, CIFTI, neuromaps, NIfTI/MGZ parcellation volumes and tractography files.
Breaking changes
A label the lookup table does not list is not drawn. It used to come back as
context_<id>, which is the pipeline guessing, and the guess only reads well when the volume happens to be shaped like anaseg: a volume carrying a whole parcellation drew a hundred grey shapes, cerebral white matter among them, filling the sulci a backdrop exists to show. Declare a label to draw it.A
contextvalue other than yes or no names a group, and every row carrying that name is traced as one silhouette called after it. A parcellation’s cortical labels markedcontext = "cortex"are one ribbon rather than a hundred patches, without anything having to recognise a parcel by its id.The package is
ggseg.extra; the oldggsegExtraname is gone from the code and the documentation.Geometry shaping is no longer part of atlas creation.
dilate,smoothness,toleranceandsmooth_refinementsare not accepted by anycreate_*()function; useatlas_polish(), oratlas_simplify(),atlas_smooth()andatlas_dilate()individually on the finished atlas. Atlas build scripts that still pass one of these spellings abort with “unused argument”, naming the creator.The
ggseg.extra.toleranceandggseg.extra.smoothnessoptions and theirGGSEG_EXTRA_*environment variables are removed, as nothing reads them.The colour-table functions are named after lookup tables:
read_lut(),write_lut(),is_lut()andget_lut(). Theread_ctab(),write_ctab(),is_ctab()andget_ctab()spellings are removed.sitrep()replacessetup_sitrep(), andggseg_atlas_github_actions()replacesatlas_github_actions(); the old names are removed.subcortical_slabs()replacessubcortical_views(), and the volumetricviewsargument ofcreate_subcortical_from_volume()andcreate_tract_from_tractography()isslabs. The cortical creators’viewsargument, which selects standard panels, is unrelated and unchanged.Related arguments are grouped into lists, and the flat spellings are no longer accepted:
tube_optsincreate_tract_from_tractography();labels,projection_opts,cortical_opts,subcortical_optsandcerebellar_optsincreate_wholebrain_from_volume().cortical_optsaccepts onlyviews.create_cerebellar_from_volume()takesinput_volumeas its first argument, as every sibling creator does, andread_neuromaps_volume()takesbreakssecond andoutput_dirlast.mri_surf2surf_rereg()takeshemisphererather thanhemi.registrationmeans one thing across the package:"mni152","header", or a path to a register.dat or LTA file, inprepare_subcortical_mni152(),project_volume_anatomical()andcreate_wholebrain_from_volume(). The ambiguousregistration = NULLand theregheaderargument are gone; passprojection_opts = list(registration = ).In
create_subcortical_from_volume()the lookup table decides what is a region and what is context, the grey anatomy the regions are drawn against. Nothing is recognised by its id or its name. A row of the table is a region; a row whose optionalcontextcolumn isTRUEis context, keeps its name, is traced on a single slice in each view so that a cortical ribbon keeps its folds, and gets no 3D mesh; a label in the volume that the table does not list is context too, named for its id (context_0002). With no table, every label is a region. To get the cortex as context, mark its rows:lut$context <- grepl("Cortex", lut$label). Context that is not wanted is removed afterwards withatlas_context_remove().create_cortical_from_neuromaps(),read_neuromaps_annotation()andread_neuromaps_volume()bin a continuous map throughbreaks, which replacesn_bins: a single number for that many quantile bins, increasing numbers as the bin edges, or a function that takes the map’s values and returns the edges. Left out, it gives quantile bins counted by Sturges’ rule. Both hemispheres are binned on one set of edges, so a bin means the same range of values on either side.read_tractography()returns.trkstreamlines in RAS world millimetres, applying the header’s voxel size, voxel order and voxel-to-world transform, and agrees with ‘nibabel’ on its reference files. It used to return the numbers as stored, which misplaced.trktracts. Big-endian files are read too.create_tract_from_tractography()therefore no longer acceptscoord_space = "voxel"for tractography files; it still applies to streamlines passed as coordinate matrices.create_wholebrain_from_volume()registers MNI152 volumes to the surface subject with FreeSurfer’saverage/mni152.register.datrather than assuming the two spaces coincide. Eachfsaverage5vertex samples a point about 2 mm away, so cortical atlases built from MNI152 volumes with earlier versions must be rebuilt.Subcortical and tract contours are traced from the projection matrix itself rather than from a PNG of it (#139), so geometry is exact in voxel coordinates instead of in pixels of a 400x400 canvas. Atlases built with earlier versions produce different geometry and need rebuilding, and distance-valued arguments –
atlas_dilate(amount = )andatlas_smooth(smoothness = )– now mean voxels everywhere rather than canvas pixels, so values tuned against the canvas want retuning.The subcortical pipeline has six steps and the tract pipeline four; the contour-smoothing and vertex-reduction steps that stopped doing anything are gone.
stepsis bounds-checked and errors on a value above the last step.skip_existingdefaults toFALSE. A step cache records which ggseg.extra wrote it and nothing about what it was built from, so reuse is now asked for rather than assumed, and every reuse is reported naming the step.Cerebellar region names come from the shared
label_to_region(), which now stripsvermisandmidlinealongsideleftandright, so the three pipelines agree. Cerebellar region names change on the next rebuild:midline_M1Lbecomesm1landVermis_VIIAtbecomesviiat. Thelabelcolumn is unchanged.The cerebellar flatmap gets a
cerebellumbackdrop, and perimeter regions are no longer inflated to reach the flatmap edge, so they stop where the parcellation says they stop. Rebuilding a published cerebellar atlas produces slightly different geometry.Every cortical atlas keeps its
unknownmedial wall as grey context rather than as a region: its outline draws behind the parcels, but it carries nocorerow, no palette entry and no 3D vertices.2D atlas geometry keeps its holes.
coords2sf()had been assembling every polygon ring as a solid polygon of its own, filling enclosed holes, so a cortical ribbon came out as a blob; rings are now assembled as exterior plus holes. Every atlas built throughget_contours()differs from one built before this fix.atlas_smooth()andatlas_simplify()do one thing each: smoothing decides how round an outline is, simplification how many vertices it costs.keepbelongs toatlas_simplify()only.The cortical creators have no
method,snapshot_dimorstepsarguments; the mesh-projection pipeline reads and projects in one pass.ggseg_atlas_repos(),install_ggseg_atlas()andinstall_ggseg_atlas_all()are removed; installing atlases is ‘ggsegverse’s job.convert_legacy_brain_atlas()lives in ’ggseg.formats’ and is re-exported here.The dependency footprint is much lighter. ImageMagick and a headless browser are not needed at all, so
chromote,magickandhtmlwidgetsare gone along withrgdal,purrr,reticulateandtidyr.freesurfer,freesurferformats,ciftiTools,gifti,RNifti,Rvcg,terra,smoothr,princurve,rgl,neuromapr,ggsegandfutureare all Suggests, checked at run time withrlang::check_installed()and requested only by the pipeline that needs them.freesurferis required at>= 1.9.0(#72, #98) andciftiToolsat>= 0.17.4.regionis derived from the region’s identifier rather than copied from it, so the hemisphere no longer ends up in both. A lookup table that declares aregioncolumn has had it curated by the atlas author and is used as given; only the fallback is derived.labelis unchanged, so no atlas’s palette keys move.The core display column is
display, followingggseg.formats. A lookup table declares it under that name.hemiis recorded as"left","right"or"midline"only, matching the closed vocabularyggseg.formatsenforces. The cerebellar vermis is recorded as"midline";"vermis"and"middle"are still read from a label or a lookup table’shemicolumn, and thevermis_spelling is still kept in the label, which is the join key.
New features
clear_atlas_cache()removes an atlas’s build intermediates, or every atlas’s under a build directory. It refuses a directory holding files this package did not write, so pointing it at the wrong folder does not delete source data.Cerebellar atlases are a first-class atlas type across the ecosystem, with SUIT flatmap polygons for 2D and per-region meshes for 3D.
create_cerebellar_from_gifti(),create_cerebellar_from_annotation()andcreate_cerebellar_from_volume()build them from GIFTI label files, FreeSurfer.annotfiles on the SUIT surface, or a NIfTI cerebellar segmentation, andread_suit_parcellation()reads SUIT-format GIFTI labels with Left/Right/Vermis hemisphere detection.Deep cerebellar nuclei (Dentate, Interposed, Fastigial) that have volume voxels but no SUIT surface vertices are tessellated as individual 3D meshes with a tkRAS-to-MNI transform and drawn as a separate coronal
nucleiview. Surface parcels too small for any SUIT vertex to land on are rescued onto the flatmap.create_wholebrain_from_volume()builds cortical, subcortical and cerebellar sub-atlases from a single volumetric parcellation, classifying each label from the lookup table’s declaration and reporting what it had to infer.create_tract_from_tractography()builds a white-matter tract atlas from.trk/.tckfiles or in-memory coordinate matrices, andcreate_tract_from_volume()builds one from a volumetric tract label map by reducing each tract’s voxel cloud to a principal-curve centerline. Both produce 3D tubes and 2D slice projections.create_cortical_from_cifti()andread_cifti_annotation()read CIFTI dense label files,read_cifti_subcortical()extracts a CIFTI file’s subcortical voxels into a NIfTI label volume plus colour table ready for the subcortical pipelines (#85), and GIFTI.label.giiannotations are supported throughout.create_cortical_from_neuromaps(),read_neuromaps_annotation()andread_neuromaps_volume()build atlases from neuromaps surface and volume annotations, binning continuous maps into regions.The cortical pipeline projects inflated mesh triangles straight to 2D polygons by orthographic projection. Atlas creation takes about five seconds rather than minutes, the geometry carries no rasterisation staircase, and reading
.annotfiles needs only thefreesurferformatspackage.Boundary triangles whose vertices belong to different regions are split along edge midpoints – a quadrilateral and a triangle for a two-region boundary, three quadrilaterals meeting at the centroid for three – so region borders are clean rather than sawtoothed, and boundary faces go to the smallest neighbouring region so tiny parcels survive.
atlas_polish()simplifies and smooths in one call against a stated vertex budget, owning the order the two have to run in so a build does not have to rediscover it (#186).atlas_smooth()gainsmethod– the default morphological"close"plus"chaikin","ksmooth"and"spline", which move vertices rather than dilating the shape and so keep enclosed holes open – asmoothnessstrength on one 0–1 scale shared by every method, andvertex_budgetto choose between preserving the starting vertex count and rounding freely.atlas_dilate()grows or shrinks region geometry on a finished atlas, the post-creation counterpart of the old build-time dilation.count_vertices()reports how many polygon vertices each region carries, which is the figure the large-atlas warning quotes andatlas_simplify()brings down.context_pattern()gives the label pattern matching an atlas’s context, in one place rather than retyped into every build. Given an atlas it matches exactly the context that atlas draws, whatever its shapes are called; without one it matches the names the pipelines generate.atlas_polish(),atlas_simplify(),atlas_smooth()andatlas_dilate()accept a function aslabelsorexcludeand ask it about the atlas they are given, soexclude = context_patternmeans “this atlas’s context” and works in the middle of a pipe.A cortical parcellation that covers only part of the mantle gets the rest of it as a silhouette to sit on (#285). Every vertex no label claims becomes one
lh_cortex/rh_cortexregion per hemisphere, drawn behind the parcellation and carrying no colour or legend entry. A sparse cerebellar parcellation likewise gets a singlecerebellumbackdrop.label_to_region()is exported: it is the rule the pipelines use to fill an atlas’sregioncolumn from its labels, so a build script adding metadata keyed onregioncan key on what the pipeline actually derives.A lookup table can declare
type("cortical","subcortical","cerebellar") andhemicolumns, whichcreate_wholebrain_from_volume(),create_subcortical_from_volume()andcreate_cerebellar_from_volume()honour rather than guessing.write_lut()writes both andread_lut()reads them back, so a declaration survives a round trip through a file and stays a FreeSurfer-readable colour table. A declared hemisphere is what stops a midline-crossing parcel becoming two regions the parcellation never had.lut_classify_anatomy()fills a lookup table’stypecolumn by resampling FreeSurfer’saparc+asegonto the volume’s own grid and judging each label on the share of labelled grey matter it touches, rather than by matching names – an authoring tool whose output you commit and review in a diff.lut_generate_colors()builds a palette for a lookup table that ships names only. Colours are assigned per structure, so a structure’s two hemispheres share one, with hues spread around the circle and stepped through three luminances;bycolours each group of a column from the whole circle, which is the whole-brain case. Out-of-gamut clipping that would hand two structures the same colour is an error namingchromaandluminance.lut_add()andlut_combine()append and merge FreeSurfer-style colour tables, validating withis_lut()and warning on index clashes.subcortical_slabs()builds a slab table from the bounding box of a set of labels, reading the volume in the same frame the builder uses, andcreate_subcortical_from_volume(slabs = )accepts that spec directly.A lookup table says more than names and colours. Alongside
typeandhemi,read_lut()andwrite_lut()carry acontextcolumn, kept separate fromtypeso a whole-brain table can say both which atlas a label belongs to and what role it plays there. A table passed as a data.frame may also carry anamescolumn, the display name of each region; every atlas the pipelines build hasnamesin itscore, falling back to the region.aseg_subcortical_labels()returns the lumped structure ids a finer parcellation subdivides.coregister_volume()wrapsmri_coregto align an atlas volume to a FreeSurfer subject’s T1 grid, returning a reusable LTA file.project_volume_anatomical()resamples each label onto the targetaparc+aseggrid, takes the per-voxel argmax and returns a merged volume plus an aligned colour table, andprepare_subcortical_anatomical()chains both.id_offsetkeeps input ids from colliding with FreeSurfer’s, andprotect_cortexkeeps the cortical ribbon and cortical white matter intact for the brain-outline context. The lookup table it returns, like the one fromprepare_subcortical_mni152(), hascontextfilled in: the FreeSurfer anatomy is context and the labels are regions.prepare_subcortical_mni152()embeds a parcellation supplied in fixed FSL-MNI152 space into a subject’sasegvia the knownmni152.register.dattransform, replacing the lumped structures the parcels subdivide.create_subcortical_from_volume()accepts the returnedlist(volume, lut, id_offset)directly asinput_volume.create_wholebrain_from_volume()gainscerebellar_opts = list(cerebellar_space = ), so an MNI cerebellar volume is transformed with the matching SUIT deformation field before it is sampled onto the flatmap rather than being drawn onto a flatmap it does not correspond to.create_tract_from_tractography()andcreate_tract_from_volume()gaincoord_space, to declare whether streamline coordinates are"voxel"indices or RAS world"mm"instead of relying on a heuristic that cannot always tell; the space in force is reported atverbose >= 1.The brain silhouette behind a whole-brain subcortical atlas is a gyrified mantle rather than a solid blob: it is taken from FreeSurfer’s
asegribbon, resampled onto the atlas volume’s own grid, wherever the parcellation’s own cortical labels are solid, and theasegcerebellar cortex and brain stem fill the posterior fossa. An atlas whose own labels are already a ribbon keeps them, the substitution is declined on grids too coarse to carry a ribbon, and the pipeline reports which silhouette it drew in every case.setup_atlas_repo()scaffolds an atlas package from the ggsegverse/ggseg-atlas-template repository, with a Quarto README, pkgdown config, shared CI workflows and adata-raw/create-atlas.Rcovering all five pipelines, falling back to a bundled minimal template when offline. It fills DESCRIPTION from theusethis.descriptionoption when one is set.use_atlas_github_actions()adds the shared ggsegverse GitHub Actions workflows to a package as short caller stubs, andggseg_atlas_github_actions()lists what is available.sitrep()reports whether each of the 13 pipelines is ready to run, naming the FreeSurfer binaries, subjects and optional R packages each one needs.Verbosity has three levels – silent, standard, debug – resolved from an explicit argument, the
ggseg.extra.verboseoption orGGSEG_EXTRA_VERBOSE, withget_verbose()andas_verbosity()as the accessors.cleanupandoutput_dirresolve the same way.Pipeline intermediates are cached per step with a
cache_manifest.rdsrecording the cache format version that wrote them, so a cache written by an older ggseg.extra is recomputed rather than silently reused. Subcortical projection snapshots additionally carry a signature of the voxels, slab and volume dimensions they were drawn from, and only what moved is redrawn.Volumetric and tract builds parallelise through
furrr, andplan(multicore)is used as given: on an eight-core machinecreate_subcortical_from_volume()goes from 337s sequential to 171s with four forked workers.The
atlas_*manipulation verbs from ggseg.formats are re-exported, along withggseg_atlas()andis_ggseg_atlas(), solibrary(ggseg.extra)is enough to build, curate and validate an atlas (#186).decimatereduces per-region 3D mesh vertex counts withRvcgin the subcortical and cerebellar pipelines, andvertex_size_limitsfilters finished 2D polygons by vertex count.read_lut()andread_tractography()are exported, so the formats the pipelines consume can be read and inspected on their own, alongsideread_annotation_data(),read_cifti_annotation(),read_cifti_subcortical(),read_gifti_annotation(),read_neuromaps_annotation(),read_neuromaps_volume()andread_suit_parcellation(). Volume reading reorients FreeSurfer.mgzvolumes to RAS+ as it has always done for NIfTI.
Improvements
Every
create_*()pipeline takesmin_coverage: a newly built atlas whose 2D geometry covers less than 80% of itscorelabels warns and names the labels with no shape.The large-atlas warning has a per-atlas-type vertex budget, each sitting just above its type’s 90th percentile measured across 109 installed atlases, so it flags the heaviest tenth of a family rather than seven atlases in ten, and its advice names
atlas_polish().Data loss is reported whatever the verbosity. A structure that fails to tessellate, a tract whose centerline is degenerate, a label dropped for too few voxels, deep-nuclei data with no
vol_idx, a missing FreeSurfer installation and a dropped cerebellar nucleus are all named rather than left as a lower count;verbosesilences progress chatter only.create_wholebrain_from_volume()says what it inferred and what it lost: how many labels it classified by surface vertex count rather than from a declaration, which labels it assumed subcortical because they reached no cortical vertex, which cortical parcels the projection did not deliver, and which parcels landed on both surfaces, with per-hemisphere vertex counts.A mistyped label in any of
labels$cortical,labels$subcorticalandlabels$cerebellaris reported rather than dropped, and atypeorhemicolumn holding a value that is not one of the accepted ones is an error raised before any projection work.lhandrhare read as a hemisphere only as a prefix, a suffix, or a token between separators, soEntorhinalis no longer right-hemisphere and a label merely containinglhis no longer left.A region named in
corebut absent from the geometry is dropped from all ofcore,palette,meshes_df,vertices_dfandvol_idxin every pipeline, with the dropped labels named, rather than shipping an atlas that claims a region it cannot draw; a build left with nothing to draw aborts.atlas_smooth()no longer multiplies an atlas’s vertex count: rounded corners are taken down to the two or three segments that read the same at plotting size.atlas_smooth()andatlas_simplify()close the hairline gaps they would otherwise open along shared boundaries, leaving genuine anatomical gaps open, controlled byclose_gaps.atlas_simplify()preserves row order, which is draw order.The grey outline behind a subcortical atlas sorts to the bottom layer in every view, including the per-hemisphere
cortex_left/cortex_rightnames the sagittal panels use, so it no longer paints over its own structures.is_cortex_outline()matches case-insensitively and remains anchored.The default subcortical projection slabs tile the bounding box of the atlas’s own labels – three coronal, three axial, one sagittal – rather than slice indices calibrated on a 256-cube, so no panel is empty and no structure is missing from every view whatever the voxel size. The default sagittal slab is clipped to the left of the midline and named
sagittal_left, so left structures are not drawn beneath their right twins.The anatomical context silhouette is taken from a single representative slice per view rather than projected through the slab, from the slice holding the most cortex for axial and coronal and the thinnest section for sagittal, and it is never dilated – all three of which used to flatten the mantle into a blob.
Label ids that collide with the FreeSurfer cortex, cerebellum and brainstem indices the silhouette uses are moved onto free values before the volume and lookup table are written, so a real structure no longer absorbs the silhouette.
project_volume_anatomical()andprepare_subcortical_mni152()share one exact guard against a parcel id merging into a surviving context structure, checked early for whateverprotect_cortexshields.registration = "mni152"refuses a volume stored in right-handed (RAS) voxel order rather than silently mirroring left and right, and registered surface projections pass--srcsubject, so labels come back as integers rather than being resampled into fractional values.Volume handling is honest about what it can place:
read_volume()says when it cannot orient an MGZ,coord_to_voxel()’s fallback reads the voxel size from the header rather than assuming a 1 mm isotropic grid, andcreate_tract_from_volume()declarescoord_space = "mm"rather than leaving a space it built itself to be inferred.FreeSurfer’s own output travels with a failure. stderr is captured whatever the verbosity, echoed at
verbose >= 2, quoted in the error with braces escaped, and chained asCaused by error:byread_fs_surface(),tessellate_smooth_mesh(),subcort_mesh_one()andresample_volume_to_grid(). A failing command carries the condition classggseg_extra_fs_command_error. A missingmris_convertaborts naming the binary rather than returning a mesh with faces pointing at vertices that do not exist.A build with
cleanup = TRUEcannot delete files it did not write. Everycreate_*()function stops before doing any work when its working folder,output_dir/atlas_name, already holds files and was not made by an earlier build.atlas_namemust be a single name: an empty name,".."or one containing a path separator is an error.Mistakes are reported before the expensive work. The cortical creators reject a view or hemisphere they do not know, the tract creators check for
input_asegbefore reading a streamline, an unusablebreaksis rejected before anything is downloaded, andread_neuromaps_volume()reports a missing volume before projecting.cleanupno longer deletes a cache the next run needs: a run that stops short of the last step keeps its working directory and says which steps can reuse it, which is what the documented two-phase inspect-then-continue workflow needs.Arguments are validated before work starts rather than failing deep inside a pipeline:
steps(whole numbers, in range),smoothness(type and length),decimate,registration,tube_segments, and the grouped lists, with an invalid grouped list reported against the argument the caller actually passed and a sub-pipeline option passed at the top level told where it belongs.Every function orders its arguments the same way – the ones without defaults, then the ones with real defaults, then
..., then everything defaulting toNULLor an empty list – and mistyped or misplaced arguments are caught byrlang::check_dots_empty(), which reports the offending value rather than just its name.Pipelines are faster where it counts. The three label-stamping loops make one pass over the volume instead of one per label, about 44x on a 400-parcel atlas with byte-identical output;
create_tract_from_volume()gathers all tracts’ voxels in a single sweep and applies the affine after thinning; the per-voxel argmax inproject_volume_anatomical()streams the running winner instead of materialising ann_voxels x n_labelsmatrix;read_tractography()reads.tckfiles without a quadratic slow-down; andset_sphere_voxels()fills with one matrix-indexed assignment.Pipelines report their total run time the way cli reports each step (
Pipeline completed [1m 15s]), progress labels interpolate the real step total, extracting contours no longer attaches terra to the search path where it maskeddescribe()andextract(), and a relativeoutput_diris made absolute on every platform whether or not the directory already exists.
Bug fixes
A step’s cache records what it was built from – the package version, the cache format and the step’s own inputs – and the step recomputes when any of it moves. It recorded the format alone, so a release that computed something different was reused as though it had not.
read_lut()keeps acontextgroup name instead of flattening it toTRUE.The atlas workflow templates
use_atlas_github_actions()writes resolve dependencies against the ggsegverse r-universe, so an atlas package’s CI can see the ggseg.formats its dependencies name.displayholds the source’s own name for a region again. It fell back toregion, which is now derived, so every atlas built since came out withdisplaya copy ofregion("v1 roi"), losing the human-readable name the column exists to keep.The cerebellar pipeline builds
labelfrom the source name rather than the derived region, somidline_17Networks_1stays itself instead of becomingmidline_17networks_1.labelis the join and palette key, so rewriting it silently broke every dataset keyed on a published cerebellar atlas.The tract pipeline prunes snapshots its slabs cannot name, as the subcortical one already did. Contour extraction reads the snapshot directory whole, so a projection left by an earlier slab configuration – or by the older
cortex_spelling – was read back, and aborted the build on a cache-format mismatch that no rerun could clear.The cerebellar orphan rescue only considers vertices the flatmap triangulation actually uses. The template keeps about a hundred vertices that belong to no face, and the nearest vertex to a deep structure is often one of them, so the rescue reported success and the region was dropped for having no geometry two steps later. That is how
17Networks_14left buckner17 andRight_Region_133, 2362 voxels, left the Shen cerebellar atlas. A structure that reaches no drawable vertex now says so.default_tract_slabs()takes three axial and three coronal slabs, asdefault_subcortical_slabs()does rather than four and five, and drops the ones no label reaches. A tract atlas came out with twelve panels where the subcortical default makes seven, each one thinner for it. The three sagittal slabs stay: the commissural tracts are only properly seen at the midline.create_subcortical_from_volume(),create_tract_from_tractography()andcreate_tract_from_volume()return the atlas they built, instead of running every step, reporting success and returningNULL.read_tractography()reads a.tckfile from where its header says the track data starts, instead of straight after theENDline, which shifted every coordinate by one column in files that pad the header. Coordinates are loaded in one pass, which is considerably faster on large files.create_tract_from_tractography()turns streamlines to run the same way before computing a centerline. A bundle stored in both directions, as most are, used to be shortened or collapsed to a point.The default sagittal views of a tract atlas show the hemisphere they are named for;
sagittal_leftandsagittal_rightwere on each other’s side. They are placed from where the brain sits ininput_aseg, so a volume that is not a 256-voxel, 1 mm cube gets views that cover it.create_tract_from_tractography()names an atlas after its tract, or"tracts"when there are several, instead of after the temporary directory.read_neuromaps_volume(), andcreate_cortical_from_neuromaps()for a volume, keep a parcellation a parcellation: a volume of whole-number labels, or one given alabel_table, is sampled with nearest-neighbour interpolation instead of being blended into fractions and cut into bins.An atlas built from a
.nii.gzvolume without anatlas_nameis named after the file, not<name>_nii.prepare_subcortical_mni152()reports a FreeSurfer step that produced nothing, andproject_volume_anatomical()andcreate_tract_from_volume()read a lookup table file the way the rest of the package does, so a file written bywrite_lut()works.sitrep()no longer reportscreate_cortical_from_annotation()as needing FreeSurfer, and suggests installing neuromapr from CRAN.read_lut()reads a label containing a space as a single token rather than backtracking into a valid-looking parse that shifted every colour channel one field along, andwrite_lut()holdslabeland all five numeric columns to the standard that keeps a row readable, so the round trip is lossless. Label names are no longer truncated to 29 characters, which used to lose a hemisphere suffix and silently merge long labels.tessellate_remap_label()writes its isolated label back as.mgzcarrying the original affine, so a.mgzvolume no longer loses every structure whose label id is above 255 – the normal case for FreeSurfer thalamic nuclei, hippocampal subfields and brainstem substructures.A subcortical rebuild no longer reuses projection snapshots drawn from a different volume and then fails to trace them; each snapshot is checked against its signature and only what moved is redrawn, and a changed
slabsargument is honoured on a cached rebuild.Running
steps = 1:4on a subcortical atlas with an existing cache no longer deletes every snapshot in it.Contours are no longer traced from files belonging to a different slab configuration: images the current configuration cannot name are cleared once the slabs are known, and a contour file matching none of the atlas’s views aborts naming the unmatched files.
Subcortical and tract 2D views are no longer upside down, mirrored or drawn in the wrong plane, and contours record that their y-axis points up.
Tract 2D projections line up with the anatomical reference: the translation
center_meshes()applies for 3D display is undone before rasterising, and the volume is reoriented explicitly rather than relying on an assignment that is a no-op for.mgz.Tract 2D projections reuse the same centerline as the 3D tube rather than recomputing a different 50-point one,
n_pointsreaches the tube increate_tract_from_volume(), andatlas_namenames the atlas object and not only the output directory.tube_radius = "density"reports uniform density and uses the middle ofdensity_radius_rangerather than silently producing the widest possible tube, including for a tract given as a singleN x 3matrix.read_tractography()aborts rather than fabricating coordinates for a.trkfile truncated mid-streamline, reports a header that declares more streamlines than the file holds, and reads to the end of the file when the declared count is0, as the TrackVis specification intends. Atract_nameslength that does not match the number of tracts is reported where it happens.create_wholebrain_from_volume()no longer leaves white holes in the cortical atlas: the volume is filtered to the lookup table before projection, and cortex vertices left unlabelled take the most common label of their neighbours. Lookup table entries the volume never carries are dropped before classification, and cortex is split at the correct 1-based midline voxel.create_wholebrain_from_volume()builds an atlas whose lookup table carries labels with spaces in them, sanitising them for the intermediate table it hands itself, so Neuromorphometrics-style tables round-trip without shifting colours.The brain silhouette behind a subcortical atlas is no longer built from the atlas’s own parcels when their ids land between 1000 and 2999; the plain
asegcortex labels win whenever the volume carries both.create_cerebellar_from_volume()builds from float-typed parcellation volumes,volumeis forwarded bycreate_cerebellar_from_gifti()andcreate_cerebellar_from_annotation()so their atlases carry 3D meshes, anddecimateis honoured by all three cerebellar creators. Trilinear resampling skips non-finite deformation coordinates rather than failing.create_cortical_from_neuromaps()honourslabel_tableon the volume path, and neither it norread_neuromaps_volume()aborts with'breaks' are not uniqueon a map with tied values; duplicate quantile breaks are collapsed with a warning and an all-medial-wall hemisphere errors clearly.create_cortical_from_labels()andcreate_tract_from_tractography()keep region names wheninput_lutis a FreeSurfer-style file path, falling back tolabelwhere there is noregioncolumn.read_cifti_annotation()andcreate_cortical_from_cifti()work on real CIFTI files, reading label names from the label table’s row names, warn when a file carries labelled subcortical voxels the cortical pipeline ignores (#85), and warn when a file holds more than one label map.atlas_dilate()with a negativeamountlarge enough to erode a region away warns naming the regions lost and removes them fromcore, the palette and the geometry, so the result still validates as aggseg_atlas.project_volume_anatomical()warns whenprotect_cortexremoves a label entirely, naming the lost labels – the defect that shipped ggsegJHU’s ICBM-DTI-81 atlas with its right superior longitudinal fasciculus missing.Annotation files whose colour table already defines an
unknownregion no longer produce a duplicate label, and parcellations that name their medial wall (FreeSurfer_Defined_Medial_Wall,medialwall,???) are treated as context while parcels such asmedialorbitofrontalstay regions.Contour extraction no longer crashes on empty or all-
NAregion rasters, keeps the valid contours when only some are empty, and reports a clear error when no region yields a contour.freesurferformatsis checked before use in every path that needs it,decimate_mesh()guardsrglas well asRvcg, andensure_fs_compatible_nifti()falls through gracefully when a NIfTI header cannot be read.prepare_subcortical_mni152()converts the aseg throughfreesurfer::mri_convert()rather than working around it (#118), andmri_infois called with a shell-quoted path so volume paths containing spaces work; region names containing()or/are handled throughout.Boolean and verbosity options parse spelled-out strings consistently across the explicit, option and environment-variable channels, so
GGSEG_EXTRA_VERBOSE=falsesilences output and"yes"/"1"are honoured.as_verbosity()falls back as documented for a value that is not length 1, and reads a factor by its label.The bundled atlas template generates a package that passes
R CMD check: placeholders are bare identifiers that parse as R, the scaffolded build script calls the current API, the licence declaration matches the shippedLICENSE,.lintrexcludesdata-raw/, and the generated workflows run on pull requests.setup_atlas_repo()no longer copies the template’s own.github/directory or rewrites files under.git, and reports failed copies.
Documentation
Six vignettes – getting started, pipeline configuration, post-processing, system setup, legacy conversion, contributing – and, on the package website, an atlas creation workflows article with six verified diagrams plus nine step-by-step tutorials covering the cortical, label, subcortical, cerebellar, whole-brain, tract, neuromaps, lookup-table and atlas-publishing workflows. Each fact has one home.
The tutorials are built from reproducible
.qmd.origsources, plot the atlas at each stage of a build with vertex counts beside it, and teach the current API:atlas_polish(),context_pattern(),plot()for an overview andggplot() + geom_brain()for a figure, rather than the defunctggseg(). Five of them are knitted on every pull request.vignette("post-processing")leads withatlas_polish()and covers polishing a context silhouette and a structure core as the separate problems they are: the thin ribbon gently withmethod = "chaikin", the solid nuclei more firmly with the defaultclose.Examples for
atlas_simplify(),atlas_smooth(),atlas_polish(),atlas_dilate(),count_vertices()andcontext_pattern()run underR CMD check, so the figures the documentation quotes are produced rather than asserted, and a test parses every documented example and asserts that each argument it names is one the function accepts.Help pages are scannable: each
@paramsays what to pass in a sentence or two with the reasoning moved to@details, the twelvecreate_*()functions share@family atlas creationand point atatlas_polish(), every creator describes what its returned object holds, and the four anatomical coregistration functions say which to reach for and why.The reference index groups geometry shaping, curation and file reading under their own headings, the re-exported
atlas_*verbs have a visible?atlas-verbspage naming each verb and what it changes, and the website separates conceptual Articles from step-by-step Tutorials.The package-level help topic maps the five pipeline families to their creators and points at
sitrep(),atlas_polish()andsetup_atlas_repo(), and the README shows a four-line first atlas and states thefreesurfer >= 1.9.0floor.Reading annotation files needs the
freesurferformatsR package, not a FreeSurfer installation, andSystemRequirementsno longer claims FreeSurfer for the tract pipelines, which never call it.
ggseg.extra 1.6
1.6.0
- Removed rgdal dependency, replaced with sf/terra (#49, #59)
- Fixed r-universe API calls (JSON array format change)
- Fixed vignette build issues with conditional evaluation for suggested packages
- Replaced reticulate/kaleido with webshot2 for plotly screenshots
- Updated system setup vignette with new requirements
- Added documentation for parallel processing and progress bars
- Added note about freesurfer dev version requirement
- Updated CITATION to use bibentry()
- Updated pkgdown site with ggseg brand styling
- Fixed mris_label2annot example documentation
ggseg.extra 1.5
1.5.33.003
- small bug fix that prevented calls to FreeSurfer
- Possibility to initiate new atlas project from the RStudio Project GUI
ggseg.extra 1.5.33
- removes purrr dependency
- used ggseg r-universe as install repo for install functions