コード例 #1
0
def find_rigid_transform(a, b, visualize=False):
    """
    Args:
        a: a 3xN array of vertex locations
        b: a 3xN array of vertex locations

    Returns: (R,T) such that R.dot(a)+T ~= b
    Based on Arun et al, "Least-squares fitting of two 3-D point sets," 1987.
    See also Eggert et al, "Estimating 3-D rigid body transformations: a
    comparison of four major algorithms," 1997.
    """
    import numpy as np
    import scipy.linalg

    if a.shape[0] != 3:
        if a.shape[1] == 3:
            a = a.T
    if b.shape[0] != 3:
        if b.shape[1] == 3:
            b = b.T
    assert a.shape[0] == 3
    assert b.shape[0] == 3

    a_mean = np.mean(a, axis=1)
    b_mean = np.mean(b, axis=1)
    a_centered = a - a_mean.reshape(-1, 1)
    b_centered = b - b_mean.reshape(-1, 1)

    c = a_centered.dot(b_centered.T)
    u, s, v = np.linalg.svd(c, full_matrices=False)
    v = v.T
    R = v.dot(u.T)

    if scipy.linalg.det(R) < 0:
        if np.any(s == 0
                  ):  # This is only valid in the noiseless case; see the paper
            v[:, 2] = -v[:, 2]
            R = v.dot(u.T)
        else:
            raise ValueError(
                "find_rigid_transform found a reflection that it cannot recover from. Try RANSAC or something..."
            )

    T = (b_mean - R.dot(a_mean)).reshape(-1, 1)

    if visualize != False:
        from lace.mesh import Mesh
        from lace.meshviewer import MeshViewer

        mv = MeshViewer() if visualize is True else visualize
        a_T = R.dot(a) + T
        mv.set_dynamic_meshes([
            Mesh(v=a.T, f=[]).set_vertex_colors("red"),
            Mesh(v=b.T, f=[]).set_vertex_colors("green"),
            Mesh(v=a_T.T, f=[]).set_vertex_colors("orange"),
        ])

    return R, T
コード例 #2
0
 def test_mesh_from_mesh(self):
     m = Mesh(
         v=np.random.randn(10, 3),
         f=np.random.randn(10, 3),
         vc=np.random.randn(10, 3),
         vn=np.random.randn(10, 3),
     )
     m2 = Mesh(m)
     np.testing.assert_array_equal(m.v, m2.v)
     np.testing.assert_array_equal(m.f, m2.f)
     np.testing.assert_array_equal(m.vc, m2.vc)
     np.testing.assert_array_equal(m.vn, m2.vn)
     self.assertNotEqual(id(m.v), id(m2.v))
コード例 #3
0
 def test_flip_faces(self):
     raw_box = Mesh(vc('/unittest/serialization/obj/test_box_simple.obj'))
     box = Mesh(v=raw_box.v, f=raw_box.f)
     box.reset_normals()
     original_vn = box.vn.copy()
     original_f = box.f.copy()
     box.flip_faces()
     box.reset_normals()
     self.assertEqual(box.f.shape, original_f.shape)
     for face, orig_face in zip(box.f, original_f):
         self.assertNotEqual(list(face), list(orig_face))
         self.assertEqual(set(face), set(orig_face))
     np.testing.assert_array_almost_equal(box.vn, np.negative(original_vn))
コード例 #4
0
 def load_texture(self, texture_version):
     '''
     Expect a texture version number as an integer, load the texture version from /is/ps/shared/data/body/template/texture_coordinates/.
     Currently there are versions [0, 1, 2, 3] availiable.
     '''
     import numpy as np
     lowres_tex_template = 's3://bodylabs-korper-assets/is/ps/shared/data/body/template/texture_coordinates/textured_template_low_v%d.obj' % texture_version
     highres_tex_template = 's3://bodylabs-korper-assets/is/ps/shared/data/body/template/texture_coordinates/textured_template_high_v%d.obj' % texture_version
     from lace.mesh import Mesh
     from lace.cache import sc
     mesh_with_texture = Mesh(filename=sc(lowres_tex_template))
     if not np.all(mesh_with_texture.f.shape == self.f.shape):
         mesh_with_texture = Mesh(filename=sc(highres_tex_template))
     self.transfer_texture(mesh_with_texture)
