Spaces:
Running
Running
| # Authors: The MNE-Python contributors. | |
| # License: BSD-3-Clause | |
| # Copyright the MNE-Python contributors. | |
| import os.path as op | |
| import numpy as np | |
| from ..._fiff._digitization import _artemis123_read_pos | |
| from ...transforms import rotation3d_align_z_axis | |
| from ...utils import logger | |
| def _load_mne_locs(fname=None): | |
| """Load MNE locs structure from file (if exists) or recreate it.""" | |
| if not fname: | |
| # find input file | |
| resource_dir = op.join(op.dirname(op.abspath(__file__)), "resources") | |
| fname = op.join(resource_dir, "Artemis123_mneLoc.csv") | |
| if not op.exists(fname): | |
| raise OSError(f'MNE locs file "{fname}" does not exist') | |
| logger.info(f"Loading mne loc file {fname}") | |
| locs = dict() | |
| with open(fname) as fid: | |
| for line in fid: | |
| vals = line.strip().split(",") | |
| locs[vals[0]] = np.array(vals[1::], np.float64) | |
| return locs | |
| def _generate_mne_locs_file(output_fname): | |
| """Generate mne coil locs and save to supplied file.""" | |
| logger.info("Converting Tristan coil file to mne loc file...") | |
| resource_dir = op.join(op.dirname(op.abspath(__file__)), "resources") | |
| chan_fname = op.join(resource_dir, "Artemis123_ChannelMap.csv") | |
| chans = _load_tristan_coil_locs(chan_fname) | |
| # compute a dict of loc structs | |
| locs = {n: _compute_mne_loc(cinfo) for n, cinfo in chans.items()} | |
| # write it out to output_fname | |
| with open(output_fname, "w") as fid: | |
| for n in sorted(locs.keys()): | |
| fid.write(f"{n},") | |
| fid.write(",".join(locs[n].astype(str))) | |
| fid.write("\n") | |
| def _load_tristan_coil_locs(coil_loc_path): | |
| """Load the Coil locations from Tristan CAD drawings.""" | |
| channel_info = dict() | |
| with open(coil_loc_path) as fid: | |
| # skip 2 Header lines | |
| fid.readline() | |
| fid.readline() | |
| for line in fid: | |
| line = line.strip() | |
| vals = line.split(",") | |
| channel_info[vals[0]] = dict() | |
| if vals[6]: | |
| channel_info[vals[0]]["inner_coil"] = np.array(vals[2:5], np.float64) | |
| channel_info[vals[0]]["outer_coil"] = np.array(vals[5:8], np.float64) | |
| else: # nothing supplied | |
| channel_info[vals[0]]["inner_coil"] = np.zeros(3) | |
| channel_info[vals[0]]["outer_coil"] = np.zeros(3) | |
| return channel_info | |
| def _compute_mne_loc(coil_loc): | |
| """Convert a set of coils to an mne Struct. | |
| Note input coil locations are in inches. | |
| """ | |
| loc = np.zeros(12) | |
| if (np.linalg.norm(coil_loc["inner_coil"]) == 0) and ( | |
| np.linalg.norm(coil_loc["outer_coil"]) == 0 | |
| ): | |
| return loc | |
| # channel location is inner coil location converted to meters From inches | |
| loc[0:3] = coil_loc["inner_coil"] / 39.370078 | |
| # figure out rotation | |
| z_axis = coil_loc["outer_coil"] - coil_loc["inner_coil"] | |
| R = rotation3d_align_z_axis(z_axis) | |
| loc[3:13] = R.T.reshape(9) | |
| return loc | |
| def _read_pos(fname): | |
| """Read the .pos file and return positions as dig points.""" | |
| nas, lpa, rpa, hpi, extra = None, None, None, None, None | |
| with open(fname) as fid: | |
| for line in fid: | |
| line = line.strip() | |
| if len(line) > 0: | |
| parts = line.split() | |
| # The lines can have 4 or 5 parts. First part is for the id, | |
| # which can be an int or a string. The last three are for xyz | |
| # coordinates. The extra part is for additional info | |
| # (e.g. 'Pz', 'Cz') which is ignored. | |
| if len(parts) not in [4, 5]: | |
| continue | |
| if parts[0].lower() == "nasion": | |
| nas = np.array([float(p) for p in parts[-3:]]) / 100.0 | |
| elif parts[0].lower() == "left": | |
| lpa = np.array([float(p) for p in parts[-3:]]) / 100.0 | |
| elif parts[0].lower() == "right": | |
| rpa = np.array([float(p) for p in parts[-3:]]) / 100.0 | |
| elif "hpi" in parts[0].lower(): | |
| if hpi is None: | |
| hpi = list() | |
| hpi.append(np.array([float(p) for p in parts[-3:]]) / 100.0) | |
| else: | |
| if extra is None: | |
| extra = list() | |
| extra.append(np.array([float(p) for p in parts[-3:]]) / 100.0) | |
| return _artemis123_read_pos(nas, lpa, rpa, hpi, extra) | |