Пример #1
0
def load_surface(lh, rh, with_normals=True, join=False):
    """
    Loads surfaces.

    Parameters
    ----------
    with_normals : bool, optional
        Whether to compute surface normals. Default is True.
    join : bool, optional.
        If False, return one surface for left and right hemispheres. Otherwise,
        return a single surface as a combination of both left and right.
        surfaces. Default is False.

    Returns
    -------
    surf : tuple of BSPolyData or BSPolyData.
        Surfaces for left and right hemispheres. If ``join == True``, one
        surface with both hemispheres.
    """

    surfs = [None] * 2
    for i, side in enumerate([lh, rh]):
        surfs[i] = read_surface(side)
        if with_normals:
            nf = wrap_vtk(vtkPolyDataNormals,
                          splitting=False,
                          featureAngle=0.1)
            surfs[i] = serial_connect(surfs[i], nf)

    if join:
        return combine_surfaces(*surfs)
    return surfs[0], surfs[1]
Пример #2
0
def test_cell_types():
    ss = vtk.vtkSphereSource()
    ss.Update()
    st = wrap_vtk(ss.GetOutput())
    sl = mc.to_lines(st)
    sv = mc.to_vertex(st)

    assert checks.get_cell_types(st) == np.array([VTK_TRIANGLE])
    assert checks.get_cell_types(st.VTKObject) == np.array([VTK_TRIANGLE])
    assert checks.get_cell_types(sl) == np.array([VTK_LINE])
    assert checks.get_cell_types(sv) == np.array([VTK_VERTEX])

    assert checks.get_number_of_cell_types(st) == 1
    assert checks.get_number_of_cell_types(st.VTKObject) == 1
    assert checks.get_number_of_cell_types(sl) == 1
    assert checks.get_number_of_cell_types(sv) == 1

    assert checks.has_unique_cell_type(st)
    assert checks.has_unique_cell_type(st.VTKObject)
    assert checks.has_unique_cell_type(sl)
    assert checks.has_unique_cell_type(sv)

    assert checks.has_only_triangle(st)
    assert checks.has_only_triangle(st.VTKObject)
    assert checks.has_only_line(sl)
    assert checks.has_only_vertex(sv)

    ss2 = vtk.vtkSphereSource()
    ss2.SetRadius(3)
    ss2.Update()
    s2 = ss2.GetOutput()

    app = vtk.vtkAppendPolyData()
    app.AddInputData(sl.VTKObject)
    app.AddInputData(s2)
    app.Update()
    spl = wrap_vtk(app.GetOutput())

    cell_types = np.sort([VTK_TRIANGLE, VTK_LINE])
    assert np.all(checks.get_cell_types(spl) == cell_types)
    assert checks.get_number_of_cell_types(spl) == cell_types.size
    assert checks.has_unique_cell_type(spl) is False
    assert checks.has_only_triangle(spl) is False
    assert checks.has_only_line(spl) is False
    assert checks.has_only_vertex(spl) is False
