def test_detect_objects(): """ Test expected ``detect_objects`` behavior when ``_detect_objects`` yields expected values. """ # Test Inputs t_de1_uuid = "test uuid" t_de1 = mock.MagicMock(spec=DataElement) t_de1.uuid.return_value = t_de1_uuid # Expected outputs of _detect_objects t_det1 = (AxisAlignedBoundingBox([0, 0], [1, 1]), {'l1': 0, 'l2': 1}) t_det2 = (AxisAlignedBoundingBox([1, 1], [2, 2]), {'l1': 1, 'l3': 0}) # Mock instance of ObjectDetector mocking _detect_objects. m_inst = mock.MagicMock(spec=ObjectDetector) m_inst._detect_objects.return_value = (t_det1, t_det2) m_inst._gen_detection_uuid = \ mock.Mock(wraps=ObjectDetector._gen_detection_uuid) dets_list = list(ObjectDetector.detect_objects(m_inst, t_de1)) m_inst._detect_objects.assert_called_once_with(t_de1) assert m_inst._gen_detection_uuid.call_count == 2 m_inst._gen_detection_uuid.assert_any_call(t_de1_uuid, t_det1[0], t_det1[1].keys()) m_inst._gen_detection_uuid.assert_any_call(t_de1_uuid, t_det2[0], t_det2[1].keys()) assert len(dets_list) == 2 # Assert detections returned have the expected properties assert dets_list[0].get_detection()[0] == t_det1[0] assert dets_list[0].get_detection()[1].get_classification() == t_det1[1] assert dets_list[1].get_detection()[0] == t_det2[0] assert dets_list[1].get_detection()[1].get_classification() == t_det2[1]
def test_serialize_deserialize_pickle(): """ Test that serialization and the deserialization of a MemoryDetectionElement instance with populated detection components results in a different but equivalent instance. """ expected_uuid = 'some-uuid' expected_bbox = AxisAlignedBoundingBox([0, 0], [1, 2]) expected_ce_map = {'a': .24, 'b': 0.5, 0: .26} expected_ce = MemoryClassificationElement('ce-type', expected_uuid) expected_ce.set_classification(expected_ce_map) e1 = MemoryDetectionElement(expected_uuid) e1._bbox = expected_bbox e1._classification = expected_ce buff = pickle.dumps(e1) #: :type: MemoryDetectionElement e2 = pickle.loads(buff) assert e2 is not e1 assert e2.uuid == expected_uuid e2_bbox, e2_ce = e2.get_detection() # Intentionally checking e2_bbox is not the same instance assert e2_bbox is not expected_bbox # lgtm[py/comparison-using-is] assert e2_bbox == expected_bbox assert e2_ce is not expected_ce assert e2_ce == expected_ce assert e2_ce.get_classification() == expected_ce_map
def test_gen_detection_uuid(): """ Test that, for the same input data, we consistently get the same UUID for some number of repetitions. """ # Arbitrary experimental input values t_uuid = "SomeDataElementChecksumUUID" t_bbox = AxisAlignedBoundingBox([2.6, 82], [98, 83.7]) t_labels = ('l1', 'l2', 42) # Number of repetitions to test over. n = 100 initial_uuid = ObjectDetector._gen_detection_uuid(t_uuid, t_bbox, t_labels) for i in range(n): ex_uuid = ObjectDetector._gen_detection_uuid(t_uuid, t_bbox, t_labels) assert initial_uuid == ex_uuid, \ "Found detection UUID ('{}', iter {}) unexpectedly deviated " \ "from initial ('{}')" \ .format(ex_uuid, i, initial_uuid) # Order of input labels should not matter. t_labels_otherorder = ('l2', 42, 'l1') assert initial_uuid == ObjectDetector._gen_detection_uuid( t_uuid, t_bbox, t_labels_otherorder), \ "Rearranging label order caused UUID deviance, this shouldn't " \ "have happened."
def test_in_bounds_zero_crop_area(self): """ Test that crop is not ``in_bounds`` when it has zero area (undefined). """ # noinspection PyArgumentList bb = AxisAlignedBoundingBox([1, 2], [1, 2]) assert not crop_in_bounds(bb, 4, 6)
def test_in_bounds_completely_outside(self): """ Test that being completely outside the given bounds causes ``in_bounds`` to return False. """ bb = AxisAlignedBoundingBox([100, 100], [102, 102]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([-100, -100], [-98, -98]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([-100, 100], [-98, 102]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([100, -100], [102, -98]) assert not crop_in_bounds(bb, 4, 6)
def test_in_bounds_crossing_edges(self): """ Test that ``in_bounds`` returns False when crop crossed the 4 edges. +--+ | | ### | => (4, 6) image, (3,2) crop ### | | | +--+ +--+ | | | ### => (4, 6) image, (3,2) crop | ### | | +--+ ## +##+ |##| | | => (4, 6) image, (2,3) crop | | | | +--+ +--+ | | | | => (4, 6) image, (2,3) crop | | |##| +##+ ## """ bb = AxisAlignedBoundingBox([-1, 2], [2, 4]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([2, 2], [5, 4]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([1, -1], [3, 2]) assert not crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([1, 4], [3, 7]) assert not crop_in_bounds(bb, 4, 6)
def test_in_bounds_inside_edges(self): """ Test that a crop is "in bounds" when contacting the 4 edges of the given rectangular bounds. +--+ | | ## | => (4, 6) image, (2,2) crop ## | | | +--+ +##+ |##| | | => (4, 6) image, (2,2) crop | | | | +--+ +--+ | | | ## => (4, 6) image, (2,2) crop | ## | | +--+ +--+ | | | | => (4, 6) image, (2,2) crop | | |##| +##+ """ # noinspection PyArgumentList bb = AxisAlignedBoundingBox([0, 2], [2, 4]) assert crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([1, 0], [3, 2]) assert crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([2, 2], [4, 4]) assert crop_in_bounds(bb, 4, 6) bb = AxisAlignedBoundingBox([1, 4], [3, 6]) assert crop_in_bounds(bb, 4, 6)
def setUpClass(cls): cls.gh_image_fp = os.path.join(TEST_DATA_DIR, "grace_hopper.png") cls.gh_file_element = DataFileElement(cls.gh_image_fp) assert cls.gh_file_element.content_type() == 'image/png' cls.gh_cropped_image_fp = \ os.path.join(TEST_DATA_DIR, 'grace_hopper.100x100+100+100.png') cls.gh_cropped_file_element = DataFileElement(cls.gh_cropped_image_fp) assert cls.gh_cropped_file_element.content_type() == 'image/png' cls.gh_cropped_bbox = AxisAlignedBoundingBox([100, 100], [200, 200])
def test_load_as_matrix_with_crop_not_in_bounds(self): """ Test that error is raised when crop bbox is not fully within the image bounds. """ inst = PilImageReader() # Nowhere close bb = AxisAlignedBoundingBox([5000, 6000], [7000, 8000]) with pytest.raises(RuntimeError, match=r"Crop provided not within input image\. " r"Image shape: \(512, 600\), crop: "): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb) # Outside left side bb = AxisAlignedBoundingBox([-1, 1], [2, 2]) with pytest.raises(RuntimeError, match=r"Crop provided not within input image\. " r"Image shape: \(512, 600\), crop: "): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb) # Outside top side bb = AxisAlignedBoundingBox([1, -1], [2, 2]) with pytest.raises(RuntimeError, match=r"Crop provided not within input image\. " r"Image shape: \(512, 600\), crop: "): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb) # Outside right side bb = AxisAlignedBoundingBox([400, 400], [513, 600]) with pytest.raises(RuntimeError, match=r"Crop provided not within input image\. " r"Image shape: \(512, 600\), crop: "): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb) # Outside bottom side bb = AxisAlignedBoundingBox([400, 400], [512, 601]) with pytest.raises(RuntimeError, match=r"Crop provided not within input image\. " r"Image shape: \(512, 600\), crop: "): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb)
def test_load_as_matrix_with_crop_not_integer(self): """ Test passing a bounding box that is not integer aligned, which should raise an error in the super call. """ inst = PilImageReader() bb = AxisAlignedBoundingBox([100, 100.6], [200, 200.2]) with pytest.raises(ValueError, match=r"Crop bounding box must be " r"composed of integer " r"coordinates\."): inst.load_as_matrix(self.gh_file_element, pixel_crop=bb)
def test_load_as_matrix_crop_zero_volume(): """ Test that a ValueError is raised when a crop bbox is passed with zero volume. """ m_reader = mock.MagicMock(spec=ImageReader) m_data = mock.MagicMock(spec_set=DataElement) crop_bb = AxisAlignedBoundingBox([0, 0], [0, 0]) with pytest.raises(ValueError, match=r"Volume of crop bounding box must be " r"greater than 0\."): ImageReader.load_as_matrix(m_reader, m_data, pixel_crop=crop_bb)
def test_in_bounds_inside(self): """ Test that ``in_bounds`` passes when crop inside given rectangle bounds. +--+ | | |##| => (4, 6) image, (2,2) crop |##| | | +--+ """ bb = AxisAlignedBoundingBox([1, 2], [3, 4]) assert crop_in_bounds(bb, 4, 8)
def test_gen_detection_uuid_perturbations(): """ Test that some minor variances in input data We obviously cannot check everything, so this is just a basic test. """ # Arbitrary experimental input values t_uuid = "SomeDataElementChecksumUUID" t_bbox = AxisAlignedBoundingBox([2.6, 82], [98, 83.7]) t_labels = ('l1', 'l2', 42) initial_uuid = ObjectDetector._gen_detection_uuid(t_uuid, t_bbox, t_labels) # perturb t_uuid assert initial_uuid != ObjectDetector._gen_detection_uuid( t_uuid[:-1]+'.', t_bbox, t_labels), \ "t_uuid perturbation resulted in same UUID" # perturb t_bbox assert initial_uuid != ObjectDetector._gen_detection_uuid( t_uuid, AxisAlignedBoundingBox([2.6, 82.1], [98, 83.7]), t_labels), \ "t_bbox perturbation resulted in same UUID" assert initial_uuid != ObjectDetector._gen_detection_uuid( t_uuid, t_bbox, ('l1', 'l3', 42)), \ "t_labels perturbation resulted in same UUID"
def setup_module(): # Initialize test image paths/elements/associated crop boxes. global GH_IMAGE_FP, GH_FILE_ELEMENT, GH_CROPPED_IMAGE_FP, \ GH_CROPPED_FILE_ELEMENT, GH_CROPPED_BBOX GH_IMAGE_FP = os.path.join(TEST_DATA_DIR, "grace_hopper.png") GH_FILE_ELEMENT = DataFileElement(GH_IMAGE_FP, readonly=True) assert GH_FILE_ELEMENT.content_type() == 'image/png' GH_CROPPED_IMAGE_FP = \ os.path.join(TEST_DATA_DIR, 'grace_hopper.100x100+100+100.png') GH_CROPPED_FILE_ELEMENT = DataFileElement(GH_CROPPED_IMAGE_FP, readonly=True) assert GH_CROPPED_FILE_ELEMENT.content_type() == 'image/png' GH_CROPPED_BBOX = AxisAlignedBoundingBox([100, 100], [200, 200])
def test_load_as_matrix_success(): """ Test successfully passing ``load_as_matrix`` and invoking implementation defined ``_load_as_matrix`` method (no ``matrix`` property on data elem). """ m_elem = mock.MagicMock(spec_set=DataElement) crop_bb = AxisAlignedBoundingBox([0, 0], [1, 1]) m_reader = mock.MagicMock(spec=ImageReader) m_reader._get_matrix_property = \ mock.MagicMock(wraps=ImageReader._get_matrix_property) m_reader._load_as_matrix = mock.MagicMock() ImageReader.load_as_matrix(m_reader, m_elem, pixel_crop=crop_bb) m_reader._load_as_matrix.assert_called_once_with(m_elem, pixel_crop=crop_bb)
def test_load_as_matrix_crop_not_integer(): """ Test that a ValueError is raised when the pixel crop bbox provided does not report an integer type as its dtype. """ m_reader = mock.MagicMock(spec=ImageReader) m_data = mock.MagicMock(spec_set=DataElement) # Create bbox with floats. crop_bb = AxisAlignedBoundingBox([0.0, 0.0], [1.0, 1.0]) with pytest.raises(ValueError, match=r"Crop bounding box must be composed of integer " r"coordinates\. Given bounding box with dtype " r".+\."): ImageReader.load_as_matrix(m_reader, m_data, pixel_crop=crop_bb)