def test_correct_mesh_orientation():
    sphere_small = ellipsoid(1, 1, 1, levelset=True)

    # Mesh with incorrectly oriented faces which was previously returned from
    # `marching_cubes`, before it guaranteed correct mesh orientation
    verts = np.array([[1., 2., 2.],
                      [2., 2., 1.],
                      [2., 1., 2.],
                      [2., 2., 3.],
                      [2., 3., 2.],
                      [3., 2., 2.]])

    faces = np.array([[0, 1, 2],
                      [2, 0, 3],
                      [1, 0, 4],
                      [4, 0, 3],
                      [1, 2, 5],
                      [2, 3, 5],
                      [1, 4, 5],
                      [5, 4, 3]])

    # Correct mesh orientation - descent
    corrected_faces1 = correct_mesh_orientation(sphere_small, verts, faces,
                                                gradient_direction='descent')
    corrected_faces2 = correct_mesh_orientation(sphere_small, verts, faces,
                                                gradient_direction='ascent')

    # Ensure ascent is opposite of descent for all faces
    assert_array_equal(corrected_faces1, corrected_faces2[:, ::-1])

    # Ensure correct faces have been reversed: 1, 4, and 5
    idx = [1, 4, 5]
    expected = faces.copy()
    expected[idx] = expected[idx, ::-1]
    assert_array_equal(expected, corrected_faces1)
Ejemplo n.º 2
0
def labels2meshes_ski(
    labelimage,
    labels,
    spacing=[0, 0, 0],
    nvoxthr=0
):  # deprecated and underdeveloped: use vtk implementation instead
    """"""
    for label in labels:
        labelmask = labelimage == label
        if np.count_nonzero(labelmask) > nvoxthr:
            labelmask = binary_closing(labelmask)
            verts, faces = marching_cubes(labelmask, 0, spacing=spacing)
            faces = correct_mesh_orientation(labelmask,
                                             verts,
                                             faces,
                                             spacing=spacing,
                                             gradient_direction='descent')
            # Fancy indexing to define two vector arrays from triangle vertices
            actual_verts = verts[faces]
            a = actual_verts[:, 0, :] - actual_verts[:, 1, :]
            b = actual_verts[:, 0, :] - actual_verts[:, 2, :]
            # Find normal vectors for each face via cross product
            crosses = np.cross(a, b)
            normals = crosses / (np.sum(crosses**2, axis=1)**(0.5))[:,
                                                                    np.newaxis]
            ob = stl.Solid(name=label)
            for ii, _ in enumerate(faces):
                ob.add_facet(normals[ii], actual_verts[ii])
            with open(str(label) + ".stl",
                      'w') as f:  #with open("allobjects.stl", 'a') as f:
                ob.write_ascii(f)  #ob.write_binary(f)
                f.write("\n")
def get_surface(mask, spacing, level=0):
    '''Obtain faces describing a surface
    
    Parameters
    ----------
    mask : numpy ndarray
        3d array.
    spacing : tuple of numeric
        resolution of mask in microns.
    level : numeric, optional
        Value of the the isosurface to be approximated.
        
    Returns
    -------
    vertices : numpy ndarray
        num_vertices X num_dimensions. Corners of triangles that define faces.
    faces :  numpy ndarray of int
        num_faces X num_vertices_per_face. Values index into vertices.
    
    '''

    vertices, faces = marching_cubes_classic(mask,
                                             level=level,
                                             spacing=spacing)
    faces = correct_mesh_orientation(mask, vertices, faces, spacing=spacing)

    return vertices, faces
