Source code for msqms.qc.basic_info

# -*- coding: utf-8 -*-
"""Obtain Basic Info of MEG Data."""

import numpy as np
from box import Box
import os.path as op
from collections import Counter, defaultdict

import mne
from mne.io import read_raw_fif
from mne import channel_type
from mne.utils.misc import _pl
from mne.utils import sizeof_fmt
from mne import pick_types

try:
    from mne.io._digitization import _dig_kind_proper, _dig_kind_rev, _dig_kind_ints
except ImportError:
    # Compatibility for mne==1.6.0
    from mne._fiff._digitization import _dig_kind_proper, _dig_kind_rev, _dig_kind_ints

from msqms.utils import clogger
from msqms.utils import format_timedelta


[docs] def get_header_info(raw: mne.io.BaseRaw) -> Box: """ Extract basic information from an MNE Raw object. This function processes an MNE Raw object to obtain essential metadata about MEG recordings, including participant information, channel types, and recording details. Parameters ---------- raw : mne.io.BaseRaw The MNE Raw object containing MEG/EEG data. Returns ------- info_box : Box A dictionary-like object containing: - "basic_info": dict General recording metadata, including experimenter, participant, digitized points, and file details. - "meg_info": dict Summary of MEG-specific information such as the number of magnetometers, gradiometers, EEG channels, and digitized points. """ assert isinstance(raw, mne.io.BaseRaw) info = raw.info # Experimenter experimenter_info = info.get('experimenter', 'unspecified') # Measurement date meas_date = info.get('meas_date') meas_date_info = meas_date.strftime('%Y-%m-%d %H:%M:%S %Z') if meas_date else 'unspecified' # Participant information try: participant = defaultdict(str, info.get('subject_info', {})) sex_dict = {0: 'unknown', 1: 'male', 2: 'female'} birthday = '-'.join(map(str, participant.get('birthday', ('unspecified', 'unspecified', 'unspecified')))) participant_info = { "name": f"{participant['first_name']} {participant['middle_name']} {participant['last_name']}".strip(), "birthday": birthday, "sex": sex_dict.get(participant['sex'], 'unspecified') } except Exception as e: clogger.error(e) participant_info = {"name": "unspecified", "birthday": "unspecified", "sex": "unspecified"} # Digitized points dig = info.get('dig', []) if dig: counts = Counter(d['kind'] for d in dig) counts = [f"{counts[ii]} {_dig_kind_proper[_dig_kind_rev[ii]]}" for ii in _dig_kind_ints if ii in counts] dig_info = f"{len(dig)} item{_pl(len(dig))} ({', '.join(counts)})" n_dig = len(dig) else: dig_info = 'Not available' n_dig = 0 # Channel information n_eeg = len(pick_types(info, meg=False, eeg=True)) n_grad = len(pick_types(info, meg='grad')) n_mag = len(pick_types(info, meg='mag')) n_stim = len(pick_types(info, stim=True)) ch_types = [channel_type(info, idx) for idx in range(len(info['chs']))] ch_counts = Counter(ch_types) chs_info = ', '.join(f"{count} {ch_type.upper()}" for ch_type, count in ch_counts.items()) # EOG and ECG channels eog = ', '.join(np.array(info['ch_names'])[pick_types(info, meg=False, eog=True)]) or '0' ecg = ', '.join(np.array(info['ch_names'])[pick_types(info, meg=False, ecg=True)]) or '0' # Bad channels bad_info = ', '.join(info.get('bads', [])) or 'unspecified' # Sampling frequency, lowpass, highpass sfreq_info = f"{info['sfreq']:.1f} Hz" lowpass_info = f"{info['lowpass']:.1f} Hz" highpass_info = f"{info['highpass']:.1f} Hz" # Duration duration_info = format_timedelta(raw.times[-1]) + " (HH:MM:SS.SSS)" # Source filename and size source_filename = raw.filenames[0] if raw.filenames else 'unspecified' file_size = sizeof_fmt(op.getsize(source_filename)) if source_filename != 'unspecified' else 'unspecified' # Prepare output basic_info = { 'Experimenter': experimenter_info, 'Measurement date': meas_date_info, 'Participant': participant_info, 'Digitized points': dig_info, 'Good channels': chs_info, 'Bad channels': bad_info, 'EOG channels': eog, 'ECG channels': ecg, 'Sampling frequency': sfreq_info, 'Highpass': highpass_info, 'Lowpass': lowpass_info, "Duration": duration_info, "Source filename": source_filename, 'Data size': file_size } meg_info = { 'n_mag': n_mag, 'n_grad': n_grad, 'n_stim': n_stim, 'n_eeg': n_eeg, 'n_ecg': ecg, 'n_eog': eog, 'n_dig': n_dig } return Box({"basic_info": basic_info, "meg_info": meg_info})