def test_tiny_array(): """ Tiny arrays trigger an exception in skimage, so they must be padded first. Verify that they can be meshified (after padding). """ one_voxel = np.ones((1, 1, 1), np.uint8) _mesh = Mesh.from_binary_vol(one_voxel, [(0, 0, 0), (1, 1, 1)], method='skimage') _mesh = Mesh.from_binary_vol(one_voxel, [(0, 0, 0), (1, 1, 1)], method='ilastik')
def test_trim(binary_vol_input): mesh = Mesh.from_binary_vol(binary_vol_input[0]) mesh.fragment_origin = np.array([25, 25, 25]) mesh.fragment_shape = np.array([50, 50, 50]) num_faces = len(mesh.faces) mesh.trim() assert len(mesh.faces) < num_faces mesh = Mesh.from_binary_vol(binary_vol_input[0]) mesh.fragment_origin = np.array([125, 125, 125]) mesh.fragment_shape = np.array([50, 50, 50]) mesh.trim() assert len(mesh.faces) == 0
def test_basic(self): # Pretend the data was downsampled and translated, # and therefore the mesh requires upscaling and translation data_box = np.array(self.data_box) data_box += 1000 nonzero_box = self.nonzero_box + 1000 FACTOR = 2 data_box *= FACTOR nonzero_box *= FACTOR mesh = Mesh.from_binary_vol(self.binary_vol, data_box) assert mesh.vertices_zyx.dtype == np.float32 mesh_box = np.array( [mesh.vertices_zyx.min(axis=0), mesh.vertices_zyx.max(axis=0)]) assert (mesh_box == nonzero_box ).all(), f"{mesh_box.tolist()} != {nonzero_box.tolist()}" serialized = mesh.serialize(fmt='obj') unserialized = mesh.from_buffer(serialized, 'obj') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx) serialized = mesh.serialize(fmt='drc') unserialized = mesh.from_buffer(serialized, 'drc') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx) serialized = mesh.serialize(fmt='ngmesh') unserialized = mesh.from_buffer(serialized, 'ngmesh') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx)
def test_compress(binary_vol_input): binary_vol, data_box, _nonzero_box = binary_vol_input mesh_orig = Mesh.from_binary_vol(binary_vol, data_box) uncompressed_size = mesh_orig.normals_zyx.nbytes + mesh_orig.vertices_zyx.nbytes + mesh_orig.faces.nbytes mesh = copy.deepcopy(mesh_orig) size = mesh.compress('lz4') assert size < uncompressed_size assert (mesh.faces == mesh_orig.faces).all() assert (mesh.vertices_zyx == mesh_orig.vertices_zyx).all() assert (mesh.normals_zyx == mesh_orig.normals_zyx).all() # Draco is lossy, so we can't compare exactly. # Just make sure the arrays are at least of the correct shape. size = mesh.compress('draco') assert size < uncompressed_size assert (mesh.faces.shape == mesh_orig.faces.shape) assert (mesh.vertices_zyx.shape == mesh_orig.vertices_zyx.shape) assert (mesh.normals_zyx.shape == mesh_orig.normals_zyx.shape) mesh = copy.deepcopy(mesh_orig) mesh.fragment_shape = np.asarray(data_box[1]) mesh.fragment_origin = np.asarray(data_box[0]) size = mesh.compress('custom_draco') assert size < uncompressed_size assert (mesh.faces.shape == mesh_orig.faces.shape) assert (mesh.vertices_zyx.shape == mesh_orig.vertices_zyx.shape) assert (mesh.normals_zyx.shape == mesh_orig.normals_zyx.shape)
def test_smoothing_X(self): """ This just exercises the code on our standard X-shaped test object, but doesn't verify the results. Uncomment the serialize() lines to inspect the effects manually. """ mesh = Mesh.from_binary_vol(self.binary_vol, self.data_box) #mesh.serialize('/tmp/x-unsmoothed.obj') mesh.simplify(0.2) mesh.laplacian_smooth(5) #mesh.serialize('/tmp/x-simplified-smoothed.obj') mesh = Mesh.from_binary_vol(self.binary_vol, self.data_box) mesh.laplacian_smooth(5) mesh.simplify(0.2)
def test_pickling(self): mesh = Mesh.from_binary_vol(self.binary_vol) pickled = pickle.dumps(mesh) unpickled = pickle.loads(pickled) # It's not easy to verify that unpickled is identical, # since draco may re-order vertices and faces. # The validity of our draco encoding functions is tested elsewhere, # so here we just check for vertex/face count assert len(mesh.vertices_zyx) == len(unpickled.vertices_zyx) assert len(mesh.faces) == len(unpickled.faces)
def test_basic(binary_vol_input): binary_vol, data_box, nonzero_box = binary_vol_input # Pretend the data was downsampled and translated, # and therefore the mesh requires upscaling and translation data_box = np.array(data_box) data_box += 1000 nonzero_box = nonzero_box + 1000 FACTOR = 2 data_box *= FACTOR nonzero_box *= FACTOR mesh = Mesh.from_binary_vol(binary_vol, data_box) assert mesh.vertices_zyx.dtype == np.float32 mesh_box = np.array( [mesh.vertices_zyx.min(axis=0), mesh.vertices_zyx.max(axis=0)]) assert (mesh_box == nonzero_box ).all(), f"{mesh_box.tolist()} != {nonzero_box.tolist()}" serialized = mesh.serialize(fmt='obj') unserialized = mesh.from_buffer(serialized, 'obj') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx) serialized = mesh.serialize(fmt='drc') unserialized = mesh.from_buffer(serialized, 'drc') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx) mesh = Mesh.from_binary_vol(binary_vol, data_box, fragment_shape=np.asarray(data_box[1]), fragment_origin=np.asarray(data_box[0])) serialized = mesh.serialize(fmt='custom_drc') unserialized = mesh.from_buffer(serialized, 'drc') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx) serialized = mesh.serialize(fmt='ngmesh') unserialized = mesh.from_buffer(serialized, 'ngmesh') assert len(unserialized.vertices_zyx) == len(mesh.vertices_zyx)
def test_solid_array(self): """ Solid volumes can't be meshified. An empty mesh is returned instead. """ box = [(0, 0, 0), (3, 3, 3)] solid_volume = np.ones((3, 3, 3), np.uint8) mesh = Mesh.from_binary_vol(solid_volume, box) assert mesh.vertices_zyx.shape == (0, 3) assert mesh.faces.shape == (0, 3) assert mesh.normals_zyx.shape == (0, 3) assert (mesh.box == box).all()
def test_normals_implementations(binary_vol_input): """ Compare the numpy-based and numba-based normals computation implementations. """ binary_vol, data_box, _nonzero_box = binary_vol_input try: from vol2mesh.normals import (compute_face_normals, compute_face_normals_numba, compute_face_normals_numpy, compute_vertex_normals, compute_vertex_normals_numba, compute_vertex_normals_numpy) except ImportError: pytest.skip("numba not installed") mesh = Mesh.from_binary_vol(binary_vol, data_box) face_normals_default = compute_face_normals(mesh.vertices_zyx, mesh.faces, normalize=True) face_normals_numba = compute_face_normals_numba(mesh.vertices_zyx, mesh.faces, normalize=True) face_normals_numpy = compute_face_normals_numpy(mesh.vertices_zyx, mesh.faces, normalize=True) assert np.allclose(face_normals_numba, face_normals_default) assert np.allclose(face_normals_numba, face_normals_numpy) vertex_normals_default = compute_vertex_normals( mesh.vertices_zyx, mesh.faces, weight_by_face_area=False, face_normals=face_normals_default) vertex_normals_numba = compute_vertex_normals_numba( mesh.vertices_zyx, mesh.faces, weight_by_face_area=False, face_normals=face_normals_numba) vertex_normals_numpy = compute_vertex_normals_numpy( mesh.vertices_zyx, mesh.faces, weight_by_face_area=False, face_normals=face_normals_numpy) assert np.allclose(vertex_normals_numba, vertex_normals_default) assert np.allclose(vertex_normals_numba, vertex_normals_numpy)
def test_compress(self): mesh_orig = Mesh.from_binary_vol(self.binary_vol, self.data_box) uncompressed_size = mesh_orig.normals_zyx.nbytes + mesh_orig.vertices_zyx.nbytes + mesh_orig.faces.nbytes mesh = copy.deepcopy(mesh_orig) size = mesh.compress('lz4') assert size < uncompressed_size assert (mesh.faces == mesh_orig.faces).all() assert (mesh.vertices_zyx == mesh_orig.vertices_zyx).all() assert (mesh.normals_zyx == mesh_orig.normals_zyx).all() # Draco is lossy, so we can't compare exactly. # Just make sure the arrays are at least of the correct shape. size = mesh.compress('draco') assert size < uncompressed_size assert (mesh.faces.shape == mesh_orig.faces.shape) assert (mesh.vertices_zyx.shape == mesh_orig.vertices_zyx.shape) assert (mesh.normals_zyx.shape == mesh_orig.normals_zyx.shape)
def test_normals_guarantees(self): """ Member functions have guarantees about whether normals are present or absent after the function runs. - simplify(): Always present afterwards - laplacian_smooth(): Always present afterwards - stitch_adjacent_faces(): Present afterwards IFF they were present before. """ data_box = np.array(self.data_box) FACTOR = 2 data_box *= FACTOR mesh_orig = Mesh.from_binary_vol(self.binary_vol, data_box) mesh = copy.deepcopy(mesh_orig) assert mesh.normals_zyx.shape[0] > 1 # Verify normals are always present after simplification, # Regardless of whether or not they were present before, # or if simplification was even performed. mesh.simplify(1.0) assert mesh.normals_zyx.shape[0] > 1 mesh.simplify(0.5) assert mesh.normals_zyx.shape[0] > 1 mesh.drop_normals() mesh.simplify(0.5) assert mesh.normals_zyx.shape[0] > 1 # Verify normals are always present after smoothing, # Regardless of whether or not they were present before, # or if smoothing was even performed. mesh = copy.deepcopy(mesh_orig) mesh.laplacian_smooth(0) assert mesh.normals_zyx.shape[0] > 1 mesh.laplacian_smooth(2) assert mesh.normals_zyx.shape[0] > 1 mesh.drop_normals() mesh.laplacian_smooth(2) assert mesh.normals_zyx.shape[0] > 1 # Verify that the presence or absence of normals is the SAME after stitching, # Whether or not stitching had any effect. # no stitching, keep normals mesh = copy.deepcopy(mesh_orig) stitching_performed = mesh.stitch_adjacent_faces() assert not stitching_performed assert mesh.normals_zyx.shape[0] > 1 # no stitching, no normals in the first place mesh.drop_normals() stitching_performed = mesh.stitch_adjacent_faces() assert not stitching_performed assert mesh.normals_zyx.shape[0] == 0 # stitching, generate normals mesh = copy.deepcopy(mesh_orig) duplicated_mesh = concatenate_meshes([mesh, mesh]) assert duplicated_mesh.normals_zyx.shape[0] > 1 stitching_performed = duplicated_mesh.stitch_adjacent_faces() assert stitching_performed assert duplicated_mesh.normals_zyx.shape[0] > 1 # stitching, no normals in the first place mesh = copy.deepcopy(mesh_orig) duplicated_mesh = concatenate_meshes([mesh, mesh]) duplicated_mesh.drop_normals() stitching_performed = duplicated_mesh.stitch_adjacent_faces() assert stitching_performed assert duplicated_mesh.normals_zyx.shape[0] == 0