Пример #3
0
def test_moran():
    # Sphere with points as locations to build spatial matrix
    sphere = wrap_vtk(vtk.vtkSphereSource,
                      radius=20,
                      thetaResolution=10,
                      phiResolution=5)
    sphere = to_data(sphere)
    n_pts = sphere.n_points

    # Features to randomize
    rs = np.random.RandomState(0)
    feats = rs.randn(n_pts, 2)

    # build spatial weight matrix
    a = me.get_immediate_distance(sphere)
    a.data **= -1

    # test default
    v, w = compute_mem(a, tol=1e-7)
    assert w.shape[0] <= (n_pts - 1)
    assert v.shape == (n_pts, w.shape[0])

    r1 = moran_randomization(feats[:, 0], v, n_rep=10, random_state=0)
    assert r1.shape == (10, n_pts)

    r2 = moran_randomization(feats, v, n_rep=10, random_state=0)
    assert r2.shape == (10, n_pts, 2)

    # test default dense
    mem, ev = compute_mem(a.toarray(), tol=1e-7)
    assert np.allclose(w, ev)
    assert np.allclose(v, mem)

    r1 = moran_randomization(feats[:, 0], mem, n_rep=10, random_state=0)
    assert r1.shape == (10, n_pts)

    r2 = moran_randomization(feats, mem, n_rep=10, random_state=0)
    assert r2.shape == (10, n_pts, 2)

    # test object api
    msr = MoranRandomization(n_rep=10, random_state=0, tol=1e-7)
    msr.fit(a)
    assert np.allclose(msr.mev_, ev)
    assert np.allclose(msr.mem_, mem)
    assert np.allclose(r1, msr.randomize(feats[:, 0]))
    assert np.allclose(r2, msr.randomize(feats))

    # test object api with PolyData
    msr = MoranRandomization(n_rep=10, random_state=0, tol=1e-7)
    msr.fit(sphere)
    assert np.allclose(msr.mev_, ev)
    assert np.allclose(msr.mem_, mem)
    assert np.allclose(r1, msr.randomize(feats[:, 0]))
    assert np.allclose(r2, msr.randomize(feats))
Пример #4
0
def _generate_sphere():
    """Generates a vtk sphere of 50 vertices.

    Returns
    -------
    BSPolyData
        Mesh of a sphere.
    """
    s = vtk.vtkSphereSource()
    s.Update()
    return wrap_vtk(s.GetOutput())
Пример #5
0
def test_pipeline():
    # check defaults
    s = vtk.vtkSphereSource()
    f = vtk.vtkSmoothPolyDataFilter()
    out = serial_connect(s, f)
    assert isinstance(out, BSPolyData)
    assert out.n_points > 0

    # check update filter
    s = vtk.vtkSphereSource()
    f = vtk.vtkSmoothPolyDataFilter()
    out = serial_connect(s, f, as_data=False)
    assert isinstance(out, BSAlgorithm)
    assert out.GetOutput().GetNumberOfPoints() > 0

    # check filter no update
    s = vtk.vtkSphereSource()
    f = vtk.vtkSmoothPolyDataFilter()
    out = serial_connect(s, f, as_data=False, update=False)
    assert isinstance(out, BSAlgorithm)
    assert out.GetOutput().GetNumberOfPoints() == 0

    # check non-existing port
    s = vtk.vtkSphereSource()
    f = vtk.vtkSmoothPolyDataFilter()
    out = serial_connect(s, f, port=1)
    assert out is None

    # check get all possible ports
    s = vtk.vtkSphereSource()
    f = vtk.vtkSmoothPolyDataFilter()
    out = serial_connect(s, f, port=-1)
    assert isinstance(out, list)
    assert len(out) == f.GetNumberOfOutputPorts()
    assert isinstance(out[0], BSPolyData)
    assert out[0].n_points > 0

    # check accept wrappers
    s = wrap_vtk(vtk.vtkSphereSource)
    f = wrap_vtk(vtk.vtkSmoothPolyDataFilter)
    assert isinstance(serial_connect(s, f), BSPolyData)
