/mne/transforms.py

https://github.com/mluessi/mne-python
Python | 235 lines | 197 code | 11 blank | 27 comment | 7 complexity | 963d54691b3afa466e068a84e304945a MD5 | raw file
  1. import copy
  2. import numpy as np
  3. from scipy import linalg
  4. from .fiff import FIFF
  5. from .fiff.open import fiff_open
  6. from .fiff.tag import read_tag
  7. def invert_transform(trans):
  8. """Invert a transformation between coordinate systems
  9. """
  10. itrans = copy.deepcopy(trans)
  11. aux = itrans['from']
  12. itrans['from'] = itrans['to']
  13. itrans['to'] = aux
  14. itrans['trans'] = linalg.inv(itrans['trans'])
  15. return itrans
  16. def transform_source_space_to(src, dest, trans):
  17. """Transform source space data to the desired coordinate system
  18. Parameters
  19. ----------
  20. src : dict
  21. Source space
  22. dest : dict
  23. destination coordinate system
  24. trans : dict
  25. Transformation
  26. Returns
  27. -------
  28. res : dict
  29. Transformed source space
  30. """
  31. if src['coord_frame'] == dest:
  32. res = src
  33. return res
  34. if trans['to'] == src['coord_frame'] and trans['from'] == dest:
  35. trans = invert_transform(trans)
  36. elif trans['from'] != src['coord_frame'] or trans['to'] != dest:
  37. raise ValueError('Cannot transform the source space using this '
  38. 'coordinate transformation')
  39. t = trans['trans'][:3, :]
  40. res = src
  41. res['coord_frame'] = dest
  42. res['rr'] = np.dot(np.c_[res['rr'], np.ones((res['np'], 1))], t.T)
  43. res['nn'] = np.dot(np.c_[res['nn'], np.zeros((res['np'], 1))], t.T)
  44. return res
  45. def transform_coordinates(filename, pos, orig, dest):
  46. """Transform coordinates between various MRI-related coordinate frames
  47. Parameters
  48. ----------
  49. filename: string
  50. Name of a fif file containing the coordinate transformations
  51. This file can be conveniently created with mne_collect_transforms
  52. pos: array of shape N x 3
  53. array of locations to transform (in meters)
  54. orig: 'meg' | 'mri'
  55. Coordinate frame of the above locations.
  56. 'meg' is MEG head coordinates
  57. 'mri' surface RAS coordinates
  58. dest: 'meg' | 'mri' | 'fs_tal' | 'mni_tal'
  59. Coordinate frame of the result.
  60. 'mni_tal' is MNI Talairach
  61. 'fs_tal' is FreeSurfer Talairach
  62. Return
  63. ------
  64. trans_pos: array of shape N x 3
  65. The transformed locations
  66. Example
  67. -------
  68. >>> transform_coordinates('all-trans.fif', np.eye(3), 'meg', 'fs_tal')
  69. >>> transform_coordinates('all-trans.fif', np.eye(3), 'mri', 'mni_tal')
  70. """
  71. # Read the fif file containing all necessary transformations
  72. fid, tree, directory = fiff_open(filename)
  73. coord_names = dict(mri=FIFF.FIFFV_COORD_MRI,
  74. meg=FIFF.FIFFV_COORD_HEAD,
  75. mni_tal=FIFF.FIFFV_MNE_COORD_MNI_TAL,
  76. fs_tal=FIFF.FIFFV_MNE_COORD_FS_TAL)
  77. orig = coord_names[orig]
  78. dest = coord_names[dest]
  79. T0 = T1 = T2 = T3plus = T3minus = None
  80. for d in directory:
  81. if d.kind == FIFF.FIFF_COORD_TRANS:
  82. tag = read_tag(fid, d.pos)
  83. trans = tag.data
  84. if (trans['from'] == FIFF.FIFFV_COORD_MRI and
  85. trans['to'] == FIFF.FIFFV_COORD_HEAD):
  86. T0 = invert_transform(trans)
  87. elif (trans['from'] == FIFF.FIFFV_COORD_MRI and
  88. trans['to'] == FIFF.FIFFV_MNE_COORD_RAS):
  89. T1 = trans
  90. elif (trans['from'] == FIFF.FIFFV_MNE_COORD_RAS and
  91. trans['to'] == FIFF.FIFFV_MNE_COORD_MNI_TAL):
  92. T2 = trans
  93. elif trans['from'] == FIFF.FIFFV_MNE_COORD_MNI_TAL:
  94. if trans['to'] == FIFF.FIFFV_MNE_COORD_FS_TAL_GTZ:
  95. T3plus = trans
  96. elif trans['to'] == FIFF.FIFFV_MNE_COORD_FS_TAL_LTZ:
  97. T3minus = trans
  98. fid.close()
  99. #
  100. # Check we have everything we need
  101. #
  102. if ((orig == FIFF.FIFFV_COORD_HEAD and T0 is None) or (T1 is None)
  103. or (T2 is None) or (dest == FIFF.FIFFV_MNE_COORD_FS_TAL and
  104. ((T3minus is None) or (T3minus is None)))):
  105. raise ValueError('All required coordinate transforms not found')
  106. #
  107. # Go ahead and transform the data
  108. #
  109. if pos.shape[1] != 3:
  110. raise ValueError('Coordinates must be given in a N x 3 array')
  111. if dest == orig:
  112. trans_pos = pos.copy()
  113. else:
  114. n_points = pos.shape[0]
  115. pos = np.c_[pos, np.ones(n_points)].T
  116. if orig == FIFF.FIFFV_COORD_HEAD:
  117. pos = np.dot(T0['trans'], pos)
  118. elif orig != FIFF.FIFFV_COORD_MRI:
  119. raise ValueError('Input data must be in MEG head or surface RAS '
  120. 'coordinates')
  121. if dest == FIFF.FIFFV_COORD_HEAD:
  122. pos = np.dot(linalg.inv(T0['trans']), pos)
  123. elif dest != FIFF.FIFFV_COORD_MRI:
  124. pos = np.dot(np.dot(T2['trans'], T1['trans']), pos)
  125. if dest != FIFF.FIFFV_MNE_COORD_MNI_TAL:
  126. if dest == FIFF.FIFFV_MNE_COORD_FS_TAL:
  127. for k in xrange(n_points):
  128. if pos[2, k] > 0:
  129. pos[:, k] = np.dot(T3plus['trans'], pos[:, k])
  130. else:
  131. pos[:, k] = np.dot(T3minus['trans'], pos[:, k])
  132. else:
  133. raise ValueError('Illegal choice for the output '
  134. 'coordinates')
  135. trans_pos = pos[:3, :].T
  136. return trans_pos
  137. # def transform_meg_chs(chs, trans):
  138. # """
  139. # %
  140. # % [res, count] = fiff_transform_meg_chs(chs,trans)
  141. # %
  142. # % Move to another coordinate system in MEG channel channel info
  143. # % Count gives the number of channels transformed
  144. # %
  145. # % NOTE: Only the coil_trans field is modified by this routine, not
  146. # % loc which remains to reflect the original data read from the fif file
  147. # %
  148. # %
  149. #
  150. # XXX
  151. # """
  152. #
  153. # res = copy.deepcopy(chs)
  154. #
  155. # count = 0
  156. # t = trans['trans']
  157. # for ch in res:
  158. # if (ch['kind'] == FIFF.FIFFV_MEG_CH
  159. # or ch['kind'] == FIFF.FIFFV_REF_MEG_CH):
  160. # if (ch['coord_frame'] == trans['from']
  161. # and ch['coil_trans'] is not None):
  162. # ch['coil_trans'] = np.dot(t, ch['coil_trans'])
  163. # ch['coord_frame'] = trans['to']
  164. # count += 1
  165. #
  166. # if count > 0:
  167. # print '\t%d MEG channel locations transformed' % count
  168. #
  169. # return res, count
  170. # def transform_eeg_chs(chs, trans):
  171. # """
  172. # %
  173. # % [res, count] = fiff_transform_eeg_chs(chs,trans)
  174. # %
  175. # % Move to another coordinate system in EEG channel channel info
  176. # % Count gives the number of channels transformed
  177. # %
  178. # % NOTE: Only the eeg_loc field is modified by this routine, not
  179. # % loc which remains to reflect the original data read from the fif file
  180. # %
  181. #
  182. # XXX
  183. # """
  184. # res = copy.deepcopy(chs)
  185. #
  186. # count = 0
  187. # #
  188. # # Output unaugmented vectors from the transformation
  189. # #
  190. # t = trans['trans'][:3,:]
  191. # for ch in res:
  192. # if ch['kind'] == FIFF.FIFFV_EEG_CH:
  193. # if (ch['coord_frame'] == trans['from']
  194. # and ch['eeg_loc'] is not None):
  195. # #
  196. # # Transform the augmented EEG location vectors
  197. # #
  198. # for p in range(ch['eeg_loc'].shape[1]):
  199. # ch['eeg_loc'][:, p] = np.dot(t,
  200. # np.r_[ch['eeg_loc'][:,p], 1])
  201. # count += 1
  202. # ch['coord_frame'] = trans['to']
  203. #
  204. # if count > 0:
  205. # print '\t%d EEG electrode locations transformed\n' % count
  206. #
  207. # return res, count