Ejemplo n.º 4
0
def test_correct_mesh_orientation():
    sphere_small = ellipsoid(1, 1, 1, levelset=True)
    verts, faces = marching_cubes(sphere_small, 0.)

    # Correct mesh orientation - descent
    corrected_faces1 = correct_mesh_orientation(sphere_small, verts, faces,
                                                gradient_direction='descent')
    corrected_faces2 = correct_mesh_orientation(sphere_small, verts, faces,
                                                gradient_direction='ascent')

    # Ensure ascent is opposite of descent for all faces
    np.testing.assert_array_equal(corrected_faces1, corrected_faces2[:, ::-1])

    # Ensure correct faces have been reversed: 1, 4, and 5
    idx = [1, 4, 5]
    expected = faces.copy()
    expected[idx] = expected[idx, ::-1]
    np.testing.assert_array_equal(expected, corrected_faces1)
Ejemplo n.º 5
0
def test_correct_mesh_orientation():
    sphere_small = ellipsoid(1, 1, 1, levelset=True)
    verts, faces = marching_cubes(sphere_small, 0.)

    # Correct mesh orientation - descent
    corrected_faces1 = correct_mesh_orientation(sphere_small,
                                                verts,
                                                faces,
                                                gradient_direction='descent')
    corrected_faces2 = correct_mesh_orientation(sphere_small,
                                                verts,
                                                faces,
                                                gradient_direction='ascent')

    # Ensure ascent is opposite of descent for all faces
    np.testing.assert_array_equal(corrected_faces1, corrected_faces2[:, ::-1])

    # Ensure correct faces have been reversed: 1, 4, and 5
    idx = [1, 4, 5]
    expected = faces.copy()
    expected[idx] = expected[idx, ::-1]
    np.testing.assert_array_equal(expected, corrected_faces1)
def create_surf(subject, subcortical_code, subcortical_name):
    aseg = nib.load(op.join(SUBJECTS_DIR, subject, 'mri',
                            'aseg.mgz')).get_data()
    t1_header = nib.load(op.join(SUBJECTS_DIR, subject, 'mri',
                                 'T1.mgz')).header
    aseg[aseg != subcortical_code] = 255
    aseg[aseg == subcortical_code] = 10
    aseg = np.array(aseg, dtype=np.float)
    aseg_smooth = scipy.ndimage.gaussian_filter(aseg, sigma=1)
    verts_vox, faces, _, _ = measure.marching_cubes(aseg_smooth, 100)
    # Doesn't seem to fix the normals directions that should be out...
    faces = measure.correct_mesh_orientation(aseg_smooth, verts_vox, faces)
    verts = utils.apply_trans(t1_header.get_vox2ras_tkr(), verts_vox)
    fol = utils.make_dir(op.join(MMVT_DIR, subject, 'subcortical_test'))
    ply_file_name = op.join(fol, '{}.ply'.format(subcortical_name))
    utils.write_ply_file(verts, faces, ply_file_name, False)
Ejemplo n.º 7
0
    def f(X, c, q):
        vert_list = []

        for j in range(N):
            print 'step {%d}' % j
            X = f_(X, c, q)

            R = np.sqrt(np.square(X[:,:,:,0]) + \
                        np.square(X[:,:,:,1]) + \
                        np.square(X[:,:,:,2]))

            B = 1.0 * (R < 2.0)

            verts, faces = measure.marching_cubes_classic(B, 0)

            for k in range(3):
                verts[:, k] = (bounds[k][1] - bounds[k][0]) * (
                    1.0 * verts[:, k]) / K + bounds[k][0]

            faces = measure.correct_mesh_orientation(B, verts, faces)

            vert_list.append(verts)

        return vert_list
Ejemplo n.º 8
0
 def march(volume, _):
     verts, faces, normals, values = marching_cubes_lewiner(volume, 1)
     faces = correct_mesh_orientation(volume, verts, faces)
     return verts, None, faces
Ejemplo n.º 9
0
label_im = f['default/CUTOUT'][z_start:z_end,y_start:y_end,x_start:x_end]
label_im = np.lib.pad(label_im.tolist(), ((1, 1), (1, 1), (1, 1)), 'constant')
# label_im, nb_labels = ndimage.label(f['default/CUTOUT'][z_start:z_end,y_start:y_end,x_start:x_end])
# label_im = measure.label(f['default/CUTOUT'][z_start:z_end,y_start:y_end,x_start:x_end])

