예제 #1
0
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')
예제 #2
0
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
예제 #3
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)
예제 #4
0
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)
예제 #5
0
    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)
예제 #6
0
    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)
예제 #7
0
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)
예제 #8
0
    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()
예제 #9
0
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)
예제 #10
0
    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)
예제 #11
0
    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