Skip to content

Changelog

0.7.1.dev0

Current development version for the next ConfUSIus release.

✨ Enhancements

  • FirstLevelModel accepts show_progress=True to display a progress bar over the runs being fitted (#442).

⚡ Performance

🐛 Fixes

  • FirstLevelModel.compute_contrast no longer emits a divide-by-zero RuntimeWarning on recordings containing voxels with no variance over time, such as those outside the recorded field of view (#442).

📚 Documentation

  • Clarified when to use .compute() or .persist() before repeated partial reads from gzip-compressed NIfTI files (#441).

🖼 Napari plugin

  • Scrolling the sidebar with the mouse wheel no longer gets hijacked by whichever combo box or spin box the cursor happens to be over (#431).

0.7.0

Released 2026-08-31.

💥 Breaking changes

VoxelData model (#278):

  • ConfUSIus' canonical dims changed from (...extra, time, pose, z, y, x) to (...extra, time, pose, k, j, i). World coordinates z/y/x are no longer stored dimensions: they're derived lazily, per voxel, from a single voxel-to-world affine owned by a custom xarray index ([VoxelToWorldIndex][confusius._utils.geometry.VoxelToWorldIndex]) attached to native voxel dims k/j/i. This lets ConfUSIus represent oblique, rotated, or sheared acquisitions and registration outputs exactly, without resampling onto an axis-aligned grid. Every loader (load_scan, load_nifti, AUTC, EchoFrame), registration function, plotting path, napari layer, and I/O round trip (Zarr, NIfTI) was migrated to this model; confusius.validation.validate_voxeldata/ensure_voxeldata enforce it unconditionally.
  • World-space units moved off the z/y/x coordinates' .attrs onto [VoxelToWorldIndex][confusius._utils.geometry.VoxelToWorldIndex] as a single shared property, exposed via data.fusi.affine.units and set with data.fusi.affine.set_units. Setting data.coords["z"].attrs["units"] directly no longer has any effect: world coordinates are always regenerated fresh from the index.
  • Multi-pose data now carries pose-dependent voxel-to-world geometry. Sequentially acquired multi-pose data correspondingly carries a pose-dependent, (time, pose)-shaped time coordinate holding each pose's own real acquisition timestamps directly, replacing the old 1D time + pose_time sidecar coordinate convention. New stack_poses assembles independently loaded single-pose grids (e.g. one NIfTI file per probe position) into a single pose-dependent DataArray; load_scan's 3Dscan/4Dscan modes, consolidate_poses (which also dropped its affines_key parameter and now always reads per-pose positions from the primary voxel-to-world geometry), and correct_slice_timings all build on this.
  • extract_with_mask/extract_with_labels now require canonical VoxelData input for both data and mask/labels, and check alignment via the full voxel-to-world affine (not just matching k/j/i integer ranges) — two arrays on different physical grids that happened to share voxel-index ranges no longer silently pass as aligned. validate_atlas similarly no longer accepts a plain, non-indexed atlas shape.
  • Split validate_mask/validate_labels into a pure check (mask/data must already be canonical VoxelData; returns None) and new ensure_mask/ ensure_labels (canonicalize via ensure_voxeldata, then validate; returns the canonicalized/coerced array) — mirroring validate_voxeldata/ensure_voxeldata. Callers that relied on validate_mask/validate_labels's return value should switch to ensure_mask/ensure_labels.
  • PCA/FastICA/ NMF are now VoxelData-only; the previously documented dual-input support for an already-reduced (time, region) signals table (e.g. extract_with_labels output) is removed, along with the resulting feature_names_in_ attribute. Decomposing a signals table is regular tabular PCA/ICA/NMF with no spatial structure to track, so use scikit-learn directly for that instead. unmask correspondingly now always requires a VoxelData mask and always returns a VoxelData array.
  • cf.io.load's .zarr branch and the napari Zarr reader now reject stores that don't contain attrs["voxel_to_world"], instead of silently loading them as non-canonical data. Use xarray.open_zarr directly for a foreign Zarr store.
  • Renamed "physical" to "world" throughout the public API (attrs["affines"] keys such as world_to_sform, function/parameter names, docs) to describe the coordinate space, reserving "physical units" for the mm-vs-voxel-index unit distinction (standard ITK/NIfTI usage). affine_to was renamed to get_relative_affine.

Follow-on API cleanup (#322):

  • Renamed create_fusi_dataarray to create_voxeldata, validate_fusi to validate_voxeldata, ensure_fusi to ensure_voxeldata, and canonicalize_fusi to canonicalize_voxeldata. Removed the separate create_iq_dataarray, validate_iq, and ensure_iq APIs; IQ data is now built with create_voxeldata, with transmit_frequency and beamforming_sound_velocity passed via attrs and validated using require_velocity_attrs=True.
  • Removed consolidate_poses's sweep_dim parameter. The swept voxel dimension is now always auto-detected from the per-pose voxel-to-world geometry (the pose-translation direction matched against each voxel dimension's world-space direction); a sweep that isn't cleanly aligned with a single voxel dimension can never form the regular grid consolidation requires, so no override was needed.

Other:

✨ Enhancements

VoxelData model:

  • Added reindex_voxels/ reindex_voxels_like to .fusi.affine, rebasing voxel-space coordinates to dense positions via plain 4x4 matrix composition (#278).
  • plot_volume (and napari layers) always displays in world space, like nilearn: an axis-aligned world plane (z/y/x), or a non-spatial dim (e.g. slice_mode="pose" facets a multi-pose array over its poses). Oblique voxel-to-world data is always resampled onto the world-axis-aligned frame for display, each volume keeping its own native per-axis resolution, controllable via new resample_interpolation/resample_fill_value parameters on plot_volume/plot_composite/plot_stat_map/VolumePlotter/ .fusi.plot.napari; transpose=True swaps which display dim is drawn on rows versus columns (#278).
  • NIfTI and Zarr I/O round-trip oblique/rotated/sheared voxel-to-world geometry exactly; load_nifti composes the full primary qform/sform affine into voxel_to_world instead of decomposing it into axis-aligned scale/origin (#278).
  • Added ensure_voxeldata to canonicalize and validate VoxelData inputs with one call, and added create_voxeldata to build VoxelData from a raw array plus higher-level metadata (dt, spacing, axis origins, attrs). It attaches regularly spaced world coordinates, units metadata, and validates the result before returning it (#322).

Other:

  • Added VolumePlotter.add_stat_map, the overlay-only counterpart of plot_stat_map (#392).
  • clean, regress_confounds, censor_samples, and interpolate_samples now accept NumPy confounds and sample_mask (time along the first axis); they take the signals' time coordinates and warn since alignment cannot be verified, as do DataArrays without time coordinates. confounds can also be a DataFrame with a time column, validated like DataArray time coordinates; its other columns must be numeric and unique. FirstLevelModel.fit and make_first_level_design_matrix now accept confounds as a (time, n_confounds) DataArray, validated against the run's time coordinates (#398).
  • register_volume now supports random metric sampling via metric_sampling_percentage (None by default, disabling random sampling), with optional deterministic seeding via metric_sampling_seed, to speed up large affine or B-spline registrations (#396).
  • register_volume gained fixed_intensity_scaling/moving_intensity_scaling and register_volumewise gained intensity_scaling (all "none" by default) to rescale the images passed to the registration optimizer without affecting the returned/resampled data: "db", "sqrt" (an alias for 0.5), or any positive float exponent for power scaling. [Napari plugin] The Registration panel exposes the same selectors (#405).
  • db_scale and data.fusi.scale.db now default factor to 20 for complex-valued (amplitude) data and 10 otherwise, instead of always defaulting to 10 (#414).
  • load_nifti now follows the BIDS inheritance principle for matching JSON sidecars, so shared metadata stored at the dataset root or parent folders is preserved when loading recordings (#359).

⚡ Performance

  • Atlas.get_masks/get_atlas_masks no longer forces xarray.concat to recompute and compare the full lazily derived world-coordinate grid across every requested region (all layers share one grid by construction), the dominant cost for multi-region calls; it also now scans the annotation volume once per 8-region batch via a bitmask lookup instead of once per region. Together, a get_masks([...]) call over dozens of regions (e.g. combining all of an ontology's major divisions into one coarse map) is over an order of magnitude faster (#412).

🐛 Fixes

  • clean, regress_confounds, censor_samples, and interpolate_samples now handle signals with pose-dependent (time, pose) time coordinates: confounds and sample_mask are aligned with the whole-volume time (as consolidate_poses computes it), NumPy inputs take that time, and signals are interpolated pose by pose (#398).
  • plot_volume/plot_composite now default planar VoxelData arrays to their singleton world dimension and preserve singleton display axes for explicit spatial slicing. plot_napari/fusi.plot.napari now default singleton spatial axes to sliders instead of the canvas, regardless of how the voxel-to-world affine maps them (#407).
  • save_nifti now always writes both a qform and sform (previously sform was silently dropped when no secondary affine had been explicitly recorded) (#278).
  • load_scan now opens Iconeus SCAN v1 files marked as 4DscanCustom (#406).
  • plot_composite no longer produces a blank/NaN composite when either input has been scaled with .fusi.scale.db(); the -inf values db_scale assigns to zero-valued voxels are now excluded from the normalization bounds (#370).
  • .fusi.scale.db() and .fusi.scale.log() no longer emit a RuntimeWarning for zero/negative values when applied to Dask-backed data (#379).
  • FirstLevelModel.fit no longer errors on multi-pose data. Its implicit all-True mask (used when no mask is passed) now covers pose when the input has it, instead of collapsing to a single pose; the explicit-mask path no longer rejects a pose-carrying mask either (#278).
  • plot_napari and the napari plugin no longer crash on a non-spatial dimension with string coordinates (e.g. a recording-id stack dim); such a dimension now falls back to scale 1/origin 0 like any other missing world geometry (#409).

🔧 Maintenance

  • [Napari plugin] ConfUSIus now requires napari 0.9.0 or newer (#413).

🔧 Maintenance

  • Bumped the brainglobe-atlasapi dependency to v3 (#412).

0.6.1

Released 2026-08-07.

✨ Enhancements

  • Added fetch_pereira_2025 and fetch_pepe_mariani_2026 for the new OSF-hosted fUSI-BIDS re-exports (#361).
  • load_echoframe_dat now returns a ConfUSIus-ordered (time, z, y, x) DataArray and defaults meta_path to the sibling ScanParameters.mat file (#343).
  • New confusius.decoding module with SearchLight, which maps how well a cross-validated scikit-learn estimator predicts a target from the local neighborhood of each voxel (#334).

🐛 Fixes

  • Time resampling now keeps floating-point input dtypes and avoids unnecessary SciPy interpolation copies, reducing memory use for large float32 arrays (#353).
  • NIfTI loading now keeps nibabel data lazy under Dask, EchoFrame .dat loading is now lazily chunked, and EchoFrame metadata reads current xAxis/zAxis fields (#343).
  • Confound regression now z-scores confounds when standardize_confounds=True, so motion-confound cleaning removes fluctuations without regressing baseline-related signal by default (#351).

0.6.0

Released 2026-07-18.

💥 Breaking changes

  • The Atlas class has been replaced by an xarray.Dataset with a registered .atlas accessor. Fetch an atlas by name with fetch_brainglobe_atlas and call operations through ds.atlas.* (ds.atlas.get_masks, ds.atlas.get_mesh, ds.atlas.search, ds.atlas.ancestors, ds.atlas.resample_like); resample_like now returns a Dataset. Name-based loading moved to confusius.datasets; atlas construction from a loaded BrainGlobe atlas is now internal to the datasets module (#274).
  • resample_volume and resample_like now use fill_value instead of default_value for out-of-field-of-view resampling, matching register_volume and the progress-plot resampling API.
  • Motion diagnostics helpers now require actual affine matrices: extract_motion_parameters, compute_framewise_displacement, and create_motion_dataframe no longer accept None placeholders in their affine lists (#302).
  • Renamed the public BIDS table I/O helpers to match the rest of ConfUSIus: read_eventsload_events, and write_eventssave_events (#294).
  • fetch_landemard_2026 now resolves the dataset from OSF project 7cf9g instead of dkseb. Existing local caches may need a one-time refresh=True to replace the cached OSF file index before downloading or checking for upstream updates (#311).
  • Axial velocity processing now always uses the standard Kasai estimator (arg(mean(R1))); the estimation_method and absolute_velocity arguments were removed, the corresponding metadata fields were dropped, and spatial_kernel now defaults to 3 and accepts explicit (z, y, x) sizes (#313).

✨ Enhancements

  • Atlases are now serializable: save and reload a complete atlas, including its structure hierarchy and region meshes, with save_atlas / load_atlas. The region .obj meshes are bundled into the Zarr store, so a reloaded atlas renders meshes without the BrainGlobe cache (#274).
  • validate_atlas_dataset checks that a Dataset is a well-formed atlas (#274).
  • load_scan now opens binary Iconeus SCAN v2 files in addition to HDF5 SCAN v1 files, detecting the format automatically. SCAN v2 support is experimental: data, timing, voxel spacing, the depth origin, provenance (subject/session/project/scan/experimenter, serial number, acquisition datetime), and BIDS-corresponding acquisition settings (probe model, center/transmit frequencies, pitch, focal depth, imaging depth, PRF, plane-wave angles, SVD low cutoff, power-Doppler integration window) are recovered (lateral and elevation axes are centered on zero). A physical_to_lab affine is derived from a header block read as a 6DOF probe pose (experimental, assumed convention), and a BPS sidecar composes a physical_to_brain affine as for v1. Multi-pose layouts are inferred (#317).
  • [Napari plugin] Added an interactive registration panel for volume alignment in napari, including linear and non-linear transforms, progress preview, manual and automatic initialization, saving/loading transforms, and forward/inverse transform application (#216).
  • Added plot_motion_diagnostics to visualize motion-correction summaries from motion_params tables returned by register_volumewise (#302).
  • Added plot_design_matrix to visualize a first-level GLM design matrix as a heatmap, with regressor names along the top and an optional acquisition-time y-axis (index_yaxis) (#331).
  • Added plot_contrast_matrix to visualize a GLM contrast, given as a string expression or a numeric vector/matrix, as a weight strip aligned with the design regressors (#331).
  • create_motion_dataframe now always reports all named rotation / translation axes exposed by the affine dimensionality, even when one spatial axis is singleton (#302).
  • Added load_physio to load BIDS physio TSV files with column names and metadata from the JSON sidecar, synthesizing a time column when needed; the napari plugin now uses it for imported signal tables (#294).
  • Added fetch_khallaf_2026 for downloading the Khallaf et al. (2026) naked mole-rat fUSI dataset from Edmond, with datasets, subjects, sessions, runs, reconstruction, and sourcedata filters (#319).
  • Added standalone cosine high-pass filtering via filter_cosine, and clean can now use it with filter_method="cosine" (#321).

🐛 Fixes

  • Axial velocity estimation now scales the Kasai phase increment by the requested autocorrelation lag, so multi-volume lags no longer overestimate velocity (#313).
  • NIfTI loading no longer crashes when a sidecar VolumeTiming length disagrees with the actual data. ConfUSIus now ignores the malformed sidecar timing, falls back to pixdim[4] when available, and otherwise warns before using frame indices (#304).
  • Motion parameter tables from create_motion_dataframe now label rotations and translations by the coordinate names x/y/z instead of by raw transform-component order, so canonical ConfUSIus arrays stored as (z, y, x) no longer mislabel in-plane motion (#301).
  • [Napari plugin] The signal import dialog now finds BIDS physio files ending in .tsv.gz, keeps the x-axis cursor visible for imported-only plots when enabled, and lets you import multiple signal files in one go (#294).
  • Opening a .scan file that is not the legacy HDF5-based Iconeus format now raises a clear error that points users to newer SCAN v2 files and to converting them to NIfTI with Iconeus tools first (#297).
  • Plotting functions now accept a slice dimension reduced to a scalar coordinate by a single-index selection, so plot_contours(atlas.annotation.sel(z=6)) works like sel(z=[6]) (#296).

📚 Documentation

🔧 Maintenance

  • [Example Gallery]: pandas DataFrame outputs in the example gallery now render with clean, theme-aware notebook styling instead of a fully-bordered table (#307).
  • [Example Gallery]: Cells can now hide their code behind a collapsed callout with a collapse cell tag, with optional custom title and type (collapse[<type>]: <title>), i.e. # %% tags=["collapse[warning]: Collapsed warning"] (#309).
  • [Example Gallery]: Hovering a gallery card reveals the example's first paragraph as an overlay over the card (#327).

0.5.2

Released 2026-07-10.

🔧 Maintenance

  • Python 3.14 now keeps xarray[accel] everywhere except macOS Intel, where ConfUSIus falls back to plain xarray to avoid a numba / llvmlite build failure caused by napari's macOS Intel numba<=0.62.1 cap.

0.5.1

Released 2026-07-10.

✨ Enhancements

  • [Napari plugin] Added a File > Open Sample entries for a Nunez-Elizalde 2022 mouse recording and for a pair of Cybis Pereira 2026 rat recordings. Samples are fetched on demand, shows download progress with an abort button, and only downloads the matching raw fUSI files instead of the full dataset (#273).
  • Dataset fetchers now print the citation to use for the fetched data and accept a print_citation argument to silence it. The template fetchers fetch_template_huang_2025 and fetch_template_pepe_mariani_2026 also expose the citation on the returned DataArray as da.attrs["citation"] (#279).
  • Dataset fetchers called with refresh=True now re-download cached files whose upstream MD5 changed, comparing the cached dataset index against the freshly fetched one instead of only checking whether the file exists; downloads are additionally verified against the index MD5. A locally cached dataset whose dataset_index.json predates this format is detected on fetch and reported with a clear error naming the directory to delete and re-fetch, rather than being silently mishandled. Affects fetch_cybis_pereira_2026, fetch_nunez_elizalde_2022, and fetch_landemard_2026 (#261).
  • Added sample_displacement_field, sample_displacement_field_like, and invert_displacement_field to sample a B-spline (or composite affine + B-spline) registration transform into a dense displacement field and invert it via SimpleITK's InvertDisplacementFieldImageFilter. resample_volume and resample_like now also accept displacement fields directly, so a saved B-spline transform's inverse can be applied without a closed-form inverse (#235).

🐛 Fixes

  • Saving to Zarr (via save or DataArray.fusi.save) now works for data carrying affines or other numpy-valued attributes: nested numpy arrays are stored as lists and non-serializable attrs (e.g. matplotlib colormaps) are dropped with a warning, matching the NIfTI sidecar behaviour (#284).
  • B-spline control-point DataArrays returned by register_volume no longer have their per-axis grid geometry (spacing, origin, domain) swapped between axes on anisotropic images. The bug was invisible on isotropic data, which is why it went unnoticed since it shipped #235.
  • plot_napari no longer sets viewer.scale_bar.unit (and the napari 0.7.0 FutureWarning is gone for good). The previous workaround in #271 is no longer needed: napari ≥ 0.7.1 infers the scale bar unit from the layer's units attribute, which plot_napari already forwards from the spatial coordinates.
  • plot_volume and friends no longer crash on matplotlib ≥ 3.11 when a threshold is set. LinearSegmentedColormap.from_list now requires strictly monotonic (value, color) pairs, and the threshold gray band could collide with neighbouring cmap entries at the boundary values.
  • plot_volume, plot_stat_map, plot_composite, and VolumePlotter.add_contours no longer silently reorder panels when slice_mode's own coordinate isn't already sorted (e.g. a region dimension built from an arbitrary list of acronyms, or a descending z). Only the two display dimensions are sorted for plotting geometry now (#268).

📚 Documentation

  • The same-subject registration example now follows the rigid registration step with a B-spline refinement, showing the extra local correction it adds and how its parameters differ from the rigid step's (#235).
  • Long output in gallery examples—warnings, text reprs, tracebacks, and rich-rendered text such as the dataset citation banner—now wraps instead of showing a horizontal scrollbar (#285).

🔧 Maintenance

  • Raised the minimum supported versions to napari 0.7.1 and matplotlib 3.11.
  • The example-gallery build tool now accepts specific example scripts as arguments (uv run python tools/build_gallery.py docs/examples/01_io/01_confusius_xarray_101.py), running only those; the rest of the gallery is still rendered, taken from cache if present or built without outputs (#285).

0.5.0

Released 2026-07-07.

💥 Breaking changes

  • Registration now takes a single initialization parameter in place of centering_initialization and initial_transform. initialization accepts "center_geometry", "center_moments", a homogeneous affine matrix or None for an identity initialization. Affects register_volume, register_volumewise, and the data.fusi.register accessor (#215).

✨ Enhancements

  • Added plot_matrix for plotting 2D matrices (e.g. connectivity or correlation matrices), with optional lower/diagonal triangle masking, grid lines, and a groups parameter that annotates contiguous label runs with colored rectangle strips—useful for marking anatomical groupings (e.g. cortex, thalamus) when there are too many individual labels to read (#243).
  • [Napari plugin] Integer-dtype files (e.g. atlas annotations, ROI masks) opened via the confusius CLI, the Data Panel, or the native napari file readers (drag-and-drop / File > Open) are now added as a Labels layer with per-label colors, instead of an Image layer with the wrong colormap (#257).
  • [Napari plugin] Added Events panel to annotate temporal events within Napari. Events shade the signal plot; active event names appear in the time overlay; load from / save to a BIDS .tsv (#176).
  • confusius.bids module is now public with new load_events and save_events (#176).
  • Added a datasets CLI namespace, listed in confusius --help: confusius datasets --list prints the table of available datasets, their sizes, and whether each is cached on disk. A bare confusius PATH... still launches the viewer (#234).
  • Added NMF for non-negative matrix factorization of fUSI time series, wrapping sklearn.decomposition.NMF with the same xarray-aware fit/transform/inverse_transform interface as PCA and FastICA. Both mode='temporal' and mode='spatial' are supported (#211).
  • Added adjust_pvalues for generic multiple-comparison correction of p-value maps, and apply_statistical_threshold to threshold z-scaled statistical DataArrays with the same family-wise-error (Bonferroni, Šidák, Holm, Holm-Šidák, Simes-Hochberg, Hommel) or false-discovery-rate (Benjamini-Hochberg, Benjamini-Yekutieli) corrections, plus an optional cluster-extent threshold (#204).
  • Added fetch_landemard_2026 for downloading the Landemard et al. (2026) fUSI-BIDS dataset from OSF, with datasets, subjects, acqs, and datatypes filters (#228).
  • Added plot_stat_map (and the matching data.fusi.plot.stat_map accessor) for plotting statistical maps, optionally overlaid fully opaque on a background anatomical volume. vmin/vmax default to the data's actual min/max, and auto_range=True (default) picks both the colormap range and colormap from the data's sign: diverging symmetric [-m, m] with "coolwarm" when both signed, sequential [0, vmax] with "viridis" when non-negative, or [vmin, 0] with "viridis_r" when non-positive (#242).
  • plot_volume and plot_stat_map (and their data.fusi.plot.* accessors) now accept cbar_kwargs, forwarded to matplotlib.figure.Figure.colorbar—useful to shrink a shared colorbar down to size on a multi-panel grid (#242).
  • apply_affine and the data.fusi.affine.apply accessor now accept a string naming a key in attrs["affines"], instead of requiring the affine matrix itself (#247).
  • Plotting functions that slice along slice_mode ( plot_volume, plot_contours, plot_composite, and the VolumePlotter methods) now support non-numeric coordinates (e.g. region/mask labels), so a single call can slice a stacked connectivity or ROI map by label instead of looping over .sel() per panel (#250).

🐛 Fixes

  • plot_volume and other image plotting functions now raise a clear ValueError when vmin/vmax (or a passed-in norm) resolve to a non-finite value, instead of crashing deep inside matplotlib.colors.LinearSegmentedColormap.from_list with an opaque IndexError (#259).
  • save_nifti now drops attrs that cannot be serialized to JSON as-is (e.g. matplotlib ListedColormap/BoundaryNorm objects) instead of writing their str() repr into the sidecar, which could corrupt fields such as cmap on reload. A warning lists the dropped keys. confusius.load now rebuilds cmap/norm from rgb_lookup when they are missing, so atlas-derived masks and annotations keep their canonical colors after a save/load round-trip. [Napari plugin] The reader now falls back to the "gray" colormap (with a napari warning) instead of crashing when a layer's cmap attr is not a valid napari colormap name (#255).
  • Atlas.get_masks now suffixes the mask coordinate with _L/_R for sides="left"/"right", so requesting the same region on both hemispheres no longer produces duplicate mask values. extract_with_labels no longer requires unique region ids across stacked mask layers—a layer is already identified by its position along mask—so get_masks output can be passed straight through without manual relabeling (#249).
  • apply_affine now rescales the voxdim attribute of the spatial coordinates along with the coordinate values (#245).
  • clean now supports ensure_finite=True to repair non-finite signals and confounds by interpolating along time, fills censored boundary samples from the nearest kept sample before filtering, and accepts interpolate_kwargs for pre-scrubbing interpolation (#239).
  • Image plotting functions now leave alpha unset by default (None), so a colormap's built-in alpha channel is respected (#225).
  • load_nifti no longer drops affines loaded from the JSON sidecar (e.g. bspline_initialization written by the registration pipeline) when merging in the NIfTI qform/sform affines (#222).
  • save_nifti no longer maps non-time additional axes to the NIfTI 4th slot. When additional axes are present in the DataArray, a degenerate length-1 time axis is inserted at NIfTI axis 4 (NIfTI's conventional time slot) so non-time additional axes always land at NIfTI axes 5, 6, 7. The original dim name for each additional axis is always written to the sidecar as ConfUSIusDim{N}Name (with N in 4, 5, 6, matching the 0-based NIfTI axis of the extra dim). The matching ConfUSIusDim{N}Coordinates entry is only written when the coord cannot be reconstructed from pixdim (i.e. when the coord does not start at 0 with regular spacing); otherwise the spacing is stored in pixdim and the coord is rebuilt as step * arange(size) on load. Attributes are preserved in ConfUSIusDim{N}Attributes entries. (#223).

📚 Documentation

  • Add an NMF example to the gallery, demonstrating the z-score + absolute-value standardization that makes signed fUSI signals NMF-compatible.
  • Add an atlas-based region correlation matrix example to the gallery, demonstrating registration to the Pepe-Mariani 2026 template, resampling the Allen Mouse Brain Atlas onto a recording's native grid, and plotting a region correlation matrix with plot_matrix's groups annotation (#243).

🔧 Maintenance

  • Simplified the NIfTI save path: time and extra-dimension voxel spacings are now written directly to the header pixdim instead of through nibabel's set_zooms (that was overwritten anyways), dropping a redundant spatial write that the qform immediately overwrote. Behavior is unchanged. (#253).

0.4.0

Released 2026-06-25.

✨ Enhancements

  • The confusius CLI now accepts multiple fUSI data files in a single invocation (e.g. confusius fixed.nii moving.nii). Each file is added as its own image layer, named after the file's basename (#206).
  • data.fusi.affine.apply now accepts affines with rotation and shear. The axis-aligned part updates the 1D z/y/x coordinates and the method returns the residual orientation as a 4x4 affine (the identity for diagonal affines) for the caller to use as they wish (#188).
  • Add smoothing_fwhm parameter to FirstLevelModel. Smoothing is applied to each run before model fitting (#201).

⚡ Performance

  • process_iq_blocks now uses dask.array.map_blocks for non-overlapping outer IQ windows and batches overlapping windows with explicit overlap before mapping blocks, reducing Dask overhead in common blockwise processing workflows (#190).

🐛 Fixes

  • Masks are now coerced to boolean by validate_mask (added return_dtype_as_bool parameter that defaults to True) to avoid DataArrays using positional indexing. Previously these masks could select the wrong voxels or, for register_volume, silently disable the metric mask (#197).
  • process_iq_blocks now handles strongly overlapping IQ windows without corrupting the output time dimension, so power Doppler and related IQ reducers work when window_stride < window_width / 2 (#192).
  • load_nifti now anchors physical_to_qform to the same physical frame as the primary (sform) coordinates, so the stored qform affine maps the array's physical coordinates to qform world space (#187).
  • save_nifti now preserves each affine's own translation, so a NIfTI file with sform and qform round-trips through load_nifti/save_nifti without corrupting the qform (#187).
  • [Napari plugin] Fixed the Signals plot x-axis for volumes without a time dimension. It now follows the slider axis world coordinates, with a matching label and dropdown option (#180).

0.3.0

Released 2026-05-27.

💥 Breaking changes

  • register_volume now also returns a RegistrationDiagnostics dataclass with the per-iteration metric values, final metric value, iteration count, optimizer stop condition, and the metric name. register_volumewise always adds per-frame final_metric_value and n_iterations columns to motion_params, and exposes the full per-frame diagnostics list under attrs["registration_diagnostics"] only when called with keep_diagnostics=True to avoid retaining the full optimizer metric trace by default (#139).
  • Renamed validate_voxeldata to validate_fusi_dataarray (#153).

✨ Enhancements

  • Added a mask argument to the PCA, FastICA, SeedBasedMaps, and FirstLevelModel estimators, restricting fitting and projection to the selected voxels. Output maps retain the full spatial geometry, with voxels outside the mask set to 0 (#155).
  • Added plot_composite, VolumePlotter.add_composite, and a matching data.fusi.plot.composite accessor that render two volumes as a red/cyan RGB overlay (#145).
  • Added datatypes filter to fetch_cybis_pereira_2026, allowing downloads to be scoped to specific BIDS datatype directories ("fusi", "angio", "motion") (#141).
  • Added fetch_template_huang_2025 for downloading and loading the Huang et al. vascular mouse template from OSF, with cache/refresh behavior matching existing template fetchers (#162).
  • Added show_progress to volumewise registration so joblib progress output can be disabled in scripted or quiet workflows (#126).
  • Added a reusable validate_fusi_dataarray validator and refactored IQ/registration validation to use it. Core dimension coordinates are now validated as 1D, numeric, finite, and strictly increasing, while extra/non-dimension coordinates remain allowed (#153).
  • Added shared fontsize parameter to plot_volume, plot_contours, and carpet plotting entry points so text sizing is consistent across all plotting APIs (#128).
  • Replaced plotting black_bg with explicit bg_color and fg_color controls for clearer visual customization (#124).
  • Added FastICA transformer for independent component analysis of fUSI recordings, with the same xarray-aware fit / transform / inverse_transform API as PCA (#118).
  • Added example gallery helper utilities to streamline writing and maintaining docs examples (#102).

⚡ Performance

  • Top-level confusius and confusius.xarray namespaces now use SPEC-0001 PEP 562 lazy loading. Submodules and exported functions are only imported on first access, reducing import confusius overhead for workflows that use a subset of the package.

🐛 Fixes

  • Fixed resample_like and resample_volume filling out-of-FOV voxels with 0.0 when resampling onto a larger grid. This caused a bright background artifact for dB-scaled data (where 0 is maximum intensity). The default_value parameter now defaults to float(moving.min()) instead of 0.0. register_volume gains a fill_value parameter that overrides the default for both the final resampled output and the live progress composite overlay (#138).
  • Fixed plotting hover information silently disappearing when the returned VolumePlotter was not held in a variable (e.g. obj.fusi.plot.volume().show()). The hover manager is now kept alive until the figure is closed (#148).
  • Fixed napari x-axis extent computation to ignore the interactive cursor guide line, preventing incorrect plot bounds (#111).

📚 Documentation

🔧 Maintenance

  • Switched documentation hosting to GitHub Pages with mike versioning and automatic PR preview deployments (#134).

0.2.0

Released 2026-05-05.

First official public beta release of ConfUSIus.

✨ Highlights

  • ConfUSIus now covers the core alpha roadmap, including I/O, beamformed IQ processing, registration, quality control, atlas integration, signal processing, decomposition, functional connectivity, and general linear model workflows.
  • The package provides both a Python API and a napari plugin for interactive data loading, visualization, signal inspection, and quality control.

📝 Notes

  • 0.1.0 was used only to reserve the confusius project name on PyPI and is not a supported public release. 0.2.0 is therefore the first official public release series for ConfUSIus.