From f93a044e84d668c7d10a6ae8d421440cc378bf89 Mon Sep 17 00:00:00 2001 From: "Thomas G. Close" Date: Thu, 6 Aug 2026 11:34:53 +1000 Subject: [PATCH] drop reid_metadata tuple return from deidentify() Implementations now only strip identifying data and return the deidentified fileset; callers that need to know what changed (e.g. for a re-identification audit trail) diff metadata before/after instead. --- extras/fileformats/extras/biosig/edf.py | 8 +++--- extras/fileformats/extras/biosig/utils.py | 32 +++++------------------ fileformats/biosig/base.py | 15 ++++++++--- 3 files changed, 22 insertions(+), 33 deletions(-) diff --git a/extras/fileformats/extras/biosig/edf.py b/extras/fileformats/extras/biosig/edf.py index e322b21..708ece4 100644 --- a/extras/fileformats/extras/biosig/edf.py +++ b/extras/fileformats/extras/biosig/edf.py @@ -35,15 +35,15 @@ def edf_deidentify( edf: Edf, spec: ty.Any = None, out_dir: os.PathLike[str] | None = None, -) -> tuple[Edf, dict[str, ty.Any]]: + **kwargs: ty.Any, +) -> Edf: out_dir = Path(tempfile.mkdtemp() if out_dir is None else out_dir) out_dir.mkdir(parents=True, exist_ok=True) raw = mne.io.read_raw_edf(edf, preload=True, verbose=False) - deidentified_info, reid = mne_deidentify(raw, spec) - raw.info = deidentified_info + raw.info = mne_deidentify(raw, spec) deid_fspath = out_dir / "eeg.edf" mne.export.export_raw(deid_fspath, raw, fmt="edf", overwrite=True) - return type(edf)(deid_fspath), reid + return type(edf)(deid_fspath) def _parse_edf_header(path: os.PathLike[str]) -> dict[str, ty.Any]: diff --git a/extras/fileformats/extras/biosig/utils.py b/extras/fileformats/extras/biosig/utils.py index 6a08437..c4bdc5d 100644 --- a/extras/fileformats/extras/biosig/utils.py +++ b/extras/fileformats/extras/biosig/utils.py @@ -8,30 +8,12 @@ def mne_deidentify( raw: mne.io.BaseRaw, spec: ty.Any = None, -) -> tuple[mne.Info, dict[str, ty.Any]]: - """Anonymize an MNE Raw object and return the deidentified Info alongside - a dict of the original values that were stripped or changed.""" - orig_info_dict = raw.info.to_json_dict() - kwargs = json.load(open(spec)) if spec is not None else {} - deidentified_info = mne.io.anonymize_info(raw.info, verbose=None, **kwargs) - reid = dict_diff(orig_info_dict, deidentified_info.to_json_dict()) - return deidentified_info, reid - +) -> mne.Info: + """Anonymize an MNE Raw object and return the deidentified Info. -def dict_diff( - orig: ty.Mapping[str, ty.Any], new: ty.Mapping[str, ty.Any] -) -> dict[str, ty.Any]: - """Get a dict of all fields in orig that are not present or differ in new. - For nested dicts, the diff is applied recursively. + Callers that need to know what was changed (e.g. for a re-identification audit + trail) should diff `metadata` before and after instead of relying on this + function to report it, since that works uniformly across formats. """ - result = {} - for k, v in orig.items(): - if k not in new: - result[k] = v - elif isinstance(v, dict) and isinstance(new[k], dict): - nested = dict_diff(v, new[k]) - if nested: - result[k] = nested - elif v != new[k]: - result[k] = v - return result + kwargs = json.load(open(spec)) if spec is not None else {} + return mne.io.anonymize_info(raw.info, verbose=None, **kwargs) diff --git a/fileformats/biosig/base.py b/fileformats/biosig/base.py index 15e89c2..bb2daa0 100644 --- a/fileformats/biosig/base.py +++ b/fileformats/biosig/base.py @@ -11,12 +11,19 @@ def deidentify( self, spec: ty.Any = None, out_dir: os.PathLike[str] | None = None, - ) -> tuple[ty.Self, dict[str, ty.Any]]: + **kwargs: ty.Any, + ) -> ty.Self: """ Deidentifies the dataset by stripping any subject-identifying information from the image header. The exact implementation of this method will depend on the specific image format and the type of identifying information that is present. + Implementations only need to strip the identifying data -- they don't need to + track/report what was changed. Callers that need that (e.g. for re-identification + audit trails) can diff `metadata` before and after calling this method instead, + since that works uniformly across formats without extra bookkeeping in each + implementation. + Parameters ---------- spec: Any, optional @@ -24,15 +31,15 @@ def deidentify( which fields to remove or how to handle certain types of data. The exact structure of this specification will depend on the specific image format and the requirements of the deidentification process. + **kwargs: Any + Additional format-specific keyword arguments (e.g. concurrency options), + which implementations that don't use them should accept and ignore Returns ------- Self A new instance of the image with any subject-identifying information stripped from the image header. - dict[str, Any] - A JSON-like nested dictionary containing the original values from the header that - were stripped/modified during the deidentification process. """ raise NotImplementedError