Пример #6
0
def test_variogram_sampled():
    # Sampled class
    # Sphere with points as locations to build distance matrix
    sphere = wrap_vtk(vtk.vtkSphereSource,
                      radius=20,
                      thetaResolution=50,
                      phiResolution=25)
    sphere = to_data(sphere)
    points = sphere.GetPoints()
    npoints = sphere.n_points
    distmat = squareform(pdist(points))  # pairwise distance matrix

    rs = np.random.RandomState(0)
    brainmap = rs.randn(npoints, 1).flatten()
    brainmap[0] = np.nan

    # Create a mask file
    mask = np.zeros(npoints, dtype=int)
    mask[[3, 4, 5]] = 1

    # Save distmat and mask file
    temp = gettempdir()
    dist_file = join(temp, 'distmat.txt')
    mask_file = join(temp, 'mask.txt')
    np.savetxt(dist_file, distmat)
    np.savetxt(mask_file, mask)
    assert exists(dist_file) and exists(mask_file)

    # Convert to memmap
    files = txt2memmap(dist_file=dist_file,
                       output_dir=temp,
                       maskfile=mask_file)
    assert exists(files['index'])
    assert exists(files['distmat'])

    masked_map = brainmap[np.where(mask == 0)]

    # Generate surrogates
    gen = Sampled(masked_map,
                  files['distmat'],
                  files['index'],
                  knn=100,
                  ns=100)
    surrs = gen(n=5)
    assert gen.D.shape == (npoints - 3, 100)
    assert surrs.shape == (5, npoints - 3)
    assert np.allclose(gen.x.data[1:], masked_map[1:])
    assert np.isnan(gen.x.data[0])
    assert not np.isnan(surrs).any()
Пример #7
0
def test_variogram_base():
    # Base class
    # Sphere with points as locations to build distance matrix
    sphere = wrap_vtk(vtk.vtkSphereSource,
                      radius=20,
                      thetaResolution=20,
                      phiResolution=10)
    sphere = to_data(sphere)
    points = sphere.GetPoints()
    npoints = sphere.n_points
    distmat = squareform(pdist(points))  # pairwise distance matrix

    rs = np.random.RandomState(0)
    brainmap = rs.randn(npoints, 1).flatten()
    brainmap[0] = np.nan

    # Generate surrogates
    gen = Base(brainmap, distmat)
    surrs = gen(n=10)
    assert surrs.shape == (10, npoints)
    assert np.allclose(gen.D, distmat)
    assert np.allclose(gen.x.data[1:], brainmap[1:])
    assert np.isnan(gen.x.data[0])
    assert not np.isnan(surrs).any()