コード例 #5
0
    def test_concatenate_vertices_only(self):
        v1 = np.array([
            [0., 0., 0.],
            [5., 5., 5.],
            [5., 0., 5.],
        ])
        m1 = Mesh(v=v1)

        v2 = np.array([
            [0., 0., 0.],
            [5., -5., 5.],
            [5., 0., 5.],
        ])
        m2 = Mesh(v=v2)

        self.assertEqual(Mesh.concatenate(m1, m2).v.shape[0], 6)
コード例 #6
0
 def test_explicit_landmarks_as_dict_of_indexes(self):
     m = Mesh(filename=self.template_fname, landmarks={'foo': 0, 'bar': 7})
     self.assertEqual(m.landm, {'foo': 0, 'bar': 7})
     self.assertDictOfArraysAlmostEqual(m.landm_xyz, {
         'foo': self.template.v[0],
         'bar': self.template.v[7]
     })
コード例 #7
0
 def test_writing_binary_ply_locally_using_serializer(self):
     local_file = os.path.join(
         self.tmp_dir,
         "test_writing_binary_ply_locally_using_serializer.ply")
     m = Mesh(filename=self.test_ply_path)
     ply.dump(m, local_file)
     self.assertFilesEqual(local_file, self.test_bin_ply_path)
コード例 #8
0
 def test_writing_ascii_ply_locally_using_mesh_write_ply(self):
     local_file = os.path.join(
         self.tmp_dir,
         "test_writing_ascii_ply_locally_using_mesh_write_ply.ply")
     m = Mesh(filename=self.test_ply_path)
     m.write_ply(local_file, ascii=True)
     self.assertFilesEqual(local_file, self.test_ply_path)
コード例 #9
0
 def test_scale_vertex_colors(self):
     m = Mesh(v=np.ones((4, 3)), vc='red')
     m.scale_vertex_colors(np.array([2.0, 1.0, 0.5, 0.0]))
     expected_c = np.array([[1.0, 0.0, 0.0], [0.5, 0.0, 0.0],
                            [0.25, 0.0, 0.0], [0.0, 0.0, 0.0]])
     np.testing.assert_array_equal(expected_c, m.vc)
     self.assertEqual(m.vc.dtype, np.float64)
コード例 #10
0
 def test_explicit_landmarks_as_array_of_indexes(self):
     m = Mesh(filename=self.template_fname, landmarks=np.array([0, 7]))
     self.assertEqual(m.landm, {'0': 0, '1': 7})
     self.assertDictOfArraysAlmostEqual(m.landm_xyz, {
         '0': self.template.v[0],
         '1': self.template.v[7]
     })
コード例 #11
0
 def test_yml_points(self):
     from baiji.serialization import yaml
     path = self.make_point_data(yaml, '.yml')
     m = Mesh(filename=self.template_fname, landmarks=path)
     self.assertEqual(m.landm, self.template.landm)
     self.assertDictOfArraysAlmostEqual(m.landm_xyz,
                                        self.template.landm_xyz)
コード例 #12
0
 def test_json_index(self):
     from baiji.serialization import json
     path = self.make_index_data(json, '.json')
     m = Mesh(filename=self.template_fname, landmarks=path)
     self.assertEqual(m.landm, self.template.landm)
     self.assertDictOfArraysAlmostEqual(
         m.landm_xyz, self.template_without_regressors.landm_xyz)
