Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions extras/fileformats/extras/biosig/edf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]:
Expand Down
32 changes: 7 additions & 25 deletions extras/fileformats/extras/biosig/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
15 changes: 11 additions & 4 deletions fileformats/biosig/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,35 @@ 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
A specification for the deidentification process, which may include details on
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

Expand Down