def test_bounds(self): points = np.array([[0.6, 0.1, -0.1], [1.1, 2.1, 3.1], [1.3, 2.2, 3.4]]) min_corner = Vector3f(-1, -2, -3) vg = VoxelGrid(0.5, min_corner=min_corner, points=points) bounds = vg.bounds() expected_bounds = Box3d(Vector3f(0.5, 0, -0.5), Vector3f(1.5, 2.5, 3.5)) self.assertAlmostEquals(bounds, expected_bounds)
def test_min_corner_offset(self): """ Test with a non-zero min_corner. """ points = np.array([[0.1, 0.1, 0.1], [1.1, 2.1, 3.1], [1.3, 2.2, 3.4]]) vg = VoxelGrid(1, min_corner=Vector3f(-1, -2, -3), points=points) centers = vg.voxel_centers() expected_centers = np.array([[0.5, 0.5, 0.5], [1.5, 2.5, 3.5]]) np.testing.assert_array_almost_equal(centers, expected_centers)
def test_constructor_with_points(self): """ Tests voxel_centers. Verifies that multiple points in the same voxel are deduplicated. """ points = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1], [1.3, 1.2, 1.4]]) vg = VoxelGrid(0.5, min_corner=Vector3f(0, 0, 0), points=points) centers = vg.voxel_centers() expected_centers = np.array([[0.25, 0.25, 0.25], [1.25, 1.25, 1.25]]) np.testing.assert_array_almost_equal(centers, expected_centers)
def test_add_points(self): """ Add additional points to an initial set. Checks that redundant voxels are counted only once. """ points = np.array([[0.1, 0.1, 0.1], [1.1, 2.1, 3.1], [1.3, 2.2, 3.4]]) vg = VoxelGrid(1, min_corner=Vector3f(-1, -2, -3), points=points) points2 = np.array([[0.2, 0.2, 0.3], [2, 2, 2], [4, 4, 4]]) vg.add_points(points2) centers = vg.voxel_centers() expected_centers = np.array([[0.5, 0.5, 0.5], [2.5, 2.5, 2.5], [1.5, 2.5, 3.5], [4.5, 4.5, 4.5]]) np.testing.assert_array_almost_equal(centers, expected_centers)
def test_constructor(self): """ Creates a simple voxel grid and checks its attributes. """ expected_min_corner = Vector3f(-5.1, -6.2, -7.5) vg = VoxelGrid(0.2, expected_min_corner) self.assertAlmostEqual(vg.voxel_size, 0.2) np.testing.assert_array_equal(vg.min_corner, expected_min_corner)
def setUp(self): # create sample bounding box, meshes, and voxel grid objects self.bounds = Box3d([-1.5, -2.2, 3], [4, 4.1, 6.5]) textured_mesh = self.bounds.to_textured_mesh() self.meshes = GltfModel.from_textured_mesh(textured_mesh) points = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1], [1.3, 1.2, 1.4]]) self.voxels = VoxelGrid(0.5, min_corner=Vector3f(0, 0, 0), points=points) # and pose rot = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]], dtype=float) trans = Vector3f(1, 1, 1) self.pose = Pose3(rot, trans) # Create temporary outout directory. self.temp_directory = tempfile.mkdtemp()
def load(cls, project_type, base_elem, path=None): """ Factory method to create a ProjectObject by reading it from disk and xml. Note: If <project_type> is bounding_box, no file is read and <path> is not relevant. Inputs: project_type (string) - Specifies the project type to construct (valid values are "bounding_box", "voxels", or "meshes") base_elem (ET.Element) - An Element with tag "element" and appropriate sub-elements. path (string) - path to project directory (only required if project_type is voxels or mesh). Return: New ProjectObject Exceptions: ValueError - If base_elem is not <element> or if none of its children is <id>. ValueError - If project_type is not valid. IOError - if file cannot be read. """ (id, pose, category, bounds, symmetry, score, evaluated) = \ cls._parse_xml(base_elem) # load file-based attributes and return the constructed object if project_type == "bounding_box": return ProjectObject.gen_bounding_box_object(id=id, bounds=bounds, pose=pose, category=category, symmetry=symmetry, score=score, evaluated=evaluated) elif project_type == "voxels": voxels = VoxelGrid.from_file(os.path.join(path, id + ".h5")) return ProjectObject.gen_voxels_object(id=id, bounds=bounds, voxels=voxels, pose=pose, category=category, symmetry=symmetry, score=score, evaluated=evaluated) elif project_type == "meshes": meshes = GltfModel.load_from_glb(os.path.join(path, id + ".glb")) return ProjectObject.gen_meshes_object(id=id, bounds=bounds, meshes=meshes, pose=pose, category=category, symmetry=symmetry, score=score, evaluated=evaluated) else: raise ValueError("Invalid project_type: " + project_type)
def test_file_io(self): """ Test round trip writing and reading a simple h5 file. """ temp_directory = tempfile.mkdtemp() filename = os.path.join(temp_directory, "test.h5") points = np.array([[0.1, 0.1, 0.1], [1.1, 2.1, 3.1], [1.3, 2.2, 3.4]]) voxel_size = 0.5 min_corner = Vector3f(-1, -2, -3) vg = VoxelGrid(voxel_size, min_corner=min_corner, points=points) # test writing vg.save(filename) self.assertTrue(os.path.isfile(filename)) # test reading vg2 = VoxelGrid.from_file(filename) self.assertAlmostEquals(voxel_size, vg2.voxel_size) np.testing.assert_array_almost_equal(vg.min_corner, vg2.min_corner) shutil.rmtree(temp_directory)
def test_voxel_io(self): """Test reading and writing voxel objects.""" points = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1], [1.3, 1.2, 1.4]]) vg = VoxelGrid(0.5, min_corner=Vector3f(0, 0, 0), points=points) po = ProjectObject.gen_voxels_object(id="foobar", voxels=vg, pose=self.pose, category="chair") self.assertIsInstance(self.temp_directory, str) po_xml = po.save(self.temp_directory) po2 = ProjectObject.load("voxels", po_xml, self.temp_directory) self.assertTrue(po2.almost_equal(po))
class TestProjectObject(unittest.TestCase): """Unit tests for ProjectObject class""" def setUp(self): # create sample bounding box, meshes, and voxel grid objects self.bounds = Box3d([-1.5, -2.2, 3], [4, 4.1, 6.5]) textured_mesh = self.bounds.to_textured_mesh() self.meshes = GltfModel.from_textured_mesh(textured_mesh) points = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1], [1.3, 1.2, 1.4]]) self.voxels = VoxelGrid(0.5, min_corner=Vector3f(0, 0, 0), points=points) # and pose rot = np.array([[1, 0, 0], [0, 0, -1], [0, 1, 0]], dtype=float) trans = Vector3f(1, 1, 1) self.pose = Pose3(rot, trans) # Create temporary outout directory. self.temp_directory = tempfile.mkdtemp() def tearDown(self): """Clean up: remove temporary outout directory.""" shutil.rmtree(self.temp_directory) def test_constructor(self): """ Test constructor. """ po = ProjectObject(id="1", project_type="bounding_box", bounds=self.bounds) self.assertTrue(isinstance(po.pose, Pose3)) self.assertTrue(isinstance(po.category, "".__class__)) po.pose.assert_almost_equal(Pose3()) self.assertTrue(po.bounds.almost_equal(self.bounds)) self.assertIs(po.meshes, None) self.assertIs(po.voxels, None) self.assertTrue(po.category == "unknown") self.assertEqual(po.id, "1") self.assertEqual(po.symmetry, ObjectSymmetry()) self.assertAlmostEqual(po.score, -1) self.assertEqual(po.evaluated, True) def test_almost_equal(self): object1 = ProjectObject.example(id="foobar") self.assertTrue(object1.almost_equal(object1)) object2 = ProjectObject.example(id="not_foobar") self.assertFalse(object1.almost_equal(object2)) def test_init(self): """Test common construction.""" po = ProjectObject.example() self.assertTrue(isinstance(po.pose, Pose3)) self.assertTrue(isinstance(po.meshes, GltfModel)) self.assertEqual(po.meshes.num_primitive_meshes(), 1) self.assertEqual(po.meshes.num_nodes(), 1) self.assertEqual(po.meshes.num_buffers(), 1) self.assertEqual(po.meshes.num_images(), 2) self.assertTrue(isinstance(po.category, "".__class__)) self.assertTrue(isinstance(po.symmetry, ObjectSymmetry)) self.assertEqual(po.category, "chair") self.assertEqual(po.id, "1") self.assertAlmostEqual(po.score, 0.57) self.assertEqual(po.evaluated, False) def test_factory_methods(self): """Test the gen_<X> methods for the 3 project_types""" po = ProjectObject.gen_bounding_box_object(id="1", bounds=self.bounds) self.assertEqual(po.project_type, "bounding_box") self.assertAlmostEqual(po.bounds, self.bounds) self.assertEqual(po.id, "1") po = ProjectObject.gen_voxels_object(id="2", voxels=self.voxels) self.assertEqual(po.project_type, "voxels") self.assertAlmostEqual(po.voxels.bounds(), self.voxels.bounds()) self.assertEqual(po.id, "2") po = ProjectObject.gen_meshes_object(id="3", meshes=self.meshes) self.assertEqual(po.project_type, "meshes") self.assertEqual(po.meshes.num_primitive_meshes(), self.meshes.num_primitive_meshes()) self.assertEqual(po.id, "3") def test_setting(self): """Test setting elements.""" po = ProjectObject.gen_meshes_object(id="-1", meshes=self.meshes) po.pose = self.pose po.category = "table" po.symmetry = ObjectSymmetry.example() po.score = 0.23 po.evaluated = True self.assertTrue(isinstance(po.pose, Pose3)) self.assertAlmostEqual(po.pose, self.pose) self.assertTrue(isinstance(po.meshes, GltfModel)) self.assertTrue(isinstance(po.category, "".__class__)) self.assertTrue(isinstance(po.symmetry, ObjectSymmetry)) self.assertEqual(po.category, "table") self.assertEqual(po.id, "-1") self.assertEqual(po.symmetry, ObjectSymmetry.example()) self.assertAlmostEqual(po.score, 0.23) self.assertEqual(po.evaluated, True) def test_xml(self): """Test converting to and from xml.""" s = """<element><id>floor1</id><category>floor</category><bounds>\ <corner1>0., 0., 0.</corner1><corner2>0., 0., 0.</corner2></bounds>\ <pose><translation>-131.596614 , -39.9279011, 92.1260558</translation>\ <rotation><c1>1., 0., 0.</c1><c2>0., 1., 0.</c2><c3>0., 0., 1.</c3></rotation>\ </pose><symmetry><x>twoFold</x><y>twoFold</y><z>fourFold</z></symmetry>\ <detectionScore>0.23</detectionScore><evaluated>True</evaluated></element>""" object_xml = ET.fromstring(s) (id, pose, category, bounds, symmetry, score, evaluated) = \ ProjectObject._parse_xml(object_xml) project_object = ProjectObject.gen_bounding_box_object( id, bounds, pose, category, symmetry, score, evaluated) object_xml2 = project_object._to_xml() self.assertEqual(ET.tostring(object_xml2, encoding="unicode"), s) def test_meshes_io(self): """Test reading and writing gltf objects.""" po = ProjectObject( id="foobar", project_type="meshes", meshes=self.meshes, pose=self.pose, category="chair", ) object_xml = po.save(self.temp_directory) po2 = ProjectObject.load("meshes", object_xml, self.temp_directory) self.assertTrue(po2.almost_equal(po)) def test_bounding_box_io(self): """ Test reading and writing a bounding_box object. """ s = """<element><id>floor1</id><category>floor</category><bounds>\ <corner1>0., 0., 0.</corner1><corner2>0., 0., 0.</corner2></bounds>\ <pose><translation>-131.596614 , -39.9279011, 92.1260558</translation>\ <rotation><c1>1., 0., 0.</c1><c2>0., 1., 0.</c2><c3>0., 0., 1.</c3></rotation>\ </pose><symmetry><x>twoFold</x><y>twoFold</y><z>fourFold</z></symmetry>\ <detectionScore>0.23</detectionScore><evaluated>True</evaluated></element>""" object_xml = ET.fromstring(s) project_object = ProjectObject.load("bounding_box", object_xml) object_xml2 = project_object.save("bounding_box") self.assertEqual(ET.tostring(object_xml2, encoding="unicode"), s) def test_voxel_io(self): """Test reading and writing voxel objects.""" points = np.array([[0.1, 0.1, 0.1], [1.1, 1.1, 1.1], [1.3, 1.2, 1.4]]) vg = VoxelGrid(0.5, min_corner=Vector3f(0, 0, 0), points=points) po = ProjectObject.gen_voxels_object(id="foobar", voxels=vg, pose=self.pose, category="chair") self.assertIsInstance(self.temp_directory, str) po_xml = po.save(self.temp_directory) po2 = ProjectObject.load("voxels", po_xml, self.temp_directory) self.assertTrue(po2.almost_equal(po)) def test_transform_pose(self): po = ProjectObject(id="1", bounds=self.bounds, pose=Pose3()) po2 = po.transform_pose(self.pose) self.assertTrue(po2.pose.almost_equal(self.pose))