labels = np.unique(label_im)
labels = np.delete(labels,0)
# nb_labels = len(labels)

spacing = (0.03, 0.006, 0.006)
xyzOffset = (1100, 8000, 5000)

for label in labels:
    labelmask = label_im == label
    verts, faces = measure.marching_cubes(labelmask, 0, spacing=spacing)
    faces = measure.correct_mesh_orientation(labelmask, verts, faces, spacing=spacing, gradient_direction='descent')
    # from scikit_image correct_mesh_orientation
    # Fancy indexing to define two vector arrays from triangle vertices
    actual_verts = verts[faces]
    a = actual_verts[:, 0, :] - actual_verts[:, 1, :]
    b = actual_verts[:, 0, :] - actual_verts[:, 2, :]
    # Find normal vectors for each face via cross product
    crosses = np.cross(a, b)
    normals = crosses / (np.sum(crosses ** 2, axis=1) ** (0.5))[:, np.newaxis]
    ob = stl.Solid(label)
    for ii,face in enumerate(faces):
        ob.add_facet(normals[ii], actual_verts[ii])
    with open(str(label) + ".stl", 'w') as f:  #with open("allobjects.stl", 'a') as f:
        ob.write_binary(f)
        f.write("\n");
Ejemplo n.º 10
0
def mandelbulb(K=200, p=8.0, N=10, extreme=1.5, optimize=False):
    M = T.ftensor4()
    C = T.ftensor4()
    n = T.fscalar()

    def step(X):
        r = T.sqrt(
            T.square(X[:, :, :, 0]) + T.square(X[:, :, :, 1]) +
            T.square(X[:, :, :, 2]))
        phi = T.arctan2(X[:, :, :, 1], X[:, :, :, 0])
        theta = T.arctan2(
            T.sqrt(T.square(X[:, :, :, 0]) + T.square(X[:, :, :, 1])),
            X[:, :, :, 2])

        X_ = T.stack((T.pow(r, n) * T.sin(n * theta) * T.cos(n * phi),
                      T.pow(r, n) * T.sin(n * theta) * T.sin(n * theta),
                      T.pow(r, n) * T.cos(n * theta)),
                     axis=-1)

        return X_ + C

    if optimize:
        result, _ = theano.scan(fn=step, outputs_info=M, n_steps=N)

        f = theano.function([M, C, n], result, allow_input_downcast=True)
    else:
        f_ = theano.function([M, C, n], step(M), allow_input_downcast=True)

        def f(X, c, q):
            for j in range(N):
                print 'step {%d}' % j
                X = f_(X, c, q)
            return np.array(X)

    x, y, z = np.meshgrid(np.linspace(-extreme, extreme, K),
                          np.linspace(-extreme, extreme, K),
                          np.linspace(-extreme, extreme, K))

    X = np.stack((x, y, z), axis=-1)

    if optimize:
        x_n = f(np.zeros((K, K, K, 3)), X, p)[-1]
    else:
        x_n = f(np.zeros((K, K, K, 3)), X, p)

    r_n = np.sqrt(np.square(x_n[:,:,:,0]) + \
                  np.square(x_n[:,:,:,1]) + \
                  np.square(x_n[:,:,:,2]))

    b_ = 1.0 * (r_n < 2.0)

    import open3d
    from scipy.misc import imresize
    from skimage import measure

    verts, faces = measure.marching_cubes_classic(b_, 0)
    faces = measure.correct_mesh_orientation(b_, verts, faces)
    verts = 2.0 * extreme * (np.array(verts, dtype=np.float32) / K) - extreme

    pcd = open3d.PointCloud()
    pcd.points = open3d.Vector3dVector(verts)
    open3d.estimate_normals(pcd,
                            search_param=open3d.KDTreeSearchParamHybrid(
                                radius=0.1, max_nn=30))

    global i, images
    i = 0
    images = []

    def custom_draw_geometry_with_rotation(pcd):
        def rotate_view(vis):
            ctr = vis.get_view_control()
            ctr.rotate(10.0, 0.0)

            global i, images
            i += 1
            print i % 210, i // 210

            image = np.asarray(vis.capture_screen_float_buffer())
            image = np.array(255 * image, dtype=np.uint8)
            image = imresize(image, 0.25)

            if (i // 210 == 0):
                images.append(image)

            return False

        open3d.draw_geometries_with_animation_callback([pcd], rotate_view)

    custom_draw_geometry_with_rotation(pcd)

    gif_name = 'Mandelbulb_%d_%d.gif' % (int(p), int(N))
    output_file = os.path.join(__file__.split('.')[0], gif_name)
    imageio.mimsave(output_file, images, duration=0.05)
Ejemplo n.º 11
0
def get_surface(mask, spacing):
    vertices, faces = marching_cubes_classic(mask, level=0, spacing=spacing)
    faces = correct_mesh_orientation(mask, vertices, faces, spacing)
    return vertices, faces
Ejemplo n.º 12
0
def get_isosurface(coordinates,
                   function,
                   isolevel=0.03,
                   color=(255, 0, 0, 255),
                   extents=(5, 5, 5),
                   resolution=100):
    '''Add an isosurface to the current scene.

    Parameters
    ----------
    coordinates : numpy.array
        the coordinates of the system
    function : func
        A function that takes x, y, z coordinates as input and is broadcastable 
        using numpy. Typically simple functions that involve standard 
        arithmetic operations and functions such as ``x**2 + y**2 + z**2`` or 
        ``np.exp(x**2 + y**2 + z**2)`` will work. If not sure, you can first
        pass the function through ``numpy.vectorize``.
        Example: ``mv.add_isosurface(np.vectorize(f))``
    isolevel : float
        The value for which the function should be constant.
    color : (int, int, int, int)
        The color given in RGBA format
    extents : (float, float, float)
        +/- x,y,z to extend the molecule geometrty when constructing the surface
    resolution : int
        The number of grid point to use for the surface. An high value will 
        give better quality but lower performance.

    '''
    if coordinates.shape[0] == 0:
        return False

    # We want to make a container that contains the whole molecule
    # and surface
    coordinates = coordinates.astype('float32')
    area_min = coordinates.min(axis=0) - np.array(extents)
    area_max = coordinates.max(axis=0) + np.array(extents)

    x = np.linspace(area_min[0], area_max[0], resolution)
    y = np.linspace(area_min[1], area_max[1], resolution)
    z = np.linspace(area_min[2], area_max[2], resolution)

    xv, yv, zv = np.meshgrid(x, y, z)
    spacing = np.array((area_max - area_min) / resolution)

    if isolevel >= 0:
        sign = 1
    else:
        sign = -1

    volume = sign * function(xv, yv, zv)
    if volume.flatten().min() <= sign * isolevel and volume.flatten().max(
    ) >= sign * isolevel:
        verts, faces = marching_cubes(volume, sign * isolevel)
        #don't know if i need this
        faces = correct_mesh_orientation(volume,
                                         verts,
                                         faces,
                                         gradient_direction='descent')
        verts = area_min + spacing / 2 + verts * spacing

        # TODO for some reason need to invert, but no one knows what the problem is
        verts[:, [0, 1]] = verts[:, [1, 0]]
        # TODO and its messing up the normals so using double facing
        #(kind of works if transparent)
        opposite_faces = faces.copy()
        opposite_faces[:, [0, 1]] = opposite_faces[:, [1, 0]]
        faces = np.concatenate([faces, opposite_faces])

    else:
        return

    normals = calc_normals(verts, faces)

    verts = verts[faces.flatten()]
    normals = normals[faces.flatten()]

    colors = np.array([color for _ in range(verts.shape[0])])

    return verts.astype('float32'), normals.astype('float32'), colors