コード例 #13
0
def create_rectangular_prism(origin, size):
    '''
    Return a Mesh which is an axis-aligned rectangular prism. One vertex is
    `origin`; the diametrically opposite vertex is `origin + size`.

    size: 3x1 array.

    '''
    from lace.topology import quads_to_tris

    lower_base_plane = np.array([
        # Lower base plane
        origin,
        origin + np.array([size[0], 0, 0]),
        origin + np.array([size[0], 0, size[2]]),
        origin + np.array([0, 0, size[2]]),
    ])
    upper_base_plane = lower_base_plane + np.array([0, size[1], 0])

    vertices = np.vstack([lower_base_plane, upper_base_plane])

    faces = quads_to_tris(np.array([
        [0, 1, 2, 3],  # lower base (-y)
        [7, 6, 5, 4],  # upper base (+y)
        [4, 5, 1, 0],  # +z face
        [5, 6, 2, 1],  # +x face
        [6, 7, 3, 2],  # -z face
        [3, 7, 4, 0],  # -x face
    ]))

    return Mesh(v=vertices, f=faces)
コード例 #14
0
    def test_writing_empty_mesh(self):
        m = Mesh()

        local_file = os.path.join(self.tmp_dir, 'test_writing_empty_mesh.obj')
        obj.dump(m, local_file)

        self.assertEqual(os.stat(local_file).st_size, 0)
コード例 #15
0
 def test_writing_obj_locally_using_serializer(self):
     local_file = os.path.join(
         self.tmp_dir,
         "test_writing_ascii_obj_locally_using_serializer.obj")
     m = Mesh(filename=self.test_obj_path)
     obj.dump(m, local_file)
     self.assertFilesEqual(local_file, self.test_obj_path)
コード例 #16
0
 def test_writing_obj_locally_using_mesh_write_obj(self):
     local_file = os.path.join(
         self.tmp_dir,
         "test_writing_ascii_obj_locally_using_mesh_write_ply.obj")
     m = Mesh(filename=self.test_obj_path)
     m.write_obj(local_file)
     self.assertFilesEqual(local_file, self.test_obj_path)
コード例 #17
0
ファイル: plane.py プロジェクト: metabolize-forks/blmath
def main():
    import argparse
    from lace.mesh import Mesh

    parser = argparse.ArgumentParser()
    parser.add_argument('-p', '--path', help='filepath to mesh', required=True)
    parser.add_argument('-c', '--cloud', help='display point cloud', required=False, default=False, action='store_true')
    parser.add_argument('-d', '--direction', help='direction of connected component',
                        choices=['N', 'S', 'E', 'W'], default=None, required=False)
    args = parser.parse_args()

    path_to_mesh = args.path
    mesh = Mesh(filename=path_to_mesh, vc='SteelBlue')

    point_on_plane = np.array([0., 1., 0.])

    n1 = np.array([0., 1., 0.])
    p1 = Plane(point_on_plane, n1)

    n2 = np.array([1., 0., 0.])
    p2 = Plane(point_on_plane, n2)

    n3 = np.array([1., 1., 0.])
    n3 /= np.linalg.norm(n3)
    p3 = Plane(point_on_plane, n3)

    n4 = np.array([-1., 1., 0.])
    n4 /= np.linalg.norm(n4)
    p4 = Plane(point_on_plane, n4)

    dirmap = {
        'N': [0., +100., 0.],
        'S': [0., -100., 0.],
        'E': [+100., 0., 0.],
        'W': [-100., 0., 0.],
        None: None,
    }

    neighborhood = dirmap[args.direction]
    if neighborhood != None:
        neighborhood = np.array([neighborhood])

    xs1 = p1.mesh_xsection(mesh, neighborhood=neighborhood)
    xs2 = p2.mesh_xsection(mesh, neighborhood=neighborhood)
    xs3 = p3.mesh_xsection(mesh, neighborhood=neighborhood)
    xs4 = p4.mesh_xsection(mesh, neighborhood=neighborhood)

    lines = [
        polyline.as_lines()
        for polyline in [xs1, xs2, xs3, xs4]
    ]

    if args.cloud:
        mesh.f = []

    from lace.meshviewer import MeshViewer
    mv = MeshViewer(keepalive=True)
    mv.set_dynamic_meshes([mesh], blocking=True)
    mv.set_dynamic_lines(lines)