Пример #8
0
def test_array_operations():
    s = _generate_sphere()

    # Cell area
    area = aop.compute_cell_area(s)
    assert isinstance(area, np.ndarray)
    assert area.shape == (s.n_cells, )

    s2 = aop.compute_cell_area(s, append=True, key='CellArea')
    assert s is s2
    assert np.allclose(s2.CellData['CellArea'], area)

    # Cell centers
    centers = aop.compute_cell_center(s)
    assert isinstance(centers, np.ndarray)
    assert centers.shape == (s.n_cells, 3)

    s2 = aop.compute_cell_center(s, append=True, key='CellCenter')
    assert s is s2
    assert np.allclose(s2.CellData['CellCenter'], centers)

    # Adjacent cells
    n_adj = aop.get_n_adjacent_cells(s)
    assert isinstance(n_adj, np.ndarray)
    assert n_adj.shape == (s.n_points, )

    s2 = aop.get_n_adjacent_cells(s, append=True, key='NAdjCells')
    assert s is s2
    assert np.all(s2.PointData['NAdjCells'] == n_adj)

    # map cell data to point data
    area2 = aop.map_celldata_to_pointdata(s, area)
    area3 = aop.map_celldata_to_pointdata(s, 'CellArea', red_func='mean')
    assert area.dtype == area2.dtype
    assert area.dtype == area3.dtype
    assert np.allclose(area2, area3)

    area4 = aop.map_celldata_to_pointdata(s,
                                          'CellArea',
                                          red_func='mean',
                                          dtype=np.float32)
    assert area4.dtype == np.float32

    for op in ['sum', 'mean', 'mode', 'one_third', 'min', 'max']:
        ap = aop.map_celldata_to_pointdata(s, 'CellArea', red_func=op)
        assert ap.shape == (s.n_points, )

        name = 'CellArea_{}'.format(op)
        s2 = aop.map_celldata_to_pointdata(s,
                                           'CellArea',
                                           red_func=op,
                                           append=True,
                                           key=name)
        assert np.allclose(s2.PointData[name], ap)

    # map point data to cell  data
    fc = aop.map_pointdata_to_celldata(s, n_adj)
    fc2 = aop.map_pointdata_to_celldata(s, 'NAdjCells', red_func='mean')
    assert fc.dtype == fc2.dtype
    assert fc.dtype == fc2.dtype
    assert np.allclose(fc, fc2)

    fc3 = aop.map_pointdata_to_celldata(s,
                                        'NAdjCells',
                                        red_func='mean',
                                        dtype=np.float32)
    assert fc3.dtype == np.float32

    for op in ['sum', 'mean', 'mode', 'one_third', 'min', 'max']:
        ac = aop.map_pointdata_to_celldata(s, 'NAdjCells', red_func=op)
        assert ac.shape == (s.n_cells, )

        name = 'NAdjCells_{}'.format(op)
        s2 = aop.map_pointdata_to_celldata(s,
                                           'NAdjCells',
                                           red_func=op,
                                           append=True,
                                           key=name)
        assert np.allclose(s2.CellData[name], ac)

    # Point area
    area = aop.compute_point_area(s)
    assert isinstance(area, np.ndarray)
    assert area.shape == (s.n_points, )

    s2 = aop.compute_point_area(s, append=True, key='PointArea')
    assert s is s2
    assert np.allclose(s2.PointData['PointArea'], area)

    s2 = aop.compute_point_area(s,
                                cell_area='CellArea',
                                append=True,
                                key='PointArea2')
    assert s is s2
    assert np.allclose(s2.PointData['PointArea2'], area)

    # Connected components
    cc = mop.get_connected_components(s)
    assert cc.shape == (s.n_points, )
    assert np.unique(cc).size == 1

    s2 = mop.get_connected_components(s, append=True, key='components')
    assert s is s2
    assert np.all(cc == s2.PointData['components'])

    # labeling border
    labeling = (s.Points[:, 0] > s.Points[:, 0].mean()).astype(int)
    s.append_array(labeling, name='parc', at='p')

    border = aop.get_labeling_border(s, labeling)
    assert border.shape == (s.n_points, )
    assert np.unique(border).size == 2

    border2 = aop.get_labeling_border(s, 'parc')
    assert np.all(border == border2)

    # parcellation centroids
    cent = aop.get_parcellation_centroids(s, labeling, non_centroid=2)
    assert cent.shape == (s.n_points, )
    assert np.unique(cent).size == 3
    assert np.count_nonzero(cent == 0) == 1
    assert np.count_nonzero(cent == 1) == 1
    assert np.count_nonzero(cent == 2) == s.n_points - 2

    cent2 = aop.get_parcellation_centroids(s, 'parc', non_centroid=2)
    assert np.all(cent == cent2)

    # propagate labeling
    labeling2 = labeling.astype(np.float32)
    labeling2[:10] = np.nan
    s.append_array(labeling2, name='parc2', at='p')

    pl1 = aop.propagate_labeling(s, labeling2)
    assert pl1.shape == (s.n_points, )
    assert np.count_nonzero(np.isnan(pl1)) == 0
    assert np.all(np.unique(pl1) == np.array([0, 1]))

    pl2 = aop.propagate_labeling(s, 'parc2')
    assert np.all(pl1 == pl2)

    # smooth array
    for k in ['uniform', 'gaussian', 'inverse_distance']:
        sa = aop.smooth_array(s, n_adj, kernel=k)
        assert sa.shape == (s.n_points, )

        sa2 = aop.smooth_array(s, 'NAdjCells', kernel=k)
        assert np.all(sa == sa2)

    # resample pointdata
    s2 = wrap_vtk(vtk.vtkSphereSource, phiResolution=20)
    s2.Update()
    s2 = wrap_vtk(s2.output)

    rd = aop.resample_pointdata(s, s2, 'NAdjCells')
    assert rd.shape == (s2.n_points, )

    rd2 = aop.resample_pointdata(s, s2, 'NAdjCells', red_func='mean')
    assert np.all(rd == rd2)
