test_surface.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import datetime
  2. import numpy
  3. import pytest
  4. from freesurfer_surface import setlocale, Vertex, Triangle, _LineSegment, \
  5. Annotation, Surface
  6. from conftest import ANNOTATION_FILE_PATH, SURFACE_FILE_PATH
  7. def test_read_triangular():
  8. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  9. assert surface.creator == b'fabianpeter'
  10. assert surface.creation_datetime \
  11. == datetime.datetime(2019, 5, 9, 22, 37, 41)
  12. assert len(surface.vertices) == 155622
  13. assert len(surface.triangles) == 311240
  14. assert not surface.using_old_real_ras
  15. assert surface.volume_geometry_info == (
  16. b'valid = 1 # volume info valid\n',
  17. b'filename = ../mri/filled-pretess255.mgz\n',
  18. b'volume = 256 256 256\n',
  19. b'voxelsize = 1.000000000000000e+00 1.000000000000000e+00 1.000000000000000e+00\n',
  20. b'xras = -1.000000000000000e+00 0.000000000000000e+00 1.862645149230957e-09\n',
  21. b'yras = 0.000000000000000e+00 -6.655682227574289e-09 -1.000000000000000e+00\n',
  22. b'zras = 0.000000000000000e+00 1.000000000000000e+00 -8.300048648379743e-09\n',
  23. b'cras = -2.773597717285156e+00 1.566547393798828e+01 -7.504364013671875e+00\n')
  24. assert surface.command_lines == [
  25. b'mris_remove_intersection ../surf/lh.orig ../surf/lh.orig'
  26. b' ProgramVersion: $Name: stable6 $'
  27. b' TimeStamp: 2019/05/09-17:42:36-GMT'
  28. b' BuildTimeStamp: Jan 18 2017 16:38:58'
  29. b' CVS: $Id: mris_remove_intersection.c,v 1.6 2011/03/02 00:04:32 nicks Exp $'
  30. b' User: fabianpeter'
  31. b' Machine: host12345'
  32. b' Platform: Linux'
  33. b' PlatformVersion: 4.15.0-46-generic'
  34. b' CompilerName: GCC'
  35. b' CompilerVersion: 40400'
  36. b' ',
  37. b'mris_make_surfaces -orig_white white.preaparc -orig_pial white.preaparc'
  38. b' -aseg ../mri/aseg.presurf -mgz -T1 brain.finalsurfs'
  39. b' fabian20190509 lh ProgramVersion: $Name: $'
  40. b' TimeStamp: 2019/05/09-20:27:28-GMT'
  41. b' BuildTimeStamp: Jan 18 2017 16:38:58'
  42. b' CVS: $Id: mris_make_surfaces.c,v 1.164.2.4 2016/12/13 22:26:32 zkaufman Exp $'
  43. b' User: fabianpeter'
  44. b' Machine: host12345'
  45. b' Platform: Linux'
  46. b' PlatformVersion: 4.15.0-46-generic'
  47. b' CompilerName: GCC'
  48. b' CompilerVersion: 40400'
  49. b' ']
  50. def test_read_triangular_locale():
  51. with setlocale('de_AT.utf8'):
  52. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  53. assert surface.creation_datetime \
  54. == datetime.datetime(2019, 5, 9, 22, 37, 41)
  55. @pytest.mark.parametrize(('creation_datetime', 'expected_str'), [
  56. (datetime.datetime(2019, 5, 9, 22, 37, 41), b'Thu May 9 22:37:41 2019'),
  57. (datetime.datetime(2019, 4, 24, 23, 29, 22), b'Wed Apr 24 23:29:22 2019'),
  58. ])
  59. def test_triangular_strftime(creation_datetime, expected_str):
  60. # pylint: disable=protected-access
  61. assert expected_str == Surface._triangular_strftime(creation_datetime)
  62. def test_read_write_triangular_same(tmpdir):
  63. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  64. output_path = tmpdir.join('surface').strpath
  65. surface.write_triangular(output_path,
  66. creation_datetime=surface.creation_datetime)
  67. with open(output_path, 'rb') as output_file:
  68. with open(SURFACE_FILE_PATH, 'rb') as expected_file:
  69. assert expected_file.read() == output_file.read()
  70. def test_read_write_datetime(tmpdir):
  71. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  72. original_creation_datetime = surface.creation_datetime
  73. output_path = tmpdir.join('surface').strpath
  74. surface.write_triangular(output_path)
  75. assert original_creation_datetime == surface.creation_datetime
  76. new_surface = Surface.read_triangular(output_path)
  77. assert new_surface.creation_datetime > original_creation_datetime
  78. assert datetime.datetime.now() > new_surface.creation_datetime
  79. assert (datetime.datetime.now() - new_surface.creation_datetime) \
  80. < datetime.timedelta(seconds=20)
  81. def test_write_read_triangular_same(tmpdir):
  82. expected_surface = Surface()
  83. expected_surface.creator = b'pytest'
  84. expected_surface.creation_datetime = datetime.datetime.now().replace(microsecond=0)
  85. expected_surface.vertices = [Vertex(0.0, 0.0, 0.0),
  86. Vertex(1.0, 2.0, 3.0),
  87. Vertex(2.0, 4.0, 6.0),
  88. Vertex(3.0, 5.0, 7.0)]
  89. expected_surface.triangles = [Triangle((0, 1, 2)),
  90. Triangle((0, 1, 3)),
  91. Triangle((3, 2, 1))]
  92. expected_surface.using_old_real_ras = False
  93. expected_surface.volume_geometry_info = tuple(b'?\n' for _ in range(8))
  94. expected_surface.command_lines = [b'?', b'!']
  95. output_path = tmpdir.join('surface').strpath
  96. expected_surface.write_triangular(output_path,
  97. creation_datetime=expected_surface.creation_datetime)
  98. resulted_surface = Surface.read_triangular(output_path)
  99. assert numpy.array_equal(expected_surface.vertices,
  100. resulted_surface.vertices)
  101. expected_surface.vertices = resulted_surface.vertices = []
  102. assert vars(expected_surface) == vars(resulted_surface)
  103. def test_write_triangular_same_locale(tmpdir):
  104. surface = Surface()
  105. surface.creator = b'pytest'
  106. surface.volume_geometry_info = tuple(b'?' for _ in range(8))
  107. creation_datetime = datetime.datetime(2018, 12, 31, 21, 42)
  108. output_path = tmpdir.join('surface').strpath
  109. with setlocale('de_AT.utf8'):
  110. surface.write_triangular(output_path,
  111. creation_datetime=creation_datetime)
  112. resulted_surface = Surface.read_triangular(output_path)
  113. assert resulted_surface.creation_datetime == creation_datetime
  114. with open(output_path, 'rb') as output_file:
  115. assert output_file.read().split(b' on ')[1] \
  116. .startswith(b'Mon Dec 31 21:42:00 2018\n')
  117. def test_load_annotation():
  118. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  119. assert not surface.annotation
  120. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  121. assert isinstance(surface.annotation, Annotation)
  122. assert len(surface.annotation.vertex_label_index) == 155622
  123. assert surface.annotation.vertex_label_index[0] == 5
  124. def test_add_vertex():
  125. surface = Surface()
  126. assert not surface.vertices
  127. assert surface.add_vertex(Vertex(1.0, 1.5, 2.0)) == 0
  128. assert len(surface.vertices) == 1
  129. assert surface.vertices[0].anterior == pytest.approx(1.5)
  130. assert surface.add_vertex(Vertex(-3.0, 0.0, 4.0)) == 1
  131. assert len(surface.vertices) == 2
  132. assert surface.vertices[1].right == pytest.approx(-3.0)
  133. @pytest.mark.parametrize('vertices_coords', [
  134. ((0, 0, 0), (2, 4, 0), (2, 4, 3)),
  135. ((0, 0, 0), (2, 4, 0), (2, 4, 3), (0, 0, 3)),
  136. ((1, 1, 0), (3, 5, 0), (3, 5, 3), (1, 1, 3)),
  137. ((1, 1, 7), (3, 5, 7), (3, 5, 3), (1, 1, 3)),
  138. ((1, 1, 1), (3, 5, 7), (3, 5, 9), (1, 1, 3)),
  139. ((3, 5, 7), (1, 1, 1), (1, 1, 3)),
  140. ((3, 5, 7), (1, 1, 1), (1, 1, 3), (3, 5, 9)),
  141. ])
  142. def test_add_rectangle(vertices_coords):
  143. surface = Surface()
  144. for vertex_coords in vertices_coords:
  145. surface.add_vertex(Vertex(*(float(c) for c in vertex_coords)))
  146. surface.add_rectangle(range(len(vertices_coords)))
  147. assert len(surface.vertices) == 4
  148. assert len(surface.triangles) == 2
  149. assert surface.triangles[0].vertex_indices == (0, 1, 2)
  150. assert surface.triangles[1].vertex_indices == (2, 3, 0)
  151. @pytest.mark.parametrize(('vertices_coords', 'expected_extra_vertex_coords'), [
  152. (((0, 0, 0), (2, 4, 0), (2, 4, 3)), (0, 0, 3)),
  153. (((1, 1, 0), (3, 5, 0), (3, 5, 3)), (1, 1, 3)),
  154. (((1, 1, 7), (3, 5, 7), (3, 5, 3)), (1, 1, 3)),
  155. (((1, 1, 1), (3, 5, 7), (3, 5, 9)), (1, 1, 3)),
  156. (((3, 5, 7), (1, 1, 1), (1, 1, 3)), (3, 5, 9)),
  157. ])
  158. def test_add_rectangle_3(vertices_coords, expected_extra_vertex_coords):
  159. surface = Surface()
  160. for vertex_coords in vertices_coords:
  161. surface.add_vertex(Vertex(*(float(c) for c in vertex_coords)))
  162. surface.add_rectangle(range(3))
  163. assert tuple(surface.vertices[3]) \
  164. == pytest.approx(expected_extra_vertex_coords)
  165. def test__get_vertex_label_index():
  166. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  167. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  168. # pylint: disable=protected-access
  169. assert surface._get_vertex_label_index(64290) == 22
  170. assert surface._get_vertex_label_index(72160) == 22
  171. assert surface._get_vertex_label_index(84028) == 24
  172. assert surface._get_vertex_label_index(97356) == 24
  173. assert surface._get_vertex_label_index(123173) == 27
  174. assert surface._get_vertex_label_index(140727) == 27
  175. assert surface._get_vertex_label_index(93859) == 28
  176. assert surface._get_vertex_label_index(78572) == 0
  177. assert surface._get_vertex_label_index(120377) == 0
  178. vertex_index = surface.add_vertex(Vertex(0.0, 21.0, 42.0))
  179. assert surface._get_vertex_label_index(vertex_index) is None
  180. del surface.annotation.vertex_label_index[140727]
  181. assert surface._get_vertex_label_index(140727) is None
  182. def test__find_label_border_segments():
  183. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  184. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  185. precentral_label, = filter(lambda l: l.name == 'precentral',
  186. surface.annotation.labels.values())
  187. # pylint: disable=protected-access
  188. border_segments = set(
  189. surface._find_label_border_segments(precentral_label))
  190. assert len(border_segments) == 417
  191. assert _LineSegment((33450, 32065)) in border_segments
  192. assert _LineSegment((33454, 33450)) in border_segments
  193. for border_vertex_index in [33450, 33454, 32065]:
  194. assert surface.annotation.vertex_label_index[border_vertex_index] == precentral_label.index
  195. for other_vertex_index in [32064, 33449, 33455, 33449, 33455]:
  196. assert _LineSegment((other_vertex_index, border_vertex_index)) \
  197. not in border_segments
  198. assert _LineSegment((border_vertex_index, other_vertex_index)) \
  199. not in border_segments
  200. def test__find_label_border_segments_incomplete_annotation():
  201. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  202. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  203. precentral_label, = filter(lambda l: l.name == 'precentral',
  204. surface.annotation.labels.values())
  205. # pylint: disable=protected-access
  206. assert surface._find_label_border_segments(precentral_label)
  207. surface.triangles.append(Triangle([
  208. surface.add_vertex(Vertex(0.0, 21.0 * factor, 42.0 * factor))
  209. for factor in range(3)
  210. ]))
  211. border_segments = set(
  212. surface._find_label_border_segments(precentral_label))
  213. assert len(border_segments) == 417
  214. def test_find_label_border_polygonal_chains():
  215. surface = Surface.read_triangular(SURFACE_FILE_PATH)
  216. surface.load_annotation_file(ANNOTATION_FILE_PATH)
  217. precentral_label, = filter(lambda l: l.name == 'precentral',
  218. surface.annotation.labels.values())
  219. border_chain, = surface.find_label_border_polygonal_chains(precentral_label)
  220. vertex_indices = list(border_chain.vertex_indices)
  221. assert len(vertex_indices) == 418
  222. min_index = vertex_indices.index(min(vertex_indices))
  223. vertex_indices_normalized = vertex_indices[min_index:] + vertex_indices[:min_index]
  224. assert vertex_indices_normalized[:4] == [32065, 32072, 32073, 32080]
  225. assert vertex_indices_normalized[-4:] == [36281, 34870, 33454, 33450]