def test_reading_obj_with_mtl_from_sc_file(self, mock_sc, mock_s3_open): from baiji.pod.asset_cache import CacheFile sc_obj_with_texure = self.obj_with_texure.replace( "s3://bodylabs-korper-assets", '') sc_obj_with_texure_mtl = self.obj_with_texure_mtl.replace( "s3://bodylabs-korper-assets", '') sc_obj_with_texure_tex = self.obj_with_texure_tex.replace( "s3://bodylabs-korper-assets", '') bucket = "bodylabs-korper-assets" m = obj.load(sc(sc_obj_with_texure, bucket=bucket)) mock_sc.assert_has_calls([ mock.call(sc_obj_with_texure, bucket=bucket), # the one above mock.call( CacheFile(sc, sc_obj_with_texure_mtl, bucket=bucket).local), # in obj.load mock.call( CacheFile(sc, sc_obj_with_texure_tex, bucket=bucket).local), # in obj.load ]) mock_s3_open.assert_has_calls([ mock.call(sc(sc_obj_with_texure, bucket=bucket), 'rb'), mock.call(sc(sc_obj_with_texure_mtl, bucket=bucket), 'r'), ]) self.assertEqual(m.materials_filepath, sc(sc_obj_with_texure_mtl, bucket=bucket)) self.assertEqual(m.texture_filepath, sc(sc_obj_with_texure_tex, bucket=bucket))
def setUp(self): self.test_wrl_url = "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_wrl.wrl" self.test_wrl_path = sc(self.test_wrl_url) self.test_obj_url = "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_box.obj" self.test_wrl_converted_path = sc( "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_wrl_converted.obj" )
def setUp(self): import tempfile from lace.cache import sc self.tmp_dir = tempfile.mkdtemp('bodylabs-test') self.truth = { 'box_v': np.array([[0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5], [0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5], [0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5]]).T, 'box_f': np.array([[0, 1, 2], [3, 2, 1], [0, 2, 4], [6, 4, 2], [0, 4, 1], [5, 1, 4], [7, 5, 6], [4, 6, 5], [7, 6, 3], [2, 3, 6], [7, 3, 5], [1, 5, 3]]), 'box_segm': { 'a': np.array(range(6), dtype=np.uint32), 'b': np.array([6, 10, 11], dtype=np.uint32), 'c': np.array([7, 8, 9], dtype=np.uint32) }, 'landm': { 'pospospos': 0, 'negnegneg': 7 }, 'landm_xyz': { 'pospospos': np.array([0.5, 0.5, 0.5]), 'negnegneg': np.array([-0.5, -0.5, -0.5]) }, } self.test_ply_url = "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_box.ply" self.test_ply_path = sc(self.test_ply_url) self.test_bin_ply_path = sc( "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_box_le.ply" ) self.test_pp_path = sc( "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_box.pp" )
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)
def test_reading_obj_with_mtl_from_missing_windows_absolute_path( self, mock_s3_open): # In this case, we're given a windows absolute path, which it totally wrong, but if there happens # to be a mtl file of the right name in the same dir as the obj, go for it. # This is a signiicant case, because 3dMD outputs mtllib this way. skip_if_unavailable('s3') obj_path = os.path.join(self.scratch_dir, 'abs_path_to_missing_windows_mtl.obj') real_mlt_path = os.path.join(self.scratch_dir, 'abs_path_to_missing_windows_mtl.mlt') arbitrary_mlt_path = 'C:/Users/ARGH/Documents/I-Did_some_scans/Subject_47/abs_path_to_missing_windows_mtl.mlt' tex_path = os.path.abspath(sc(self.obj_with_texure_tex)) with open(obj_path, 'w') as f: f.write('mtllib {}\n'.format(arbitrary_mlt_path)) with open(real_mlt_path, 'w') as f: f.write('map_Ka {}\n'.format(tex_path)) m = obj.load(obj_path) mock_s3_open.assert_has_calls([ mock.call(obj_path, 'rb'), mock.call(real_mlt_path, 'r'), ]) self.assertEqual(m.materials_filepath, real_mlt_path) self.assertEqual(m.texture_filepath, tex_path)
def test_writing_obj_with_mtl(self): local_file = os.path.join(self.tmp_dir, "test_writing_obj_with_mtl.obj") m = obj.load(sc(self.obj_with_texure)) obj.dump(m, local_file) self.assertTrue(s3.exists(os.path.splitext(local_file)[0] + '.mtl')) self.assertTrue(s3.exists(os.path.splitext(local_file)[0] + '.png'))
def test_reading_obj_with_mtl_from_missing_absolute_path( self, mock_s3_open): # If an absolute path is given and the file is missing, try looking in the same directory; # this lets you find the most common intention when an abs path is used. skip_if_unavailable('s3') obj_path = os.path.join(self.scratch_dir, 'abs_path_to_missing_mtl.obj') real_mlt_path = os.path.join(self.scratch_dir, 'abs_path_to_missing_mtl.mlt') arbitrary_mlt_path = os.path.join(self.scratch_dir, 'some_other_absolute_path', 'abs_path_to_missing_mtl.mlt') tex_path = os.path.abspath(sc(self.obj_with_texure_tex)) with open(obj_path, 'w') as f: f.write('mtllib {}\n'.format(arbitrary_mlt_path)) with open(real_mlt_path, 'w') as f: f.write('map_Ka {}\n'.format(tex_path)) m = obj.load(obj_path) mock_s3_open.assert_has_calls([ mock.call(obj_path, 'rb'), mock.call(real_mlt_path, 'r'), ]) self.assertEqual(m.materials_filepath, real_mlt_path) self.assertEqual(m.texture_filepath, tex_path)
def test_reading_obj_with_mtl_from_local_file(self, mock_s3_open): local_obj_with_texure = os.path.join( self.tmp_dir, os.path.basename(self.obj_with_texure)) local_obj_with_texure_mtl = os.path.join( self.tmp_dir, os.path.basename(self.obj_with_texure_mtl)) local_obj_with_texure_tex = os.path.join( self.tmp_dir, os.path.basename(self.obj_with_texure_tex)) s3.cp(sc(self.obj_with_texure), local_obj_with_texure) s3.cp(sc(self.obj_with_texure_mtl), local_obj_with_texure_mtl) s3.cp(sc(self.obj_with_texure_tex), local_obj_with_texure_tex) m = obj.load(local_obj_with_texure) mock_s3_open.assert_has_calls([ mock.call(local_obj_with_texure, 'rb'), mock.call(local_obj_with_texure_mtl, 'r'), ]) self.assertEqual(m.materials_filepath, local_obj_with_texure_mtl) self.assertEqual(m.texture_filepath, local_obj_with_texure_tex)
def test_loading_vertex_colors(self): # Mesh without vertex colors should not have vertex colors mesh_without_vertex_colors = obj.load(sc(self.test_obj_url)) self.assertIsNone(mesh_without_vertex_colors.vc) # Mesh with vertex colors should have vertex colors mesh_with_vertex_colors = obj.load( sc(self.test_obj_with_vertex_colors_url)) self.assertIsNotNone(mesh_with_vertex_colors.vc) # Check sizes vc_length, vc_size = mesh_with_vertex_colors.vc.shape v_length, _ = mesh_with_vertex_colors.v.shape self.assertEqual(vc_length, v_length) self.assertEqual(vc_size, 3) # Vertices should be the same self.assertTrue( (mesh_without_vertex_colors.v == mesh_with_vertex_colors.v).all())
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()
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)
def test_loading_brings_in_normals_and_uvs(self): # This file is known to have vt, vn, and faces of the form 1/2/3 texture_template = 's3://bodylabs-korper-assets/is/ps/shared/data/body/template/texture_coordinates/textured_template_low_v2.obj' mesh_with_texture = obj.load(sc(texture_template)) self.assertIsNotNone(mesh_with_texture.vt) self.assertIsNotNone(mesh_with_texture.ft) self.assertEqual(mesh_with_texture.vt.shape[1], 2) self.assertEqual(mesh_with_texture.vt.shape[0], np.max(mesh_with_texture.ft) + 1) self.assertIsNotNone(mesh_with_texture.vn) self.assertIsNotNone(mesh_with_texture.fn) self.assertEqual(mesh_with_texture.vn.shape[1], 3) self.assertEqual(mesh_with_texture.vn.shape[0], np.max(mesh_with_texture.fn) + 1)
def test_cut_across_axis_by_percentile(self): original_mesh = Mesh(filename=sc( 's3://bodylabs-assets/example_meshes/average_female.obj')) # Set up mesh = original_mesh.copy() # Sanity check np.testing.assert_almost_equal(mesh.v[:, 0].min(), -0.3668) np.testing.assert_almost_equal(mesh.v[:, 0].max(), 0.673871) # Act mesh.cut_across_axis_by_percentile(0, 25, 40) # Test np.testing.assert_almost_equal(mesh.v[:, 0].min(), 0.03, decimal=1) np.testing.assert_almost_equal(mesh.v[:, 0].max(), 0.10, decimal=1) # Visualize if self.debug: mesh.show()
def test_reading_obj_with_mtl_from_absolute_path(self, mock_s3_open): # This is generally a very bad idea; it makes it hard to move an obj around skip_if_unavailable('s3') obj_path = os.path.join(self.scratch_dir, 'abs_path_to_mtl.obj') mlt_path = os.path.join(self.scratch_dir, 'abs_path_to_mtl.mlt') tex_path = os.path.abspath(sc(self.obj_with_texure_tex)) with open(obj_path, 'w') as f: f.write('mtllib {}\n'.format(mlt_path)) with open(mlt_path, 'w') as f: f.write('map_Ka {}\n'.format(tex_path)) m = obj.load(obj_path) mock_s3_open.assert_has_calls([ mock.call(obj_path, 'rb'), mock.call(mlt_path, 'r'), ]) self.assertEqual(m.materials_filepath, mlt_path) self.assertEqual(m.texture_filepath, tex_path)
def path_relative_to_mesh(filename): # The OBJ file we're loading may have come from a local path, an s3 url, # or a file cached by sc. Since OBJ defines materials and texture files # with paths relative to the OBJ itself, we need to cope with the various # possibilities and if it's a cached file make sure that the material and # texture have been downloaded as well. # # If an absolute path is given and the file is missing, try looking in the same directory; # this lets you find the most common intention when an abs path is used. # # NB: We do not support loading material & texture info from objs read # from filelike objects without a location on the filesystem; what would # the relative file names mean in that case, anyway? (unless we're given # a valid absolute path, in which case go for it) import os import re # The second term here let's us detect windows absolute paths when we're running on posix if filename == os.path.abspath(filename) or re.match( r'^.\:(\\|/)', filename): if s3.exists(filename): return filename else: filename = s3.path.basename(filename) if hasattr(fd, 'remotename'): mesh_path = fd.remotename elif hasattr(fd, 'name'): mesh_path = fd.name else: return None path = s3.path.join(s3.path.dirname(mesh_path), filename) if sc.is_cachefile(mesh_path): try: return sc(path) except s3.KeyNotFound: return None return path
def setUp(self): import tempfile self.tmp_dir = tempfile.mkdtemp('bodylabs-test') self.truth = { 'box_v': np.array([[0.5, -0.5, 0.5, -0.5, 0.5, -0.5, 0.5, -0.5], [0.5, 0.5, -0.5, -0.5, 0.5, 0.5, -0.5, -0.5], [0.5, 0.5, 0.5, 0.5, -0.5, -0.5, -0.5, -0.5]]).T, 'box_f': np.array([[0, 1, 2], [3, 2, 1], [0, 2, 4], [6, 4, 2], [0, 4, 1], [5, 1, 4], [7, 5, 6], [4, 6, 5], [7, 6, 3], [2, 3, 6], [7, 3, 5], [1, 5, 3]]), 'box_fn': np.array([[0, 0, 1], [0, 0, 1], [1, 0, 0], [1, 0, 0], [0, 1, 0], [0, 1, 0], [0, 0, -1], [-0, -0, -1], [0, -1, 0], [-0, -1, -0], [-1, 0, 0], [-1, -0, -0]]), } # Because STL gives duplicated verts self.truth['box_v'] = self.truth['box_v'][ self.truth['box_f'].flatten()] self.truth['box_f'] = np.array(range( self.truth['box_v'].shape[0])).reshape((-1, 3)) self.test_stl_url = "s3://bodylabs-korper-assets/is/ps/shared/data/body/korper_testdata/test_box.stl" self.test_stl_path = sc(self.test_stl_url)
def test_loads_unsupported_format_raise_exception(self): with self.assertRaises(wrl.ParseError): with open(sc(self.test_obj_url)) as f: wrl.load(f)