Пример #9
0
def _generate_sphere():
    s = vtk.vtkSphereSource()
    s.Update()
    return wrap_vtk(s.GetOutput())
Пример #10
0
def test_mesh_elements():
    s = _generate_sphere()

    ee = vtk.vtkExtractEdges()
    ee.SetInputData(s.VTKObject)
    ee.Update()
    ee = wrap_vtk(ee.GetOutput())
    n_edges = ee.n_cells

    assert np.all(me.get_points(s) == s.Points)
    assert np.all(me.get_cells(s) == s.GetCells2D())
    assert me.get_extent(s).shape == (3, )

    pc = me.get_point2cell_connectivity(s)
    assert pc.shape == (s.n_points, s.n_cells)
    assert pc.dtype == np.uint8
    assert np.all(pc.sum(axis=0) == 3)

    cp = me.get_cell2point_connectivity(s)
    assert pc.dtype == np.uint8
    assert (pc - cp.T).nnz == 0

    adj = me.get_immediate_adjacency(s)
    assert adj.shape == (s.n_points, s.n_points)
    assert adj.dtype == np.uint8
    assert adj.nnz == (2 * n_edges + s.n_points)

    adj2 = me.get_immediate_adjacency(s, include_self=False)
    assert adj2.shape == (s.n_points, s.n_points)
    assert adj2.dtype == np.uint8
    assert adj2.nnz == (2 * n_edges)

    radj = me.get_ring_adjacency(s)
    assert radj.dtype == np.uint8
    assert (adj - radj).nnz == 0

    radj2 = me.get_ring_adjacency(s, include_self=False)
    assert radj2.dtype == np.uint8
    assert (adj2 - radj2).nnz == 0

    radj3 = me.get_ring_adjacency(s, n_ring=2, include_self=False)
    assert radj3.dtype == np.uint8
    assert (radj3 - adj2).nnz > 0

    d = me.get_immediate_distance(s)
    assert d.shape == (s.n_points, s.n_points)
    assert d.dtype == np.float
    assert d.nnz == adj2.nnz

    d2 = me.get_immediate_distance(s, metric='sqeuclidean')
    d_sq = d.copy()
    d_sq.data **= 2
    assert np.allclose(d_sq.A, d2.A)

    rd = me.get_ring_distance(s)
    assert rd.dtype == np.float
    assert np.allclose(d.A, rd.A)

    rd2 = me.get_ring_distance(s, n_ring=2)
    assert (rd2 - d).nnz > 0

    assert me.get_cell_neighbors(s).shape == (s.n_cells, s.n_cells)
    assert me.get_edges(s).shape == (n_edges, 2)
    assert me.get_edge_length(s).shape == (n_edges, )

    assert me.get_boundary_points(s).size == 0
    assert me.get_boundary_edges(s).size == 0
    assert me.get_boundary_cells(s).size == 0
Пример #11
0
from vtk import vtkPolyDataNormals

from brainspace.mesh.mesh_io import read_surface
from brainspace.mesh.mesh_operations import combine_surfaces
from brainspace.utils.parcellation import reduce_by_labels
from brainspace.vtk_interface import wrap_vtk, serial_connect

template_path = "Z:/hschoi/backup/hschoi/template/MMP"
template_L = "S900.L.midthickness_MSMAll.10k_fs_LR.surf.gii"  # S900.L.midthickness_MSMAll.10k_fs_LR.surf.gii # L.very_inflated_MSMAll.10k_fs_LR.surf.gii
template_R = "S900.R.midthickness_MSMAll.10k_fs_LR.surf.gii"  # S900.R.midthickness_MSMAll.10k_fs_LR.surf.gii # R.very_inflated_MSMAll.10k_fs_LR.surf.gii

surfs = [None] * 2

