def test_requires_in_range_marker_id(marker_type: MarkerType) -> None: MarkerCamera(marker_type.max_id, marker_size=200, marker_type=marker_type) with pytest.raises(ValueError) as e: MarkerCamera(marker_type.max_id + 1, marker_size=200, marker_type=marker_type) assert "must be less than the maximum allowed" in e.value.args[0]
def test_sees_nothing_in_blank_image(): marker_camera = MarkerCamera(25, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200) empty_frame = numpy.zeros((200, 200, 3), numpy.uint8) markers = list(marker_camera.process_frame(frame=empty_frame)) assert markers == []
def test_process_eager_frame_without_calibrations(marker_type: MarkerType) -> None: marker_camera = MarkerCamera( MAX_ALL_ALLOWED_ID, marker_size=200, marker_type=marker_type ) marker_camera.calibration_params = None with pytest.raises(MissingCalibrationsError): list(marker_camera.process_frame_eager())
def is_valid_calibration(filename: Path) -> bool: if not filename.is_file(): return False # First, Try and parse the file try: parse_calibration_file(filename) except (SystemError, ValueError): return False with tempfile.NamedTemporaryFile(suffix=".png") as temp_file: marker_camera = MarkerCamera(0, 200, marker_type=MarkerType.APRILTAG_36H11) marker_camera.save_frame(Path(temp_file.name)) image_camera = ImageFileCamera( Path(temp_file.name), marker_size=200, marker_type=MarkerType.APRILTAG_36H11, calibration_file=filename, ) # Sanity check the image camera if image_camera.get_visible_markers() != [0]: return False # Then, confirm the detection works if len(list(marker_camera.process_frame_eager())) != 1: return False return True
def test_marker_type_max_id_disallowed(marker_type: MarkerType) -> None: camera = MarkerCamera(marker_type.max_id, marker_size=100, marker_type=marker_type) camera.marker_id = marker_type.max_id + 1 # There's an assertion in the constructor with pytest.raises(cv2.error): camera.get_visible_markers()
def setUp(self): self.marker_camera = MarkerCamera( self.MARKER_ID, marker_dict=MarkerDict.DICT_6X6_50, marker_size=self.MARKER_SIZE, ) self.markers = list(self.marker_camera.process_frame()) self.marker = self.markers[0]
def test_saved_image(temp_image_file, marker_id): marker_camera = MarkerCamera(marker_id, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200) marker_camera.save_frame(temp_image_file) image_file_camera = ImageFileCamera(temp_image_file, marker_dict=MarkerDict.DICT_6X6_50) assert image_file_camera.get_visible_markers() == [marker_id]
def setUp(self) -> None: self.marker_camera = MarkerCamera( self.MARKER_ID, marker_size=self.MARKER_SIZE, marker_type=MarkerType.ARUCO_6X6, ) self.markers = list(self.marker_camera.process_frame_eager()) self.marker = self.markers[0]
def setUp(self) -> None: self.marker_camera = MarkerCamera( self.MARKER_ID, marker_size=self.MARKER_SIZE, marker_type=MarkerType.ARUCO_6X6, ) self.marker_camera.calibration_params = None self.markers = list(self.marker_camera.process_frame()) self.marker = self.markers[0]
def test_saved_image( marker_type: MarkerType, temp_image_file: Path, marker_id: int ) -> None: marker_camera = MarkerCamera(marker_id, marker_size=200, marker_type=marker_type) marker_camera.save_frame(temp_image_file) image_file_camera = ImageFileCamera( temp_image_file, marker_type=marker_type, marker_size=200 ) assert image_file_camera.get_visible_markers() == [marker_id]
def test_detect_at_minimum_size(marker_type: MarkerType) -> None: marker_camera = MarkerCamera( 0, marker_size=marker_type.min_marker_image_size, marker_type=marker_type, border_size=MarkerCamera.MIN_BORDER_SIZE, ) frame = marker_camera.capture_frame() assert frame.shape == marker_camera.get_resolution() marker = next(marker_camera.process_frame(frame=frame)) assert marker.id == 0
def test_marker_size( marker_type: MarkerType, temp_image_file: Path, marker_id: int ) -> None: class TestCamera(ImageFileCamera): def get_marker_size(self, inner_marker_id: int) -> int: return inner_marker_id * 10 marker_camera = MarkerCamera(marker_id, marker_size=200, marker_type=marker_type) marker_camera.save_frame(temp_image_file) image_file_camera = TestCamera(temp_image_file, marker_type=marker_type) marker = next(image_file_camera.process_frame()) assert marker.size == marker_id * 10 assert marker.id == marker_id
def test_no_markers(temp_image_file: Path) -> None: marker_camera = MarkerCamera( MAX_ALL_ALLOWED_ID, marker_size=100, marker_type=MarkerType.ARUCO_4X4, ) marker_camera.save_frame(temp_image_file) image_file_camera = ImageFileCamera( temp_image_file, marker_type=MarkerType.ARUCO_5X5, marker_size=100 ) assert marker_camera.marker_type != image_file_camera.marker_type assert image_file_camera.get_visible_markers() == []
def test_detects_markers(marker_id): markers = list( MarkerCamera(marker_id, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200).process_frame()) assert len(markers) == 1 assert markers[0].id == marker_id
def test_minimum_marker_size(marker_type: MarkerType) -> None: camera = MarkerCamera( marker_type.max_id, marker_size=marker_type.min_marker_image_size, marker_type=marker_type, ) assert camera.get_visible_markers() == [marker_type.max_id] with pytest.raises(ValueError) as e: MarkerCamera( marker_type.max_id, marker_size=marker_type.min_marker_image_size - 1, marker_type=marker_type, ) assert "marker must be at least" in e.value.args[0]
class UncalibratedMarkerTestCase(MarkerTestCase): EXPECTED_DICT_KEYS = {"id", "size", "pixel_corners"} def setUp(self) -> None: self.marker_camera = MarkerCamera( self.MARKER_ID, marker_size=self.MARKER_SIZE, marker_type=MarkerType.ARUCO_6X6, ) self.marker_camera.calibration_params = None self.markers = list(self.marker_camera.process_frame()) self.marker = self.markers[0] def test_is_uncalibrated(self) -> None: self.assertIsInstance(self.marker, UncalibratedMarker) with self.assertRaises(MissingCalibrationsError): self.marker._get_pose_vectors() def __getattribute__(self, name: str) -> Any: attr = super().__getattribute__(name) if name in [ "test_orientation", "test_distance", "test_cartesian_coordinates", "test_spherical_coordinates", ]: def test_raises(*args: Any, **kwargs: Any) -> None: with self.assertRaises(MissingCalibrationsError): attr(*args, **kwargs) return test_raises return attr
def test_detects_markers(marker_type: MarkerType, marker_id: int) -> None: markers = list( MarkerCamera( marker_id, marker_size=100, marker_type=marker_type ).process_frame() ) assert len(markers) == 1 assert markers[0].id == marker_id
def test_eager_capture(marker_id): markers = list( MarkerCamera(marker_id, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200).process_frame_eager()) assert len(markers) == 1 assert markers[0].id == marker_id assert markers[0]._is_eager()
class MarkerFromDictTestCase(EagerMarkerTestCase): def setUp(self): self.marker_camera = MarkerCamera( self.MARKER_ID, marker_dict=MarkerDict.DICT_6X6_50, marker_size=self.MARKER_SIZE, ) self.markers = list(self.marker_camera.process_frame()) self.marker = Marker.from_dict(self.markers[0].as_dict())
def test_eager_capture(marker_type: MarkerType, marker_id: int) -> None: markers = list( MarkerCamera( marker_id, marker_size=200, marker_type=marker_type ).process_frame_eager() ) assert len(markers) == 1 assert markers[0].id == marker_id assert isinstance(markers[0], EagerMarker)
class EagerMarkerTestCase(MarkerTestCase): def setUp(self): self.marker_camera = MarkerCamera( self.MARKER_ID, marker_dict=MarkerDict.DICT_6X6_50, marker_size=self.MARKER_SIZE, ) self.markers = list(self.marker_camera.process_frame_eager()) self.marker = self.markers[0] def test_is_eager(self): self.assertTrue(self.marker._is_eager()) @patch("cv2.aruco.estimatePoseSingleMarkers") def test_doesnt_calculate_pose(self, pose_mock): assert self.marker._tvec is not None assert self.marker._rvec is not None pose_mock.assert_not_called()
class EagerMarkerTestCase(MarkerTestCase): def setUp(self) -> None: self.marker_camera = MarkerCamera( self.MARKER_ID, marker_size=self.MARKER_SIZE, marker_type=MarkerType.ARUCO_6X6, ) self.markers = list(self.marker_camera.process_frame_eager()) self.marker = self.markers[0] @patch("cv2.aruco.estimatePoseSingleMarkers") def test_doesnt_calculate_pose(self, pose_mock: Any) -> None: assert self.marker._tvec is not None assert self.marker._rvec is not None pose_mock.assert_not_called() def test_is_eager(self) -> None: self.assertIsInstance(self.marker, EagerMarker)
def main(args: argparse.Namespace) -> None: from PIL import Image, ImageDraw marker_type = MarkerType[args.type] output_dir: Path = args.path.resolve() output_dir.mkdir(exist_ok=True, parents=True) marker_ids = (parse_ranges(args.range) if args.range != "ALL" else marker_type.marker_ids) for marker_id in sorted(marker_ids): with MarkerCamera( marker_id, marker_type=marker_type, marker_size=marker_type.min_marker_image_size, ) as camera: camera.border_size = 1 # HACK: There's validation in the constructor print("Saving", marker_id) # noqa:T001 if args.raw: camera.save_frame(output_dir / "{}.png".format(marker_id)) else: image = Image.fromarray(camera.capture_frame()) # Resize the image to the required size resized_image = image.resize((500, 500), resample=0) img_size = resized_image.size[0] ImageDraw.Draw(resized_image).text( (25, img_size - 25), args.description_format.format( marker_type=marker_type.name, marker_id=marker_id), anchor="lt", ) resized_image.save(output_dir / "{}.png".format(marker_id))
def test_camera_as_context_manager(): with MarkerCamera(25, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200) as marker_camera: markers = list(marker_camera.get_visible_markers()) assert markers == [25]
class MarkerTestCase(TestCase): MARKER_SIZE = 200 MARKER_ID = MAX_ALL_ALLOWED_ID EXPECTED_DICT_KEYS = {"id", "size", "pixel_corners", "rvec", "tvec"} def setUp(self) -> None: self.marker_camera = MarkerCamera( self.MARKER_ID, marker_size=self.MARKER_SIZE, marker_type=MarkerType.ARUCO_6X6, ) self.markers = list( self.marker_camera.process_frame()) # type: List[BaseMarker] self.marker = self.markers[0] def assertIsType(self, a: Any, b: Any) -> None: self.assertEqual(type(a), b) def test_marker_size(self) -> None: self.assertEqual(self.marker.size, self.MARKER_SIZE) def test_marker_id(self) -> None: self.assertEqual(self.marker.id, self.MARKER_ID) def test_marker_type(self) -> None: self.assertEqual(self.marker.marker_type, MarkerType.ARUCO_6X6) def test_pixel_corners(self) -> None: self.assertEqual(len(self.marker.pixel_corners), 4) border_size = self.marker_camera.border_size tl, tr, br, bl = self.marker.pixel_corners self.assertEqual(tl, (border_size, border_size)) self.assertEqual(tr, (self.MARKER_SIZE + border_size - 1, border_size)) self.assertEqual( br, (self.MARKER_SIZE + border_size - 1, self.MARKER_SIZE + border_size - 1), ) self.assertEqual(bl, (border_size, self.MARKER_SIZE + border_size - 1)) def test_pixel_centre(self) -> None: tl, _, br, _ = self.marker.pixel_corners self.assertEqual(self.marker.pixel_centre, (139, 139)) def test_distance(self) -> None: self.assertAlmostEqual(self.marker.distance, 900, delta=100) # HACK: Sometimes it changes def test_orientation(self) -> None: rot_x, rot_y, rot_z = self.marker.orientation self.assertIn(int(rot_x), [3, -3]) # HACK: Sometimes it changes self.assertEqual(int(rot_y), 0) self.assertEqual(int(rot_z), 0) def test_cartesian_coordinates(self) -> None: x, y, z = self.marker.cartesian self.assertAlmostEqual(int(x), 52, delta=10) # HACK: Sometimes it changes self.assertAlmostEqual(int(y), 15, delta=20) # HACK: Sometimes it changes self.assertAlmostEqual(int(z), 910, delta=100) # HACK: Sometimes it changes def test_spherical_coordinates(self) -> None: rot_x, rot_y, dist = self.marker.spherical self.assertEqual(dist, self.marker.distance) self.assertAlmostEqual(rot_x, 0, delta=0.1) self.assertAlmostEqual(rot_y, 0, delta=0.1) def test_as_dict(self) -> None: marker_dict = self.marker.as_dict() self.assertIsInstance(marker_dict, dict) self.assertEqual(self.EXPECTED_DICT_KEYS, set(marker_dict.keys())) self.assertEqual(marker_dict["size"], self.MARKER_SIZE) self.assertEqual(marker_dict["id"], self.MARKER_ID) def test_as_dict_json(self) -> None: marker_dict = self.marker.as_dict() created_marker_dict = json.loads(json.dumps(self.marker.as_dict())) self.assertEqual(marker_dict, created_marker_dict) self.assertEqual(self.EXPECTED_DICT_KEYS, set(marker_dict.keys())) def test_dict_value_types(self) -> None: marker_dict = self.marker.as_dict() self.assertIsType(marker_dict["id"], int) self.assertIsType(marker_dict["size"], int) pixel_corners = marker_dict["pixel_corners"] self.assertIsType(pixel_corners, list) self.assertIsType(pixel_corners[0], list) self.assertIsType(pixel_corners[0][0], float) if "rvec" in marker_dict: self.assertIsType(marker_dict["rvec"], list) self.assertIsType(marker_dict["rvec"][0], float) self.assertIsType(marker_dict["tvec"], list) self.assertIsType(marker_dict["tvec"][0], float) def test_marker_types(self) -> None: self.assertIsType(self.marker.id, int) self.assertIsType(self.marker.size, int) self.assertIsType(self.marker.pixel_corners[0].x, float) self.assertIsType(self.marker.pixel_corners[0].y, float) self.assertIsType(self.marker.pixel_centre.x, float) self.assertIsType(self.marker.pixel_centre.y, float) if "rvec" in self.EXPECTED_DICT_KEYS: self.assertIsType(self.marker.distance, int) self.assertIsType(self.marker.spherical.rot_x, float) self.assertIsType(self.marker.spherical.rot_y, float) self.assertIsType(self.marker.spherical.dist, int) self.assertIsType(self.marker.cartesian.x, float) self.assertIsType(self.marker.cartesian.y, float) self.assertIsType(self.marker.cartesian.z, float) def test_repr(self) -> None: self.assertIn(str(self.MARKER_ID), repr(self.marker)) self.assertIn(str(self.MARKER_SIZE), repr(self.marker)) self.assertIn("ARUCO_6X6", repr(self.marker)) # Eager markers also show their distance if isinstance(self.marker, EagerMarker): self.assertIn(str(self.marker.distance), repr(self.marker))
def test_process_frame_eager(benchmark: Callable, marker_camera: MarkerCamera) -> None: frame = marker_camera.capture_frame() benchmark(lambda: list(marker_camera.process_frame_eager(frame=frame)))
def test_get_visible_markers(benchmark: Callable, marker_camera: MarkerCamera) -> None: frame = marker_camera.capture_frame() benchmark(marker_camera.get_visible_markers, frame=frame)
def test_saved_image_with_annotation(temp_image_file, marker_id): marker_camera = MarkerCamera(marker_id, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200) output_file = temp_image_file marker_camera.save_frame(output_file, annotate=True)
def test_marker_with_falsy_id(): with MarkerCamera(0, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200) as marker_camera: markers = list(marker_camera.get_visible_markers()) assert markers == [0]
def test_detects_marker_ids(marker_id): markers = MarkerCamera(marker_id, marker_dict=MarkerDict.DICT_6X6_50, marker_size=200).get_visible_markers() assert markers == [marker_id]