@@ -152,6 +152,181 @@ def test_round_trip():
152
152
assert_true (streamlist_equal (streams , streams2 ))
153
153
154
154
155
+ def test_points_processing ():
156
+ # We may need to process points if they are in voxel or mm format
157
+ out_f = BytesIO ()
158
+ ijk0 = np .arange (15 ).reshape ((5 ,3 )) / 2.0
159
+ ijk1 = ijk0 + 20
160
+ vx_streams = [(ijk0 , None , None ), (ijk1 , None , None )]
161
+ vxmm_streams = [(ijk0 * [[2 ,3 ,4 ]], None , None ),
162
+ (ijk1 * [[2 ,3 ,4 ]], None , None )]
163
+ def _rt (streams , hdr , points_space ):
164
+ # run round trip through IO object
165
+ out_f .seek (0 )
166
+ tv .write (out_f , streams , hdr , points_space = points_space )
167
+ out_f .seek (0 )
168
+ res0 = tv .read (out_f )
169
+ out_f .seek (0 )
170
+ return res0 , tv .read (out_f , points_space = points_space )
171
+ # voxmm is the default. In this case we don't do anything to the points,
172
+ # and we let the header pass through without further checks
173
+ (raw_streams , hdr ), (proc_streams , _ ) = _rt (vxmm_streams , {}, None )
174
+ assert_true (streamlist_equal (raw_streams , proc_streams ))
175
+ assert_true (streamlist_equal (vxmm_streams , proc_streams ))
176
+ (raw_streams , hdr ), (proc_streams , _ ) = _rt (vxmm_streams , {}, 'voxmm' )
177
+ assert_true (streamlist_equal (raw_streams , proc_streams ))
178
+ assert_true (streamlist_equal (vxmm_streams , proc_streams ))
179
+ # with 'voxels' as input, check for not all voxel_size == 0, warn if any
180
+ # voxel_size == 0
181
+ for hdr in ( # these cause read / write errors
182
+ # empty header has 0 voxel sizes
183
+ {},
184
+ {'voxel_size' : [0 ,0 ,0 ]}, # the default
185
+ {'voxel_size' : [- 2 ,3 ,4 ]}, # negative not valid
186
+ ):
187
+ # Check error on write
188
+ out_f .seek (0 )
189
+ assert_raises (tv .HeaderError ,
190
+ tv .write , out_f , vx_streams , hdr , None , 'voxel' )
191
+ out_f .seek (0 )
192
+ # bypass write error and check read
193
+ tv .write (out_f , vxmm_streams , hdr , None , points_space = None )
194
+ out_f .seek (0 )
195
+ assert_raises (tv .HeaderError , tv .read , out_f , False , 'voxel' )
196
+ # There's a warning for any voxel sizes == 0
197
+ hdr = {'voxel_size' : [2 , 3 , 0 ]}
198
+ with ErrorWarnings ():
199
+ assert_raises (UserWarning , _rt , vx_streams , hdr , 'voxel' )
200
+ # This should be OK
201
+ hdr = {'voxel_size' : [2 , 3 , 4 ]}
202
+ (raw_streams , hdr ), (proc_streams , _ ) = _rt (vx_streams , hdr , 'voxel' )
203
+ assert_true (streamlist_equal (vxmm_streams , raw_streams ))
204
+ assert_true (streamlist_equal (vx_streams , proc_streams ))
205
+ # Now we try with rasmm points. In this case we need valid voxel_size, and
206
+ # voxel_order, and vox_to_ras. The voxel_order has to match the vox_to_ras,
207
+ # and so do the voxel sizes
208
+ aff = np .diag ([2 ,3 ,4 ,1 ])
209
+ # In this case the trk -> vx and vx -> mm invert each other
210
+ rasmm_streams = vxmm_streams
211
+ for hdr in ( # all these cause read and write errors for rasmm
212
+ # Empty header has no valid affine
213
+ {},
214
+ # Error if ras_to_mm not defined (as in version 1)
215
+ {'voxel_size' : [2 , 3 , 4 ], 'voxel_order' : 'RAS' , 'version' :1 },
216
+ # or it's all zero
217
+ {'voxel_size' : [2 , 3 , 4 ], 'voxel_order' : 'RAS' ,
218
+ 'vox_to_ras' : np .zeros ((4 ,4 ))},
219
+ # as it is by default
220
+ {'voxel_size' : [2 , 3 , 4 ], 'voxel_order' : 'RAS' },
221
+ # or the voxel_size doesn't match the affine
222
+ {'voxel_size' : [2 , 2 , 4 ], 'voxel_order' : 'RAS' ,
223
+ 'vox_to_ras' : aff },
224
+ # or the voxel_order doesn't match the affine
225
+ {'voxel_size' : [2 , 3 , 4 ], 'voxel_order' : 'LAS' ,
226
+ 'vox_to_ras' : aff },
227
+ ):
228
+ # Check error on write
229
+ out_f .seek (0 )
230
+ assert_raises (tv .HeaderError ,
231
+ tv .write , out_f , rasmm_streams , hdr , None , 'rasmm' )
232
+ out_f .seek (0 )
233
+ # bypass write error and check read
234
+ tv .write (out_f , vxmm_streams , hdr , None , points_space = None )
235
+ out_f .seek (0 )
236
+ assert_raises (tv .HeaderError , tv .read , out_f , False , 'rasmm' )
237
+ # This should be OK
238
+ hdr = {'voxel_size' : [2 , 3 , 4 ], 'voxel_order' : 'RAS' ,
239
+ 'vox_to_ras' : aff }
240
+ (raw_streams , hdr ), (proc_streams , _ ) = _rt (rasmm_streams , hdr , 'rasmm' )
241
+ assert_true (streamlist_equal (vxmm_streams , raw_streams ))
242
+ assert_true (streamlist_equal (rasmm_streams , proc_streams ))
243
+ # More complex test to check matrix orientation
244
+ fancy_affine = np .array ([[0. , - 2 , 0 , 10 ],
245
+ [3 , 0 , 0 , 20 ],
246
+ [0 , 0 , 4 , 30 ],
247
+ [0 , 0 , 0 , 1 ]])
248
+ hdr = {'voxel_size' : [3 , 2 , 4 ], 'voxel_order' : 'ALS' ,
249
+ 'vox_to_ras' : fancy_affine }
250
+ def f (pts ): # from vx to mm
251
+ pts = pts [:,[1 ,0 ,2 ]] * [[- 2 ,3 ,4 ]] # apply zooms / reorder
252
+ return pts + [[10 ,20 ,30 ]] # apply translations
253
+ xyz0 , xyz1 = f (ijk0 ), f (ijk1 )
254
+ fancy_rasmm_streams = [(xyz0 , None , None ), (xyz1 , None , None )]
255
+ fancy_vxmm_streams = [(ijk0 * [[3 ,2 ,4 ]], None , None ),
256
+ (ijk1 * [[3 ,2 ,4 ]], None , None )]
257
+ (raw_streams , hdr ), (proc_streams , _ ) = _rt (
258
+ fancy_rasmm_streams , hdr , 'rasmm' )
259
+ assert_true (streamlist_equal (fancy_vxmm_streams , raw_streams ))
260
+ assert_true (streamlist_equal (fancy_rasmm_streams , proc_streams ))
261
+
262
+
263
+ def test__check_hdr_points_space ():
264
+ # Test checking routine for points_space input given header
265
+ # None or voxmm -> no checks, pass through
266
+ assert_equal (tv ._check_hdr_points_space ({}, None ), None )
267
+ assert_equal (tv ._check_hdr_points_space ({}, 'voxmm' ), None )
268
+ # strange value for points_space -> ValueError
269
+ assert_raises (ValueError ,
270
+ tv ._check_hdr_points_space , {}, 'crazy' )
271
+ # Input not in (None, 'voxmm', 'voxels', 'rasmm') - error
272
+ # voxels means check voxel sizes present and not all 0.
273
+ hdr = tv .empty_header ()
274
+ assert_array_equal (hdr ['voxel_size' ], [0 ,0 ,0 ])
275
+ assert_raises (tv .HeaderError ,
276
+ tv ._check_hdr_points_space , hdr , 'voxel' )
277
+ # Negative voxel size gives error - because it is not what trackvis does,
278
+ # and this not what we mean by 'voxmm'
279
+ hdr ['voxel_size' ] = [- 2 , 3 , 4 ]
280
+ assert_raises (tv .HeaderError ,
281
+ tv ._check_hdr_points_space , hdr , 'voxel' )
282
+ # Warning here only
283
+ hdr ['voxel_size' ] = [2 , 3 , 0 ]
284
+ with ErrorWarnings ():
285
+ assert_raises (UserWarning ,
286
+ tv ._check_hdr_points_space , hdr , 'voxel' )
287
+ # This is OK
288
+ hdr ['voxel_size' ] = [2 , 3 , 4 ]
289
+ assert_equal (tv ._check_hdr_points_space (hdr , 'voxel' ), None )
290
+ # rasmm - check there is an affine, that it matches voxel_size and
291
+ # voxel_order
292
+ # no affine
293
+ hdr ['voxel_size' ] = [2 , 3 , 4 ]
294
+ assert_raises (tv .HeaderError ,
295
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
296
+ # still no affine
297
+ hdr ['voxel_order' ] = 'RAS'
298
+ assert_raises (tv .HeaderError ,
299
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
300
+ # nearly an affine, but 0 at position 3,3 - means not recorded in trackvis
301
+ # standard
302
+ hdr ['vox_to_ras' ] = np .diag ([2 ,3 ,4 ,0 ])
303
+ assert_raises (tv .HeaderError ,
304
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
305
+ # This affine doesn't match RAS voxel order
306
+ hdr ['vox_to_ras' ] = np .diag ([- 2 ,3 ,4 ,1 ])
307
+ assert_raises (tv .HeaderError ,
308
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
309
+ # This affine doesn't match the voxel size
310
+ hdr ['vox_to_ras' ] = np .diag ([3 ,3 ,4 ,1 ])
311
+ assert_raises (tv .HeaderError ,
312
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
313
+ # This should be OK
314
+ good_aff = np .diag ([2 ,3 ,4 ,1 ])
315
+ hdr ['vox_to_ras' ] = good_aff
316
+ assert_equal (tv ._check_hdr_points_space (hdr , 'rasmm' ),
317
+ None )
318
+ # Default voxel order of LPS assumed
319
+ hdr ['voxel_order' ] = ''
320
+ # now the RAS affine raises an error
321
+ assert_raises (tv .HeaderError ,
322
+ tv ._check_hdr_points_space , hdr , 'rasmm' )
323
+ # this affine does have LPS voxel order
324
+ good_lps = np .dot (np .diag ([- 1 ,- 1 ,1 ,1 ]), good_aff )
325
+ hdr ['vox_to_ras' ] = good_lps
326
+ assert_equal (tv ._check_hdr_points_space (hdr , 'rasmm' ),
327
+ None )
328
+
329
+
155
330
def test_empty_header ():
156
331
for endian in '<>' :
157
332
for version in (1 , 2 ):
0 commit comments