surfs[0] = read_surface(join(template_path, template_L))
nf = wrap_vtk(vtkPolyDataNormals, splitting=False, featureAngle=0.1)
surf_lh = serial_connect(surfs[0], nf)

surfs[1] = read_surface(join(template_path, template_R))
nf = wrap_vtk(vtkPolyDataNormals, splitting=False, featureAngle=0.1)
surf_rh = serial_connect(surfs[1], nf)

# Visualization

from brainspace.datasets import load_group_fc, load_parcellation, load_conte69
from brainspace.gradient import GradientMaps
from brainspace.plotting import plot_hemispheres
from brainspace.utils.parcellation import map_to_labels

atlas = np.load("Z:\\hschoi\\backup\\hschoi\\template\\MMP\\MMP.10k_fs_LR.npy")
Пример #12
0
def test_basic_wrapping():

    assert is_vtk(vtk.vtkPolyData) is False
    assert is_vtk(vtk.vtkPolyData()) is True
    assert is_vtk(None) is False

    ws = wrap_vtk(vtk.vtkPolyData())
    assert is_wrapper(ws.VTKObject) is False
    assert is_wrapper(ws) is True
    assert is_wrapper(None) is False

    assert wrap_vtk(None) is None
    assert wrap_vtk(ws) is ws

    # test source
    s = wrap_vtk(vtk.vtkSphereSource, radius=3)
    assert isinstance(s, BSAlgorithm)
    assert s.is_source
    assert s.VTKObject.GetRadius() == 3

    s.setVTK(radius=4.5)
    assert s.VTKObject.GetRadius() == 4.5

    s.radius = 2.5
    assert s.VTKObject.GetRadius() == 2.5

    with pytest.raises(Exception):
        s.radius2 = 0

    # test filter (no source no sink)
    s = wrap_vtk(vtk.vtkSmoothPolyDataFilter, numberOfIterations=2)
    assert isinstance(s, BSAlgorithm)
    assert s.is_filter
    assert s.VTKObject.GetNumberOfIterations() == 2

    # test sink
    s = wrap_vtk(vtk.vtkXMLPolyDataWriter, filename='some/path')
    assert isinstance(s, BSAlgorithm)
    assert s.is_sink
    assert s.VTKObject.GetFileName() == 'some/path'

    # test vtkPolyDataMapper
    s = wrap_vtk(vtk.vtkPolyDataMapper,
                 arrayName='array_name',
                 scalarMode='UseCellData')
    assert isinstance(s, BSAlgorithm)
    assert isinstance(s, BSPolyDataMapper)
    assert s.is_sink
    assert s.VTKObject.GetScalarModeAsString() == 'UseCellData'
    assert s.VTKObject.GetArrayName() == 'array_name'
    assert s.VTKObject.GetArrayAccessMode() == 1

    # test change in access mode
    s.arrayid = 3
    assert s.VTKObject.GetArrayId() == 3
    assert s.VTKObject.GetArrayAccessMode() == 0

    # test actor access to property
    s = wrap_vtk(vtk.vtkActor, opacity=0.5, interpolation='phong')
    assert isinstance(s, BSActor)
    assert s.VTKObject.GetProperty().GetOpacity() == 0.5
    assert s.property.opacity == 0.5
    assert s.opacity == 0.5

    # test implemented wrapper
    s = wrap_vtk(vtk.vtkPolyData)
    assert isinstance(s, BSPolyData)

    # test not implemented --> default to superclass
    s = wrap_vtk(vtk.vtkImageData)
    assert isinstance(s, BSDataSet)

    # test wrappers to create objects
    with pytest.raises(TypeError):
        w = BSVTKObjectWrapper()

    with pytest.raises(AttributeError):
        w = BSVTKObjectWrapper(None)

    pd = BSPolyData()
    assert isinstance(pd.VTKObject, vtk.vtkPolyData)
    a = BSActor()
    assert isinstance(a.VTKObject, vtk.vtkActor)
    m = BSPolyDataMapper()
    assert isinstance(m.VTKObject, vtk.vtkPolyDataMapper)