コード例 #18
0
 def test_explicit_landmarks_as_array_of_points(self):
     m = Mesh(filename=self.template_fname,
              landmarks=[self.template.v[0], self.template.v[7]])
     self.assertEqual(m.landm, {'0': 0, '1': 7})
     self.assertDictOfArraysAlmostEqual(m.landm_xyz, {
         '0': self.template.v[0],
         '1': self.template.v[7]
     })
コード例 #19
0
    def test_recenter_over_floor_raises_expected_on_empty_mesh(self):
        mesh = Mesh()

        with self.assertRaises(ValueError) as ctx:
            mesh.recenter_over_floor()

        self.assertEqual(str(ctx.exception),
                         'Mesh has no vertices; centroid is not defined')
コード例 #20
0
    def test_bounding_box_is_undefined_on_empty_mesh(self):
        mesh = Mesh()

        with self.assertRaises(ValueError) as ctx:
            mesh.bounding_box  # pylint: disable=pointless-statement

        self.assertEqual(str(ctx.exception),
                         'Mesh has no vertices; bounding box is not defined')
コード例 #21
0
def load_front_torso_mesh():
    from lace.mesh import Mesh

    mesh = Mesh(filename="/Users/pnm/code/tape/examples/anonymized_female.obj")
    mesh.cut_across_axis(1, minval=100.0, maxval=120.0)
    mesh.cut_across_axis(0, minval=-19.0, maxval=19.0)
    mesh.cut_across_axis(2, minval=0)
    return mesh
コード例 #22
0
 def test_loads_from_local_path_using_constructor_with_landmarks(self):
     skip_on_import_error('lace-search')
     m = Mesh(filename=self.test_obj_with_landmarks_path,
              ppfilename=self.test_pp_path)
     self.assertTrue((m.v == self.truth['box_v']).all())
     self.assertTrue((m.f == self.truth['box_f']).all())
     self.assertEqual(m.landm, self.truth['landm'])
     self.assertDictOfArraysEqual(m.landm_xyz, self.truth['landm_xyz'])
     self.assertDictOfArraysEqual(m.segm, self.truth['box_segm'])
コード例 #23
0
def _load(f, existing_mesh=None):
    import numpy as np
    from lace.mesh import Mesh
    v = np.loadtxt(f.name)
    if existing_mesh is None:
        return Mesh(v=v)
    else:
        existing_mesh.v = v
        return existing_mesh
コード例 #24
0
def _load(f, existing_mesh=None):
    from lace.mesh import Mesh
    parser = BSFParser(f)
    if existing_mesh is None:
        return Mesh(v=parser.v, f=parser.f, vc=parser.vc)
    else:
        existing_mesh.v = parser.v
        existing_mesh.f = parser.f
        existing_mesh.vc = parser.vc
        return existing_mesh
コード例 #25
0
def create_horizontal_plane():
    '''
    Creates a horizontal plane.
    '''
    v = np.array([
        [1., 0., 0.],
        [-1., 0., 0.],
        [0., 0., 1.],
        [0., 0., -1.]
    ])
    f = [[0, 1, 2], [3, 1, 0]]
    return Mesh(v=v, f=f)
コード例 #26
0
 def test_estimate_vertex_normals(self):
     # normals of a sphere should be scaled versions of the vertices
     test_sphere_path = sc(
         's3://bodylabs-korper-assets/is/ps/shared/data/body/'
         'korper_testdata/sphere.ply'
     )
     m = Mesh(filename=test_sphere_path)
     m.v -= np.mean(m.v, axis=0)
     rad = np.linalg.norm(m.v[0])
     m.estimate_vertex_normals()
     mse = np.mean(np.sqrt(np.sum((m.vn - m.v/rad)**2, axis=1)))
     self.assertTrue(mse < 0.05)
