def test_rotation(): """Test conversion between rotation angles and transformation matrix.""" tests = [(0, 0, 1), (.5, .5, .5), (np.pi, 0, -1.5)] for rot in tests: x, y, z = rot m = rotation3d(x, y, z) m4 = rotation(x, y, z) assert_array_equal(m, m4[:3, :3]) back = rotation_angles(m) assert_equal(back, rot) back4 = rotation_angles(m4) assert_equal(back4, rot)
def initAvg_headpos(condition=[], folder=[]): """ Write the average head position based of the initial fit of several independent files and save to a trans fif file Parameters ---------- condition : str String containing part of common filename, e.g. "task" for files task_a.fif, task_b.fif, etc. Consistent naiming of files is mandatory! If no condition is provided, it will average all files in folder. folder : str Path to input files. Default = current dir. Returns ------- None """ if not folder: rawdir = getcwd() else: rawdir = folder # Change to subject dir if not condition: files2combine = listdir(rawdir) print('Using all files in %s' % rawdir) else: files2combine = glob.glob('%s*' % condition) if not files2combine: print('No files called \"%s\" found in %s' % (condition, rawdir)) return elif len(files2combine) < 2: warnings.warn( 'Only one file, please check!', RuntimeWarning) # [!!!] should it just copy the initial headpos? # Define output mean_trans_folder = path.join(rawdir, 'trans_files') if not path.exists(mean_trans_folder): mkdir(mean_trans_folder) mean_trans_file = path.join(mean_trans_folder, condition + '-trans.fif') if op.isfile(mean_trans_file): warnings.warn( 'N"%s\" already exists is %s. Delete if you want to rerun' % (mean_trans_file, mean_trans_folder), RuntimeWarning) return files2combine = [f for f in files2combine if '.fif' in f] # Make sure only fif files files2combine.sort() print('Files used for average head pos:') for ib in range(len(files2combine)): print('{:d}: {:s}'.format(ib + 1, files2combine[ib])) init_xfm = [] init_rot = [] for ff in files2combine: fname = path.join(rawdir, ff) # first file is enough NOT ANYMORE! with warnings.catch_warnings( ): # suppress some annoying warnings for now warnings.simplefilter("ignore") info = Raw(fname, preload=False, verbose=False, allow_maxshield=True).info print( 'Ignore the warning above. We\'ll run MaxFilter in a few moments...' ) init_xfm += [info['dev_head_t']['trans']] # translations: info['dev_head_t']['trans'][:, 3][:-1] init_rot += [info['dev_head_t']['trans'][:3, :3]] mean_init_xfm = np.mean(np.stack(init_xfm), axis=0) # stack, then average over new dim init_rot_angles = [rotation_angles(m) for m in init_rot] mean_init_rot_xfm = rotation3d(*tuple( np.mean(np.stack(init_rot_angles), axis=0))) # stack, then average, then make new xfm assert (np.sum(mean_init_xfm[-1]) == 1.0) # sanity check result mean_trans = info['dev_head_t'] # use the last info as a template mean_trans['trans'] = mean_init_xfm # replace the transformation mean_trans[ 'trans'][:3, :3] = mean_init_rot_xfm # replace the rotation part mean_init_headpos = mean_trans['trans'][:-1, -1] # meters print('Mean head position (device coords): ({:.1f}, {:.1f}, {:.1f}) mm'.\ format(*tuple(mean_init_headpos*1e3))) print('Discrepancies from mean:') for ib, xfm in enumerate(init_xfm): diff = 1e3 * (xfm[:-1, -1] - mean_init_headpos) rmsdiff = np.linalg.norm(diff) print('\tSession {:d}: norm {:.1f} mm ({:.1f}, {:.1f}, {:.1f}) mm '.\ format(ib + 1, rmsdiff, *tuple(diff))) mean_rots = rotation_angles( mean_trans['trans'][:3, :3]) # these are in radians mean_rots_deg = tuple([180. * rot / np.pi for rot in mean_rots]) # convert to deg print('Mean head rotations (around x, y & z axes): ({:.1f}, {:.1f}, {:.1f}) deg'.\ format(*mean_rots_deg)) print('Block discrepancies from mean:') for ib, rot in enumerate(init_rot): cur_rots = rotation_angles(rot) diff = tuple([ 180. * cr / np.pi - mr for cr, mr in zip(cur_rots, mean_rots_deg) ]) print('\tSession {:d}: ({:.1f}, {:.1f}, {:.1f}) deg '.\ format(ib + 1, *tuple(diff))) # Write trans file write_trans(mean_trans_file, mean_trans) print("Wrote " + mean_trans_file)
def contAvg_headpos(condition, method='median', folder=[], summary=False): """ Calculate average transformation from dewar to head coordinates, based on the continous head position estimated from MaxFilter Parameters ---------- condition : str String containing part of common filename, e.g. "task" for files task-1.fif, task-2.fif, etc. Consistent naiming of files is mandatory! method : str How to calculate "average, "mean" or "median" (default = "median") folder : str Path to input files. Default = current dir. Returns ------- MNE-Python transform object 4x4 transformation matrix """ # Check that the method works method = method.lower() if method not in ['median', 'mean']: raise RuntimeError( 'Wrong method. Must be either \"mean\" or "median"!') if not condition: raise RuntimeError('You must provide a conditon!') # Get and set folders if not folder: rawdir = getcwd() # [!] Match up with bash script ! else: rawdir = folder print(rawdir) quatdir = op.join(rawdir, 'quat_files') mean_trans_folder = op.join(rawdir, 'trans_files') if not op.exists(mean_trans_folder): # Make sure output folder exists mkdir(mean_trans_folder) mean_trans_file = op.join(mean_trans_folder, condition + '-trans.fif') if op.isfile(mean_trans_file): warnings.warn( 'N"%s\" already exists is %s. Delete if you want to rerun' % (mean_trans_file, mean_trans_folder), RuntimeWarning) return # Change to subject dir files2combine = find_condition_files(quatdir, condition) files2combine.sort() if not files2combine: raise RuntimeError('No files called \"%s\" found in %s' % (condition, quatdir)) allfiles = [] for ff in files2combine: fl = ff.split('_')[0] tmplist = [f for f in listdir(quatdir) if fl in f and '_quat' in f] #Fix order if len(tmplist) > 1: tmplist.sort() if any("-" in f for f in tmplist): firstfile = tmplist[ -1] # The file without a number will always be last! tmpfs = sorted(tmplist[:-1], key=lambda a: int(re.split('-|.fif', a)[-2]) ) # Assuming consistent naming!!! tmplist[0] = firstfile tmplist[1:] = tmpfs allfiles = allfiles + tmplist if len(allfiles) > 1: print('Files used for average head pos:') for ib in range(len(allfiles)): print('{:d}: {:s}'.format(ib + 1, allfiles[ib])) else: print('Will find average head pos in %s' % files2combine) # LOAD DATA # raw = read_raw_fif(op.join(quatdir,firstfile), preload=True, allow_maxshield=True, verbose=False).pick_types(meg=False, chpi=True) # Use files2combine instead of allfiles as MNE will find split files automatically. for idx, ffs in enumerate(files2combine): if idx == 0: raw = read_raw_fif(op.join(quatdir, ffs), preload=True, allow_maxshield=True).pick_types(meg=False, chpi=True) else: raw.append( read_raw_fif(op.join(quatdir, ffs), preload=True, allow_maxshield=True).pick_types(meg=False, chpi=True)) quat, times = raw.get_data(return_times=True) gof = quat[6, ] # Godness of fit channel # fs = raw.info['sfreq'] # In case "record raw" started before "cHPI" if np.any(gof < 0.98): begsam = np.argmax(gof > 0.98) raw.crop(tmin=raw.times[begsam]) quat = quat[:, begsam:].copy() times = times[begsam:].copy() # Make summaries if summary: plot_movement(quat, times, dirname=rawdir, identifier=condition) total_dist_moved(quat, times, write=True, dirname=rawdir, identifier=condition) # Get continous transformation print('Reading transformation. This will take a while...') H = np.empty([4, 4, len(times)]) # Initiate transforms init_rot_angles = np.empty([len(times), 3]) for i, t in enumerate(times): Hi = np.eye(4, 4) Hi[0:3, 3] = quat[3:6, i].copy() Hi[:3, :3] = quat_to_rot(quat[0:3, i]) init_rot_angles[i, :] = rotation_angles(Hi[:3, :3]) assert (np.sum(Hi[-1]) == 1.0) # sanity check result H[:, :, i] = Hi.copy() if method in ["mean"]: H_mean = np.mean(H, axis=2) # stack, then average over new dim mean_rot_xfm = rotation3d(*tuple( np.mean(init_rot_angles, axis=0))) # stack, then average, then make new xfm elif method in ["median"]: H_mean = np.median(H, axis=2) # stack, then average over new dim mean_rot_xfm = rotation3d(*tuple( np.median(init_rot_angles, axis=0))) # stack, then average, then make new xfm H_mean[:3, :3] = mean_rot_xfm assert (np.sum(H_mean[-1]) == 1.0) # sanity check result # Create the mean structure and save as .fif mean_trans = raw.info['dev_head_t'] # use the last info as a template mean_trans['trans'] = H_mean.copy() # Write file write_trans(mean_trans_file, mean_trans) print("Wrote " + mean_trans_file) return mean_trans
def contAvg_headpos(condition, method='median', folder=[]): """ Calculate average transformation from dewar to head coordinates, based on the continous head position estimated from MaxFilter Parameters ---------- condition : str String containing part of common filename, e.g. "task" for files task-1.fif, task-2.fif, etc. Consistent naiming of files is mandatory! method : str How to calculate "average, "mean" or "median" (default = "median") folder : str Path to input files. Default = current dir. Returns ------- MNE-Python transform object 4x4 transformation matrix """ # Check that the method works if method not in ['median', 'mean']: raise RuntimeError( 'Wrong method. Must be either \"mean\" or "median"!') if not condition: raise RuntimeError('You must provide a conditon!') # Get and set folders if not folder: rawdir = getcwd() # [!] Match up with bash script ! else: rawdir = folder print(rawdir) quatdir = op.join(rawdir, 'quat_files') mean_trans_folder = op.join(rawdir, 'trans_files') if not op.exists(mean_trans_folder): # Make sure output folder exists mkdir(mean_trans_folder) mean_trans_file = op.join(mean_trans_folder, condition + '-trans.fif') if op.isfile(mean_trans_file): raise RuntimeError( 'N"%s\" already exists is %s. Delete aif you want to rerun' % (mean_trans_file, mean_trans_folder)) # Change to subject dir # files2combine = glob.glob('%s*' % condition) files2combine = [ f for f in listdir(quatdir) if condition in f and '_quat' in f ] if not files2combine: raise RuntimeError('No files called \"%s\" found in %s' % (condition, quatdir)) elif len(files2combine) > 1: print('Files used for average head pos:') for ib in range(len(files2combine)): print('{:d}: {:s}'.format(ib + 1, files2combine[ib])) else: print('Will find average head pos in %s' % files2combine) # LOAD DATA for idx, ffs in enumerate(files2combine): # print op.join(quatdir,ffs) if idx == 0: raw = read_raw_fif(op.join(quatdir, ffs), preload=True, allow_maxshield=True).pick_types(meg=False, chpi=True) else: raw.append( read_raw_fif(op.join(quatdir, ffs), preload=True, allow_maxshield=True).pick_types(meg=False, chpi=True)) quat, times = raw.get_data(return_times=True) gof = quat[6, ] # Godness of fit channel fs = raw.info['sfreq'] # In case "record raw" started before "cHPI" if np.any(gof < 0.98): begsam = np.argmax(gof > 0.98) raw.crop(tmin=raw.times[begsam]) quat = quat[:, begsam:].copy() times = times[begsam:].copy() # Get continous transformation print('Reading transformation. This will take a while...') H = np.empty([4, 4, len(times)]) # Initiate transforms init_rot_angles = np.empty([len(times), 3]) for i, t in enumerate(times): Hi = np.eye(4, 4) Hi[0:3, 3] = quat[3:6, i].copy() Hi[:3, :3] = quat_to_rot(quat[0:3, i]) init_rot_angles[i, :] = rotation_angles(Hi[:3, :3]) assert (np.sum(Hi[-1]) == 1.0) # sanity check result H[:, :, i] = Hi.copy() if method in ["mean"]: H_mean = np.mean(H, axis=2) # stack, then average over new dim mean_rot_xfm = rotation3d(*tuple( np.mean(init_rot_angles, axis=0))) # stack, then average, then make new xfm elif method in ["median"]: H_mean = np.median(H, axis=2) # stack, then average over new dim mean_rot_xfm = rotation3d(*tuple( np.median(init_rot_angles, axis=0))) # stack, then average, then make new xfm H_mean[:3, :3] = mean_rot_xfm assert (np.sum(H_mean[-1]) == 1.0) # sanity check result # Create the mean structure and save as .fif mean_trans = raw.info['dev_head_t'] # use the last info as a template mean_trans['trans'] = H_mean.copy() # plot_alignment(raw.info,subject='0406',subjects_dir='/home/mikkel/PD_motor/fs_subjects_dir/',dig=True, meg='helmet') # Write file write_trans(mean_trans_file, mean_trans) print("Wrote " + mean_trans_file) return mean_trans