Пример #13
0
def test_spin():
    # Create dummy spheres or left and right hemispheres
    sphere_lh = wrap_vtk(vtk.vtkSphereSource,
                         radius=20,
                         thetaResolution=10,
                         phiResolution=5)
    sphere_lh = to_data(sphere_lh)
    pts_lh = sphere_lh.Points
    n_pts_lh = sphere_lh.n_points

    # Right with more points
    sphere_rh = wrap_vtk(vtk.vtkSphereSource,
                         radius=20,
                         thetaResolution=10,
                         phiResolution=6)
    sphere_rh = to_data(sphere_rh)
    pts_rh = sphere_rh.Points
    n_pts_rh = sphere_rh.n_points

    # Features to randomize
    rs = np.random.RandomState(0)
    feats_lh = rs.randn(n_pts_lh, 2)
    feats_rh = rs.randn(n_pts_rh, 2)

    # generate spin indices
    ridx1 = _generate_spins(pts_lh, n_rep=10, random_state=0)
    assert ridx1['lh'].shape == (10, n_pts_lh)
    assert 'rh' not in ridx1

    ridx2 = _generate_spins(pts_lh, points_rh=pts_rh, n_rep=10, random_state=0)
    assert ridx2['lh'].shape == (10, n_pts_lh)
    assert ridx2['rh'].shape == (10, n_pts_rh)

    # test api lh
    sp = SpinPermutations(n_rep=10, random_state=0)
    sp.fit(pts_lh)
    assert np.all(sp.spin_lh_ == ridx1['lh'])
    assert sp.spin_rh_ is None
    assert sp.randomize(feats_lh[:, 0]).shape == (10, n_pts_lh)
    assert sp.randomize(feats_lh).shape == (10, n_pts_lh, 2)

    # test api lh and rh
    sp = SpinPermutations(n_rep=10, random_state=0)
    sp.fit(pts_lh, points_rh=pts_rh)
    assert np.all(sp.spin_lh_ == ridx2['lh'])
    assert np.all(sp.spin_rh_ == ridx2['rh'])

    r1 = sp.randomize(feats_lh[:, 0])
    assert r1[0].shape == (10, n_pts_lh)
    assert r1[1] is None

    r1bis = spin_permutations(pts_lh, feats_lh[:, 0], n_rep=10, random_state=0)
    assert np.all(r1[0] == r1bis)

    r2 = sp.randomize(feats_lh)
    assert r2[0].shape == (10, n_pts_lh, 2)
    assert r2[1] is None

    r2bis = spin_permutations(pts_lh, feats_lh, n_rep=10, random_state=0)
    assert np.all(r2[0] == r2bis)

    r1 = sp.randomize(feats_lh[:, 0], x_rh=feats_rh[:, 0])
    assert r1[0].shape == (10, n_pts_lh)
    assert r1[1].shape == (10, n_pts_rh)

    r1bis = spin_permutations({
        'lh': pts_lh,
        'rh': pts_rh
    }, {
        'lh': feats_lh[:, 0],
        'rh': feats_rh[:, 0]
    },
                              n_rep=10,
                              random_state=0)
    assert np.all(r1[0] == r1bis[0])
    assert np.all(r1[1] == r1bis[1])

    r2 = sp.randomize(feats_lh, x_rh=feats_rh)
    assert r2[0].shape == (10, n_pts_lh, 2)
    assert r2[1].shape == (10, n_pts_rh, 2)

    r2bis = spin_permutations({
        'lh': pts_lh,
        'rh': pts_rh
    }, {
        'lh': feats_lh,
        'rh': feats_rh
    },
                              n_rep=10,
                              random_state=0)
    assert np.all(r2[0] == r2bis[0])
    assert np.all(r2[1] == r2bis[1])