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)
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
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 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)
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
def march(volume, _): verts, faces, normals, values = marching_cubes_lewiner(volume, 1) faces = correct_mesh_orientation(volume, verts, faces) return verts, None, faces
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");
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)
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
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