Skip to content

Commit 2a3aa8e

Browse files
committed
changes
1 parent 67693b9 commit 2a3aa8e

8 files changed

Lines changed: 77 additions & 54 deletions

File tree

src/rbc/bids/functional.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,22 @@
1717
from rbc.workflows.functional import FunctionalOutputs
1818

1919

20+
def _smooth_label(fwhm: float, precision: int | None = None) -> str:
21+
"""Format FWHM as a BIDS-safe label (e.g. 6.0 -> 'sm6', 0.1 -> 'sm0p1').
22+
23+
Trailing zeros are stripped and '.' is replaced with 'p' for BIDS compliance.
24+
25+
Args:
26+
fwhm: Smoothing kernel FWHM in mm.
27+
precision: Optional number of decimal places to format to before stripping.
28+
29+
Returns:
30+
BIDS-safe label string (e.g. 'sm6', 'sm0p1').
31+
"""
32+
s = f"{fwhm:.{precision}f}" if precision is not None else str(fwhm)
33+
return "sm" + s.rstrip("0").rstrip(".").replace(".", "p")
34+
35+
2036
class FunctionalRun(NamedTuple):
2137
"""A single functional run discovered from a BIDS session.
2238
@@ -186,7 +202,7 @@ def export_functional(
186202
mni.save(
187203
outputs.cleaned_bold_smooth[reg],
188204
suffix=Suffix.BOLD,
189-
desc=f"sm{int(smooth)}preproc",
205+
desc=f"{_smooth_label(smooth)}preproc",
190206
extra={"reg": bids_safe_label(reg)},
191207
)
192208
mni.save(outputs.template_bold, suffix=Suffix.BOLD, desc="preproc")

src/rbc/bids/metrics.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@
1616
from rbc.workflows.metrics import MetricsOutputs
1717

1818

19+
def _smooth_label(fwhm: float, precision: int | None = None) -> str:
20+
"""Format FWHM as a BIDS-safe label (e.g. 6.0 -> 'sm6', 0.1 -> 'sm0p1').
21+
22+
Trailing zeros are stripped and '.' is replaced with 'p' for BIDS compliance.
23+
24+
Args:
25+
fwhm: Smoothing kernel FWHM in mm.
26+
precision: Optional number of decimal places to format to before stripping.
27+
28+
Returns:
29+
BIDS-safe label string (e.g. 'sm6', 'sm0p1').
30+
"""
31+
s = f"{fwhm:.{precision}f}" if precision is not None else str(fwhm)
32+
return "sm" + s.rstrip("0").rstrip(".").replace(".", "p")
33+
34+
1935
class MetricsInputs(TypedDict):
2036
"""Resolved functional inputs for the metrics workflow."""
2137

@@ -70,7 +86,7 @@ def export_metrics(
7086
7187
Raw and z-scored raw maps are always exported. Smoothed and
7288
z-scored smoothed variants are exported only when the corresponding
73-
fields are not None (i.e. when ``smooth=True`` was passed to
89+
fields are not None (i.e. when ``smooth`` is not ``None`` in
7490
``single_session_metrics``).
7591
7692
Args:
@@ -96,7 +112,7 @@ def export_metrics(
96112

97113
# Smoothed + z-scored smoothed
98114
if smooth is not None:
99-
sm_desc = f"sm{int(smooth)}"
115+
sm_desc = _smooth_label(smooth)
100116
if outputs.alff_smooth is not None:
101117
mex.save(outputs.alff_smooth, suffix="alff", desc=sm_desc)
102118
assert outputs.alff_smooth_zscored is not None # noqa: S101

src/rbc/core/common.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
"""Processing steps shared across anatomical and functional streams.
1+
"""Processing steps shared across anatomical, functional, and metrics streams.
22
33
Currently provides:
44
- Deobliquing and RPI reorientation (initial preprocessing for T1w and BOLD).
55
- 4D NIfTI splitting and merging utilities.
6+
- Spatially smooth a 3D map or 4D timeseries to a target FWHM.
67
"""
78

89
from __future__ import annotations
@@ -21,7 +22,7 @@
2122
from rbc.core.nifti import strip_afni_volatile_metadata
2223
from rbc.core.niwrap import generate_exec_folder
2324

24-
__all__ = ["deoblique_and_reorient", "merge_3d_to_4d", "split_4d"]
25+
__all__ = ["deoblique_and_reorient", "merge_3d_to_4d", "smooth", "split_4d"]
2526

2627

