Compute a masked temporal-SNR map and write it so the Receipt is embedded in the .nii.gz itself — no sidecar JSON, no manifest.
Code
from pathlib import Pathimport sysimport tempfilesys.path.insert(0, str(Path.cwd().parents[1] /"src"))import nibabel as nibimport neuroim as nirepo = Path.cwd().parents[1] if Path.cwd().name =="cookbook"else Path.cwd()bold = ni.read_image(str(repo /"golden_tests/fixtures/tiny_bold.nii.gz"))mask = ni.read_image(str(repo /"golden_tests/fixtures/tiny_mask.nii.gz"), type="vol")tsnr = bold.temporal_snr(mask=mask)out = Path(tempfile.mkdtemp()) /"tsnr.nii.gz"nib.save(tsnr.to_nibabel(), str(out)) # Receipt embeds as NIfTI ecode-6 'comment'out.stat().st_size >0, tsnr.provenance.method_name
(True, 'temporal_snr')
Note
to_nibabel() + nib.save(...) is the explicit write path that embeds the Receipt. ni.write_vol(tsnr, path) routes through to_nibabel() and embeds the same Receipt, so either path lets the file itself explain which operation, input image, and mask produced the map.
What’s guaranteed
The mask/BOLD frame is validated inside temporal_snr before the reduction runs.
to_nibabel() writes exactly one neuroim/receipt/v1: extension, replacing any prior one; foreign comment extensions are untouched.
What survives the file
The eight Receipt fields, byte-identical, in a NIfTI comment extension (ecode 6), recoverable by ten lines of nibabel alone:
import jsonimg = nib.load(str(out))ext =next(e for e in img.header.extensions ifint(e.get_code()) ==6)text =bytes(ext.get_content()).rstrip(b"\x00").decode("utf-8")receipt = json.loads(text[len("neuroim/receipt/v1:"):])sorted(receipt)