def _compare_positions(a, b, max_dist=0.003, max_angle=5.): """Compare estimated cHPI positions""" from scipy.interpolate import interp1d trans, rot, t = a trans_est, rot_est, t_est = b quats_est = rot_to_quat(rot_est) # maxfilter produces some times that are implausibly large (weird) use_mask = (t >= t_est[0]) & (t <= t_est[-1]) t = t[use_mask] trans = trans[use_mask] quats = rot_to_quat(rot) quats = quats[use_mask] # double-check our angle function for q in (quats, quats_est): angles = _angle_between_quats(q, q) assert_allclose(angles, 0., atol=1e-5) # < 3 mm translation difference between MF and our estimation trans_est_interp = interp1d(t_est, trans_est, axis=0)(t) worst = np.sqrt(np.sum((trans - trans_est_interp)**2, axis=1)).max() assert_true(worst <= max_dist, '%0.1f > %0.1f mm' % (1000 * worst, 1000 * max_dist)) # < 5 degrees rotation difference between MF and our estimation # (note that the interpolation will make this slightly worse) quats_est_interp = interp1d(t_est, quats_est, axis=0)(t) worst = 180 * _angle_between_quats(quats_est_interp, quats).max() / np.pi assert_true(worst <= max_angle, '%0.1f > %0.1f deg' % ( worst, max_angle, ))
def _compare_positions(a, b, max_dist=0.003, max_angle=5.): """Compare estimated cHPI positions""" from scipy.interpolate import interp1d trans, rot, t = a trans_est, rot_est, t_est = b quats_est = rot_to_quat(rot_est) # maxfilter produces some times that are implausibly large (weird) use_mask = (t >= t_est[0]) & (t <= t_est[-1]) t = t[use_mask] trans = trans[use_mask] quats = rot_to_quat(rot) quats = quats[use_mask] # double-check our angle function for q in (quats, quats_est): angles = _angle_between_quats(q, q) assert_allclose(angles, 0., atol=1e-5) # < 3 mm translation difference between MF and our estimation trans_est_interp = interp1d(t_est, trans_est, axis=0)(t) worst = np.sqrt(np.sum((trans - trans_est_interp) ** 2, axis=1)).max() assert_true(worst <= max_dist, '%0.1f > %0.1f mm' % (1000 * worst, 1000 * max_dist)) # < 5 degrees rotation difference between MF and our estimation # (note that the interpolation will make this slightly worse) quats_est_interp = interp1d(t_est, quats_est, axis=0)(t) worst = 180 * _angle_between_quats(quats_est_interp, quats).max() / np.pi assert_true(worst <= max_angle, '%0.1f > %0.1f deg' % (worst, max_angle,))
def _assert_quats(actual, desired, dist_tol=0.003, angle_tol=5., err_rtol=0.5, gof_rtol=0.001, vel_atol=2e-3): # 2 mm/s """Compare estimated cHPI positions.""" __tracebackhide__ = True trans_est, rot_est, t_est = head_pos_to_trans_rot_t(actual) trans, rot, t = head_pos_to_trans_rot_t(desired) quats_est = rot_to_quat(rot_est) gofs, errs, vels = desired[:, 7:].T gofs_est, errs_est, vels_est = actual[:, 7:].T del actual, desired # maxfilter produces some times that are implausibly large (weird) if not np.isclose(t[0], t_est[0], atol=1e-1): # within 100 ms raise AssertionError('Start times not within 100 ms: %0.3f != %0.3f' % (t[0], t_est[0])) use_mask = (t >= t_est[0]) & (t <= t_est[-1]) t = t[use_mask] trans = trans[use_mask] quats = rot_to_quat(rot) quats = quats[use_mask] gofs, errs, vels = gofs[use_mask], errs[use_mask], vels[use_mask] # double-check our angle function for q in (quats, quats_est): angles = _angle_between_quats(q, q) assert_allclose(angles, 0., atol=1e-5) # limit translation difference between MF and our estimation trans_est_interp = interp1d(t_est, trans_est, axis=0)(t) distances = np.sqrt(np.sum((trans - trans_est_interp) ** 2, axis=1)) assert np.isfinite(distances).all() arg_worst = np.argmax(distances) assert distances[arg_worst] <= dist_tol, ( '@ %0.3f seconds: %0.3f > %0.3f mm' % (t[arg_worst], 1000 * distances[arg_worst], 1000 * dist_tol)) # limit rotation difference between MF and our estimation # (note that the interpolation will make this slightly worse) quats_est_interp = interp1d(t_est, quats_est, axis=0)(t) angles = 180 * _angle_between_quats(quats_est_interp, quats) / np.pi arg_worst = np.argmax(angles) assert angles[arg_worst] <= angle_tol, ( '@ %0.3f seconds: %0.3f > %0.3f deg' % (t[arg_worst], angles[arg_worst], angle_tol)) # error calculation difference errs_est_interp = interp1d(t_est, errs_est)(t) assert_allclose(errs_est_interp, errs, rtol=err_rtol, atol=1e-3, err_msg='err') # 1 mm # gof calculation difference gof_est_interp = interp1d(t_est, gofs_est)(t) assert_allclose(gof_est_interp, gofs, rtol=gof_rtol, atol=1e-7, err_msg='gof') # velocity calculation difference vel_est_interp = interp1d(t_est, vels_est)(t) assert_allclose(vel_est_interp, vels, atol=vel_atol, err_msg='velocity')
def test_quaternions(): """Test quaternion calculations.""" rots = [np.eye(3)] for fname in [test_fif_fname, ctf_fname, hp_fif_fname]: rots += [read_info(fname)['dev_head_t']['trans'][:3, :3]] # nasty numerical cases rots += [ np.array([ [-0.99978541, -0.01873462, -0.00898756], [-0.01873462, 0.62565561, 0.77987608], [-0.00898756, 0.77987608, -0.62587152], ]) ] rots += [ np.array([ [0.62565561, -0.01873462, 0.77987608], [-0.01873462, -0.99978541, -0.00898756], [0.77987608, -0.00898756, -0.62587152], ]) ] rots += [ np.array([ [-0.99978541, -0.00898756, -0.01873462], [-0.00898756, -0.62587152, 0.77987608], [-0.01873462, 0.77987608, 0.62565561], ]) ] for rot in rots: assert_allclose(rot, quat_to_rot(rot_to_quat(rot)), rtol=1e-5, atol=1e-5) rot = rot[np.newaxis, np.newaxis, :, :] assert_allclose(rot, quat_to_rot(rot_to_quat(rot)), rtol=1e-5, atol=1e-5) # let's make sure our angle function works in some reasonable way for ii in range(3): for jj in range(3): a = np.zeros(3) b = np.zeros(3) a[ii] = 1. b[jj] = 1. expected = np.pi if ii != jj else 0. assert_allclose(_angle_between_quats(a, b), expected, atol=1e-5) y_180 = np.array([[-1, 0, 0], [0, 1, 0], [0, 0, -1.]]) assert_allclose(_angle_between_quats(rot_to_quat(y_180), np.zeros(3)), np.pi) h_180_attitude_90 = np.array([[0, 1, 0], [1, 0, 0], [0, 0, -1.]]) assert_allclose( _angle_between_quats(rot_to_quat(h_180_attitude_90), np.zeros(3)), np.pi)
def _check_fit_matched_points( p, x, weights, do_scale, angtol=1e-5, dtol=1e-5, stol=1e-7): __tracebackhide__ = True mne.coreg._ALLOW_ANALITICAL = False try: params = mne.coreg.fit_matched_points( p, x, weights=weights, scale=do_scale, out='params') finally: mne.coreg._ALLOW_ANALITICAL = True quat_an, scale_an = _fit_matched_points(p, x, weights, scale=do_scale) assert len(params) == 6 + int(do_scale) q_co = _euler_to_quat(params[:3]) translate_co = params[3:6] angle = np.rad2deg(_angle_between_quats(quat_an[:3], q_co)) dist = np.linalg.norm(quat_an[3:] - translate_co) assert 0 <= angle < angtol, 'angle' assert 0 <= dist < dtol, 'dist' if do_scale: scale_co = params[6] assert_allclose(scale_an, scale_co, rtol=stol, err_msg='scale') # errs trans = _quat_to_affine(quat_an) trans[:3, :3] *= scale_an weights = np.ones(1) if weights is None else weights err_an = np.linalg.norm( weights[:, np.newaxis] * apply_trans(trans, p) - x) trans = mne.coreg._trans_from_params((True, True, do_scale), params) err_co = np.linalg.norm( weights[:, np.newaxis] * apply_trans(trans, p) - x) if err_an > 1e-14: assert err_an < err_co * 1.5 return quat_an, scale_an
def test_average_quats(): """Test averaging of quaternions.""" sq2 = 1. / np.sqrt(2.) quats = np.array( [[0, sq2, sq2], [0, sq2, sq2], [0, sq2, 0], [0, 0, sq2], [sq2, 0, 0]], float) # In MATLAB: # quats = [[0, sq2, sq2, 0]; [0, sq2, sq2, 0]; # [0, sq2, 0, sq2]; [0, 0, sq2, sq2]; [sq2, 0, 0, sq2]]; expected = [ quats[0], quats[0], [0, 0.788675134594813, 0.577350269189626], [0, 0.657192299694123, 0.657192299694123], [0.100406058540540, 0.616329446922803, 0.616329446922803] ] # Averaging the first two should give the same thing: for lim, ex in enumerate(expected): assert_allclose(_average_quats(quats[:lim + 1]), ex, atol=1e-7) quats[1] *= -1 # same quaternion (hidden value is zero here)! rot_0, rot_1 = quat_to_rot(quats[:2]) assert_allclose(rot_0, rot_1, atol=1e-7) for lim, ex in enumerate(expected): assert_allclose(_average_quats(quats[:lim + 1]), ex, atol=1e-7) # Assert some symmetry count = 0 extras = [[sq2, sq2, 0]] + list(np.eye(3)) for quat in np.concatenate((quats, expected, extras)): if np.isclose(_quat_real(quat), 0., atol=1e-7): # can flip sign count += 1 angle = _angle_between_quats(quat, -quat) assert_allclose(angle, 0., atol=1e-7) rot_0, rot_1 = quat_to_rot(np.array((quat, -quat))) assert_allclose(rot_0, rot_1, atol=1e-7) assert count == 4 + len(extras)
def test_vector_rotation(): """Test basic rotation matrix math.""" x = np.array([1., 0., 0.]) y = np.array([0., 1., 0.]) rot = _find_vector_rotation(x, y) assert_array_equal(rot, [[0, -1, 0], [1, 0, 0], [0, 0, 1]]) quat_1 = rot_to_quat(rot) quat_2 = rot_to_quat(np.eye(3)) assert_allclose(_angle_between_quats(quat_1, quat_2), np.pi / 2.)
def test_fit_matched_points(quats, scaling, do_scale): """Test analytical least-squares matched point fitting.""" if scaling != 1 and not do_scale: return # no need to test this, it will not be good rng = np.random.RandomState(0) fro = rng.randn(10, 3) translation = rng.randn(3) for qi, quat in enumerate(quats): to = scaling * np.dot(quat_to_rot(quat), fro.T).T + translation for corrupted in (False, True): # mess up a point if corrupted: to[0, 2] += 100 weights = np.ones(len(to)) weights[0] = 0 else: weights = None est, scale_est = _check_fit_matched_points( fro, to, weights=weights, do_scale=do_scale) assert_allclose(scale_est, scaling, rtol=1e-5) assert_allclose(est[:3], quat, atol=1e-14) assert_allclose(est[3:], translation, atol=1e-14) # if we don't adjust for the corruption above, it should get worse angle = dist = None for weighted in (False, True): if not weighted: weights = None dist_bounds = (5, 20) if scaling == 1: angle_bounds = (5, 95) angtol, dtol, stol = 1, 15, 3 else: angle_bounds = (5, 105) angtol, dtol, stol = 20, 15, 3 else: weights = np.ones(len(to)) weights[0] = 10 # weighted=True here means "make it worse" angle_bounds = (angle, 180) # unweighted values as new min dist_bounds = (dist, 100) if scaling == 1: # XXX this angtol is not great but there is a hard to # identify linalg/angle calculation bug on Travis... angtol, dtol, stol = 180, 70, 3 else: angtol, dtol, stol = 50, 70, 3 est, scale_est = _check_fit_matched_points( fro, to, weights=weights, do_scale=do_scale, angtol=angtol, dtol=dtol, stol=stol) assert not np.allclose(est[:3], quat, atol=1e-5) assert not np.allclose(est[3:], translation, atol=1e-5) angle = np.rad2deg(_angle_between_quats(est[:3], quat)) assert_array_less(angle_bounds[0], angle) assert_array_less(angle, angle_bounds[1]) dist = np.linalg.norm(est[3:] - translation) assert_array_less(dist_bounds[0], dist) assert_array_less(dist, dist_bounds[1])
def test_talxfm_rigid(): """Test that talxfm_rigid gives reasonable results.""" rigid = _estimate_talxfm_rigid('fsaverage', subjects_dir=subjects_dir) assert_allclose(rigid, np.eye(4), atol=1e-6) rigid = _estimate_talxfm_rigid('sample', subjects_dir=subjects_dir) assert_allclose(np.linalg.norm(rigid[:3, :3], axis=1), 1., atol=1e-6) move = 1000 * np.linalg.norm(rigid[:3, 3]) assert 30 < move < 70 ang = np.rad2deg(_angle_between_quats(rot_to_quat(rigid[:3, :3]))) assert 20 < ang < 25
def _assert_trans(actual, desired, dist_tol=0.003, angle_tol=5.): trans_est = actual[0:3, 3] quat_est = rot_to_quat(actual[0:3, 0:3]) trans = desired[0:3, 3] quat = rot_to_quat(desired[0:3, 0:3]) angle = 180 * _angle_between_quats(quat_est, quat) / np.pi dist = np.sqrt(np.sum((trans - trans_est) ** 2)) assert dist <= dist_tol, '%0.3f > %0.3f mm' % (1000 * dist, 1000 * dist_tol) assert angle <= angle_tol, '%0.3f > %0.3f deg' % (angle, angle_tol)
def _assert_trans(actual, desired, dist_tol=0.003, angle_tol=5.): trans_est = actual[0:3, 3] quat_est = rot_to_quat(actual[0:3, 0:3]) trans = desired[0:3, 3] quat = rot_to_quat(desired[0:3, 0:3]) angle = 180 * _angle_between_quats(quat_est, quat) / np.pi dist = np.sqrt(np.sum((trans - trans_est)**2)) assert_true(dist <= dist_tol, '%0.3f > %0.3f mm' % (1000 * dist, 1000 * dist_tol)) assert_true(angle <= angle_tol, '%0.3f > %0.3f deg' % (angle, angle_tol))
def _assert_trans(actual, desired, dist_tol=0.017, angle_tol=5.): __tracebackhide__ = True trans_est = actual[0:3, 3] quat_est = rot_to_quat(actual[0:3, 0:3]) trans = desired[0:3, 3] quat = rot_to_quat(desired[0:3, 0:3]) angle = np.rad2deg(_angle_between_quats(quat_est, quat)) dist = np.linalg.norm(trans - trans_est) assert dist <= dist_tol, \ '%0.3f > %0.3f mm translation' % (1000 * dist, 1000 * dist_tol) assert angle <= angle_tol, \ '%0.3f > %0.3f° rotation' % (angle, angle_tol)
def _assert_quats(actual, desired, dist_tol=0.003, angle_tol=5.): """Compare estimated cHPI positions.""" __tracebackhide__ = True trans_est, rot_est, t_est = head_pos_to_trans_rot_t(actual) trans, rot, t = head_pos_to_trans_rot_t(desired) quats_est = rot_to_quat(rot_est) # maxfilter produces some times that are implausibly large (weird) if not np.isclose(t[0], t_est[0], atol=1e-1): # within 100 ms raise AssertionError('Start times not within 100 ms: %0.3f != %0.3f' % (t[0], t_est[0])) use_mask = (t >= t_est[0]) & (t <= t_est[-1]) t = t[use_mask] trans = trans[use_mask] quats = rot_to_quat(rot) quats = quats[use_mask] # double-check our angle function for q in (quats, quats_est): angles = _angle_between_quats(q, q) assert_allclose(angles, 0., atol=1e-5) # limit translation difference between MF and our estimation trans_est_interp = interp1d(t_est, trans_est, axis=0)(t) distances = np.sqrt(np.sum((trans - trans_est_interp) ** 2, axis=1)) assert np.isfinite(distances).all() arg_worst = np.argmax(distances) assert distances[arg_worst] <= dist_tol, ( '@ %0.3f seconds: %0.3f > %0.3f mm' % (t[arg_worst], 1000 * distances[arg_worst], 1000 * dist_tol)) # limit rotation difference between MF and our estimation # (note that the interpolation will make this slightly worse) quats_est_interp = interp1d(t_est, quats_est, axis=0)(t) angles = 180 * _angle_between_quats(quats_est_interp, quats) / np.pi arg_worst = np.argmax(angles) assert angles[arg_worst] <= angle_tol, ( '@ %0.3f seconds: %0.3f > %0.3f deg' % (t[arg_worst], angles[arg_worst], angle_tol))
def _assert_quats(actual, desired, dist_tol=0.003, angle_tol=5.): """Compare estimated cHPI positions.""" from scipy.interpolate import interp1d trans_est, rot_est, t_est = head_pos_to_trans_rot_t(actual) trans, rot, t = head_pos_to_trans_rot_t(desired) quats_est = rot_to_quat(rot_est) # maxfilter produces some times that are implausibly large (weird) if not np.isclose(t[0], t_est[0], atol=1e-1): # within 100 ms raise AssertionError('Start times not within 100 ms: %0.3f != %0.3f' % (t[0], t_est[0])) use_mask = (t >= t_est[0]) & (t <= t_est[-1]) t = t[use_mask] trans = trans[use_mask] quats = rot_to_quat(rot) quats = quats[use_mask] # double-check our angle function for q in (quats, quats_est): angles = _angle_between_quats(q, q) assert_allclose(angles, 0., atol=1e-5) # limit translation difference between MF and our estimation trans_est_interp = interp1d(t_est, trans_est, axis=0)(t) distances = np.sqrt(np.sum((trans - trans_est_interp) ** 2, axis=1)) arg_worst = np.argmax(distances) assert_true(distances[arg_worst] <= dist_tol, '@ %0.3f seconds: %0.3f > %0.3f mm' % (t[arg_worst], 1000 * distances[arg_worst], 1000 * dist_tol)) # limit rotation difference between MF and our estimation # (note that the interpolation will make this slightly worse) quats_est_interp = interp1d(t_est, quats_est, axis=0)(t) angles = 180 * _angle_between_quats(quats_est_interp, quats) / np.pi arg_worst = np.argmax(angles) assert_true(angles[arg_worst] <= angle_tol, '@ %0.3f seconds: %0.3f > %0.3f deg' % (t[arg_worst], angles[arg_worst], angle_tol))
def test_set_montage_artinis_fsaverage(kind): """Test that artinis montages match fsaverage's head<->MRI transform.""" # Compare OctaMon and Brite23 to fsaverage trans_fs, _ = _get_trans('fsaverage') montage = make_standard_montage(f'artinis-{kind}') trans = compute_native_head_t(montage) assert trans['to'] == trans_fs['to'] assert trans['from'] == trans_fs['from'] translation = 1000 * np.linalg.norm(trans['trans'][:3, 3] - trans_fs['trans'][:3, 3]) assert 0 < translation < 1 # mm rotation = np.rad2deg( _angle_between_quats(rot_to_quat(trans['trans'][:3, :3]), rot_to_quat(trans_fs['trans'][:3, :3]))) assert 0 < rotation < 1 # degrees
def test_initial_fit_redo(): """Test that initial fits can be redone based on moments.""" raw = read_raw_fif(chpi_fif_fname, allow_maxshield='yes') slopes = np.array( [[c['slopes'] for c in raw.info['hpi_meas'][0]['hpi_coils']]]) amps = np.linalg.norm(slopes, axis=-1) amps /= slopes.shape[-1] assert_array_less(amps, 5e-11) assert_array_less(1e-12, amps) proj, _, _ = _setup_ext_proj(raw.info, ext_order=1) chpi_amplitudes = dict(times=np.zeros(1), slopes=slopes, proj=proj) chpi_locs = compute_chpi_locs(raw.info, chpi_amplitudes) # check GOF coil_gof = raw.info['hpi_results'][0]['goodness'] assert_allclose(chpi_locs['gofs'][0], coil_gof, atol=0.3) # XXX not good # check moment # XXX our forward and theirs differ by an extra mult by _MAG_FACTOR coil_moment = raw.info['hpi_results'][0]['moments'] / _MAG_FACTOR py_moment = chpi_locs['moments'][0] coil_amp = np.linalg.norm(coil_moment, axis=-1, keepdims=True) py_amp = np.linalg.norm(py_moment, axis=-1, keepdims=True) assert_allclose(coil_amp, py_amp, rtol=0.2) coil_ori = coil_moment / coil_amp py_ori = py_moment / py_amp angles = np.rad2deg(np.arccos(np.abs(np.sum(coil_ori * py_ori, axis=1)))) assert_array_less(angles, 20) # check resulting dev_head_t head_pos = compute_head_pos(raw.info, chpi_locs) assert head_pos.shape == (1, 10) nm_pos = raw.info['dev_head_t']['trans'] dist = 1000 * np.linalg.norm(nm_pos[:3, 3] - head_pos[0, 4:7]) assert 0.1 < dist < 2 angle = np.rad2deg( _angle_between_quats(rot_to_quat(nm_pos[:3, :3]), head_pos[0, 1:4])) assert 0.1 < angle < 2 gof = head_pos[0, 7] assert_allclose(gof, 0.9999, atol=1e-4)
def test_quaternions(): """Test quaternion calculations """ rots = [np.eye(3)] for fname in [test_fif_fname, ctf_fname, hp_fif_fname]: rots += [read_info(fname)['dev_head_t']['trans'][:3, :3]] # nasty numerical cases rots += [np.array([ [-0.99978541, -0.01873462, -0.00898756], [-0.01873462, 0.62565561, 0.77987608], [-0.00898756, 0.77987608, -0.62587152], ])] rots += [np.array([ [0.62565561, -0.01873462, 0.77987608], [-0.01873462, -0.99978541, -0.00898756], [0.77987608, -0.00898756, -0.62587152], ])] rots += [np.array([ [-0.99978541, -0.00898756, -0.01873462], [-0.00898756, -0.62587152, 0.77987608], [-0.01873462, 0.77987608, 0.62565561], ])] for rot in rots: assert_allclose(rot, quat_to_rot(rot_to_quat(rot)), rtol=1e-5, atol=1e-5) rot = rot[np.newaxis, np.newaxis, :, :] assert_allclose(rot, quat_to_rot(rot_to_quat(rot)), rtol=1e-5, atol=1e-5) # let's make sure our angle function works in some reasonable way for ii in range(3): for jj in range(3): a = np.zeros(3) b = np.zeros(3) a[ii] = 1. b[jj] = 1. expected = np.pi if ii != jj else 0. assert_allclose(_angle_between_quats(a, b), expected, atol=1e-5)
R = quat_to_rot(Rq) # translation vectors T, _ = raw[picks_chpi_t, :] T = T[:, ind_ok].T # head origin (see above) nrot = R.shape[0] Y = np.zeros((nrot, 3)) A = np.zeros((nrot, 3)) for k in np.arange(nrot): Y[k, :] = -np.dot(R[k].T, T[k]) # get rot. angle changes directly from quaternion data #Rq = lowpass(Rq, raw.info['sfreq'], args.lpcorner, axis=0) dA = _angle_between_quats(Rq[1:, :], Rq[:-1, :]) / np.pi * 180 # get pos. changes #Yf = lowpass(Y, raw.info['sfreq'], args.lpcorner, axis=0) dY = np.diff(Y, axis=0) dYv = np.sqrt(np.sum(dY**2, axis=1)) # len of movement at each time point tlen = times_ok[-1] - times_ok[0] trans_ok_ind = np.where(dYv < MAX_TRANS)[0] if len(trans_ok_ind) < len(dYv): print('Warning: ignoring %d translation steps that are larger ' 'than %g m' % (len(dYv) - len(trans_ok_ind), MAX_TRANS)) rot_ok_ind = np.where(dA < MAX_ROT)[0] if len(rot_ok_ind) < len(dA): print('Warning: ignoring %d rotation steps that are larger ' 'than %g deg' % (len(dA) - len(rot_ok_ind), MAX_ROT))
def test_coregistration(scale_mode, ref_scale, grow_hair, fiducials, fid_match): """Test automated coregistration.""" subject = 'sample' if fiducials is None: fiducials, coord_frame = read_fiducials(fid_fname) assert coord_frame == FIFF.FIFFV_COORD_MRI info = read_info(raw_fname) for d in info['dig']: d['r'] = d['r'] * ref_scale trans = read_trans(trans_fname) coreg = Coregistration(info, subject=subject, subjects_dir=subjects_dir, fiducials=fiducials) assert np.allclose(coreg._last_parameters, coreg._parameters) coreg.set_fid_match(fid_match) default_params = list(coreg._default_parameters) coreg.set_rotation(default_params[:3]) coreg.set_translation(default_params[3:6]) coreg.set_scale(default_params[6:9]) coreg.set_grow_hair(grow_hair) coreg.set_scale_mode(scale_mode) # Identity transform errs_id = coreg.compute_dig_mri_distances() is_scaled = ref_scale != [1., 1., 1.] id_max = 0.03 if is_scaled and scale_mode == '3-axis' else 0.02 assert 0.005 < np.median(errs_id) < id_max # Fiducial transform + scale coreg.fit_fiducials(verbose=True) assert coreg._extra_points_filter is None coreg.omit_head_shape_points(distance=0.02) assert coreg._extra_points_filter is not None errs_fid = coreg.compute_dig_mri_distances() assert_array_less(0, errs_fid) if is_scaled or scale_mode is not None: fid_max = 0.05 fid_med = 0.02 else: fid_max = 0.03 fid_med = 0.01 assert_array_less(errs_fid, fid_max) assert 0.001 < np.median(errs_fid) < fid_med assert not np.allclose(coreg._parameters, default_params) coreg.omit_head_shape_points(distance=-1) coreg.omit_head_shape_points(distance=5. / 1000) assert coreg._extra_points_filter is not None # ICP transform + scale coreg.fit_icp(verbose=True) assert isinstance(coreg.trans, Transform) errs_icp = coreg.compute_dig_mri_distances() assert_array_less(0, errs_icp) if is_scaled or scale_mode == '3-axis': icp_max = 0.015 else: icp_max = 0.01 assert_array_less(errs_icp, icp_max) assert 0.001 < np.median(errs_icp) < 0.004 assert np.rad2deg( _angle_between_quats(rot_to_quat(coreg.trans['trans'][:3, :3]), rot_to_quat(trans['trans'][:3, :3]))) < 13 if scale_mode is None: atol = 1e-7 else: atol = 0.35 assert_allclose(coreg._scale, ref_scale, atol=atol) coreg.reset() assert_allclose(coreg._parameters, default_params)
def test_compute_fine_cal(): """Test computing fine calibration coefficients.""" raw = read_raw_fif(erm_fname) want_cal = read_fine_calibration(cal_mf_fname) got_cal, counts = compute_fine_calibration(raw, cross_talk=ctc, n_imbalance=1, verbose='debug') assert counts == 1 assert set(got_cal.keys()) == set(want_cal.keys()) assert got_cal['ch_names'] == want_cal['ch_names'] # in practice these should never be exactly 1. assert sum([(ic == 1.).any() for ic in want_cal['imb_cals']]) == 0 assert sum([(ic == 1.).any() for ic in got_cal['imb_cals']]) == 0 got_imb = np.array(got_cal['imb_cals'], float) want_imb = np.array(want_cal['imb_cals'], float) assert got_imb.shape == want_imb.shape == (306, 1) got_imb, want_imb = got_imb[:, 0], want_imb[:, 0] orig_locs = np.array([ch['loc'] for ch in raw.info['chs'][:306]]) want_locs = want_cal['locs'] got_locs = got_cal['locs'] assert want_locs.shape == got_locs.shape orig_trans = _loc_to_coil_trans(orig_locs) want_trans = _loc_to_coil_trans(want_locs) got_trans = _loc_to_coil_trans(got_locs) dist = np.linalg.norm(got_trans[:, :3, 3] - want_trans[:, :3, 3], axis=1) assert_allclose(dist, 0., atol=1e-6) dist = np.linalg.norm(got_trans[:, :3, 3] - orig_trans[:, :3, 3], axis=1) assert_allclose(dist, 0., atol=1e-6) orig_quat = rot_to_quat(orig_trans[:, :3, :3]) want_quat = rot_to_quat(want_trans[:, :3, :3]) got_quat = rot_to_quat(got_trans[:, :3, :3]) want_orig_angles = np.rad2deg(_angle_between_quats(want_quat, orig_quat)) got_want_angles = np.rad2deg(_angle_between_quats(got_quat, want_quat)) got_orig_angles = np.rad2deg(_angle_between_quats(got_quat, orig_quat)) for key in ('mag', 'grad'): # imb_cals value p = pick_types(raw.info, meg=key, exclude=()) r2 = np.dot(got_imb[p], want_imb[p]) / (np.linalg.norm(want_imb[p]) * np.linalg.norm(got_imb[p])) assert 0.99 < r2 <= 1.00001, f'{key}: {r2:0.3f}' # rotation angles want_orig_max_angle = want_orig_angles[p].max() got_orig_max_angle = got_orig_angles[p].max() got_want_max_angle = got_want_angles[p].max() if key == 'mag': assert 8 < want_orig_max_angle < 11, want_orig_max_angle assert 1 < got_orig_max_angle < 2, got_orig_max_angle assert 9 < got_want_max_angle < 11, got_want_max_angle else: # Some of these angles are large, but mostly this has to do with # processing a very short (one 10-sec segment), downsampled (90 Hz) # file assert 66 < want_orig_max_angle < 68, want_orig_max_angle assert 67 < got_orig_max_angle < 107, got_orig_max_angle assert 53 < got_want_max_angle < 60, got_want_max_angle kwargs = dict(bad_condition='warning', cross_talk=ctc, coord_frame='meg') raw_sss = maxwell_filter(raw, **kwargs) raw_sss_mf = maxwell_filter(raw, calibration=cal_mf_fname, **kwargs) raw_sss_py = maxwell_filter(raw, calibration=got_cal, **kwargs) _assert_shielding(raw_sss, raw, 26, 27) _assert_shielding(raw_sss_mf, raw, 61, 63) _assert_shielding(raw_sss_py, raw, 61, 63) # redoing with given mag data should yield same result got_cal_redo, _ = compute_fine_calibration(raw, cross_talk=ctc, n_imbalance=1, calibration=got_cal, verbose='debug') assert got_cal['ch_names'] == got_cal_redo['ch_names'] assert_allclose(got_cal['imb_cals'], got_cal_redo['imb_cals'], atol=5e-5) assert_allclose(got_cal['locs'], got_cal_redo['locs'], atol=1e-6) assert sum([(ic == 1.).any() for ic in got_cal['imb_cals']]) == 0 # redoing with 3 imlabance parameters should improve the shielding factor grad_picks = pick_types(raw.info, meg='grad') assert len(grad_picks) == 204 and grad_picks[0] == 0 got_grad_imbs = np.array( [got_cal['imb_cals'][pick] for pick in grad_picks]) assert got_grad_imbs.shape == (204, 1) got_cal_3, _ = compute_fine_calibration(raw, cross_talk=ctc, n_imbalance=3, calibration=got_cal, verbose='debug') got_grad_3_imbs = np.array( [got_cal_3['imb_cals'][pick] for pick in grad_picks]) assert got_grad_3_imbs.shape == (204, 3) corr = np.corrcoef(got_grad_3_imbs[:, 0], got_grad_imbs[:, 0])[0, 1] assert 0.6 < corr < 0.7 raw_sss_py = maxwell_filter(raw, calibration=got_cal_3, **kwargs) _assert_shielding(raw_sss_py, raw, 68, 70)