2728
def deoblique_and_reorient(
@@ -92,3 +93,33 @@ def merge_3d_to_4d(volumes: Sequence[Path], output: Path) -> Path:
9293
merged = nib.funcs.concat_images(imgs, axis=None)
9394
nib.save(merged, output)
9495
return output
96+
97+
98+
def smooth(
99+
in_file: Path,
100+
mask_file: Path,
101+
fwhm: float = 6.0,
102+
) -> Path:
103+
"""Spatially smooth a 3D map or 4D timeseries to a target FWHM.
104+
105+
Uses AFNI ``3dBlurToFWHM`` to iteratively blur the input until the
106+
estimated smoothness reaches the requested FWHM within the brain mask.
107+
Supports both 3D derivative maps (ALFF, fALFF, ReHo) and 4D BOLD
108+
timeseries.
109+
110+
Args:
111+
in_file: NIfTI image to smooth (3-D map or 4-D timeseries).
112+
mask_file: Binary brain mask; voxels outside are set to zero.
113+
fwhm: Target full-width at half-maximum in mm.
114+
115+
Returns:
116+
Path to the smoothed image.
117+
"""
118+
result = afni.v_3d_blur_to_fwhm(
119+
in_file=in_file,
120+
mask=mask_file,
121+
fwhm=fwhm,
122+
prefix="smoothed.nii.gz",
123+
)
124+
assert result.out_file is not None # noqa: S101
125+
return result.out_file

src/rbc/core/metrics/smoothing.py

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/rbc/workflows/functional.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from niwrap import ants
1515

1616
from rbc.core.common import deoblique_and_reorient
17+
from rbc.core.common import smooth as apply_smooth
1718
from rbc.core.fsl2itk import mat_to_itk
1819
from rbc.core.functional import (
1920
PEPolarFieldmap,
@@ -34,7 +35,6 @@
3435
slice_timing_correction,
3536
truncate_trs,
3637
)
37-
from rbc.core.metrics.smoothing import smooth as apply_smooth
3838
from rbc.core.niwrap import generate_exec_folder
3939
from rbc_resources import REGISTRATION_TEMPLATES
4040

@@ -84,7 +84,7 @@ class FunctionalOutputs(NamedTuple):
8484
file, matching what ``3dTproject -bandpass`` actually applied.
8585
For BIDS export only.
8686
cleaned_bold_smooth: Spatially smoothed nuisance-regressed
87-
& bandpass-filtered BOLD, or *None* is no smoothing requested.
87+
& bandpass-filtered BOLD, or *None* if no smoothing requested.
8888
regressor_file: Bandpass-filtered nuisance regressor ``.1D`` file.
8989
template_brain_mask: Brain mask warped to template space.
9090
"""

src/rbc/workflows/metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
import logging
1313
from typing import TYPE_CHECKING, NamedTuple
1414

15+
from rbc.core.common import smooth as apply_smooth
1516
from rbc.core.metrics.alff import compute_alff
1617
from rbc.core.metrics.reho import compute_reho
17-
from rbc.core.metrics.smoothing import smooth as apply_smooth
1818
from rbc.core.metrics.standardization import compute_zscore
1919
from rbc.core.metrics.timeseries import compute_timeseries
2020
from rbc.core.niwrap import generate_exec_folder

tests/integration/metrics/test_smoothing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@
77
import nibabel as nib
88
import pytest
99

10-
from rbc.core.common import deoblique_and_reorient
11-
from rbc.core.metrics.smoothing import smooth
10+
from rbc.core.common import deoblique_and_reorient, smooth
1211

1312
if TYPE_CHECKING:
1413
from conftest import TestSubjectData

tests/unit/bids/test_exports.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from rbc.bids.anatomical import export_anatomical
1515
from rbc.bids.functional import export_functional
16-
from rbc.bids.metrics import export_metrics
16+
from rbc.bids.metrics import _smooth_label, export_metrics
1717
from rbc.bids.qc import export_qc
1818
from rbc.context import RunContext
1919
from rbc.workflows.anatomical import AnatomicalOutputs
@@ -364,6 +364,10 @@ def test_smooth_maps_have_correct_desc(
364364
assert len(sm_only) == 3
365365
assert len(sm_zstd) == 3
366366

367+
def test_smooth_label_non_integer(self) -> None:
368+
"""Non-integer FWHM values are formatted with 'p' instead of '.'."""
369+
assert _smooth_label(0.1) == "sm0p1"
370+
367371

368372
# ---------------------------------------------------------------------------
369373
# QC exports

0 commit comments

Comments
 (0)