コード例 #27
0
def _load(f, mesh=None):
    import numpy as np
    from lace.mesh import Mesh
    if not mesh:
        mesh = Mesh()
    faces = []
    facenormals = []
    verts = []

    head = f.readline().strip()
    if head.startswith("solid"):  # ascii STL format
        #name = head[6:]
        current_face = []
        for line in f:
            line = line.split()
            if line[0] == "endsolid":
                break
            elif line[0] == "facet":
                current_face = []
                if line[1] == "normal":
                    try:
                        facenormals.append([float(x) for x in line[2:]])
                    except:  # pylint: disable=bare-except
                        facenormals.append([np.nan, np.nan, np.nan])
                else:
                    facenormals.append([np.nan, np.nan, np.nan])
            elif line[0] == "endfacet":
                faces.append(current_face)
                current_face = []
            elif line[0:2] == ["outer", "loop"]:
                pass
            elif line[0] == "endloop":
                pass
            elif line[0] == "vertex":
                current_face.append(len(verts))
                try:
                    verts.append([float(x) for x in line[1:]])
                except:  # pylint: disable=bare-except
                    verts.append([np.nan, np.nan, np.nan])
            else:
                raise ValueError(
                    "Badly formatted STL file. I don't understand the line %s"
                    % line)
    else:
        raise Exception(
            "Looks like this is a binary STL file; you're going to have to implement that"
        )
        # format docs are here: http://en.wikipedia.org/wiki/STL_(file_format)

    mesh.v = np.array(verts, dtype=np.float64).copy()
    mesh.f = np.array(faces, dtype=np.uint32).copy()
    mesh.fn = np.array(facenormals, dtype=np.float64).copy()
    return mesh
コード例 #28
0
 def setUp(self):
     skip_on_import_error('lace-search')
     self.scan_fname = sc(
         's3://bodylabs-korper-assets/is/ps/shared/data/body/caesar/RawScans/csr0001a.ply'
     )
     self.scan_lmrk = sc(
         's3://bodylabs-korper-assets/is/ps/shared/data/body/caesar/Landmarks/csr0001a.lmrk'
     )
     self.template_fname = sc(
         's3://bodylabs-korper-assets/is/ps/shared/data/body/template/textured_mean_scape_female.obj'
     )
     self.template_pp = sc(
         's3://bodylabs-korper-assets/is/ps/shared/data/body/template/template_caesar_picked_points.pp'
     )
     self.scan = Mesh(filename=self.scan_fname, lmrkfilename=self.scan_lmrk)
     self.template = Mesh(filename=self.template_fname,
                          ppfilename=self.template_pp)
     self.template_without_regressors = Mesh(filename=self.template_fname,
                                             ppfilename=self.template_pp)
     self.template_without_regressors.landm_regressors = {}
     super(TestLandmarks, self).setUp()
コード例 #29
0
    def test_concatenate_partial_missing_vc(self):
        v1 = np.array([
            [0., 0., 0.],
            [5., 5., 5.],
            [5., 0., 5.],
        ])
        vc1 = np.array([
            [1., 0., 0.],
            [0., 1., 0.],
            [0., 0., 1.],
        ])
        m1 = Mesh(v=v1, vc=vc1)

        v2 = np.array([
            [0., 0., 0.],
            [5., -5., 5.],
            [5., 0., 5.],
        ])
        m2 = Mesh(v=v2)

        with self.assertRaisesRegexp(ValueError, 'all args or none.'):
            Mesh.concatenate(m1, m2)
コード例 #30
0
 def test_explicit_landmark_indexes_from_another_mesh(self):
     m = Mesh(filename=self.template_fname, landmarks=self.template.landm)
     np.testing.assert_array_equal(self.template.v, m.v)
     self.assertEqual(m.landm, self.template.landm)
     # The following are _not_ expected to pass, as the initial load of template is
     # loading points that are slightly off from the actual vertex locations,
     # so the regressors are going to be subtly different.
     #   self.assertDictOfArraysAlmostEqual(m.landm_xyz, self.template.landm_xyz)
     #   self.assertDictOfArraysAlmostEqual(m.landm_regressors, self.template.landm_regressors)
     # But if we disable the regressors and just use the landmark verts:
     m.landm_regressors = {}
     self.assertDictOfArraysAlmostEqual(
         m.landm_xyz, self.template_without_regressors.landm_xyz)