# Copyright 2022 The KerasCV Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import tensorflow as tf from keras_cv import bounding_box class BoundingBoxUtilTestCase(tf.test.TestCase): def test_clip_to_image(self): # Test xyxy format unbatched height = 256 width = 256 bboxes = tf.convert_to_tensor( [[200, 200, 400, 400, 0], [100, 100, 300, 300, 0]] ) image = tf.ones(shape=(height, width, 3)) bboxes_out = bounding_box.clip_to_image( bboxes, bounding_box_format="xyxy", images=image ) self.assertAllGreaterEqual(bboxes_out, 0) x1, y1, x2, y2, rest = tf.split(bboxes_out, [1, 1, 1, 1, -1], axis=1) self.assertAllLessEqual([x1, x2], width) self.assertAllLessEqual([y1, y2], height) # Test relative format batched image = tf.ones(shape=(1, height, width, 3)) bboxes = tf.convert_to_tensor( [[[0.2, -1, 1.2, 0.3, 0], [0.4, 1.5, 0.2, 0.3, 0]]] ) bboxes_out = bounding_box.clip_to_image( bboxes, bounding_box_format="rel_xyxy", images=image ) self.assertAllLessEqual(bboxes_out, 1) def test_clip_to_image_filters_fully_out_bounding_boxes(self): # Test xyxy format unbatched height = 256 width = 256 bounding_boxes = tf.convert_to_tensor( [[257, 257, 400, 400, 0], [100, 100, 300, 300, 0]] ) image = tf.ones(shape=(height, width, 3)) bounding_boxes = bounding_box.clip_to_image( bounding_boxes, bounding_box_format="xyxy", images=image ) self.assertAllEqual( bounding_boxes, tf.convert_to_tensor([[-1, -1, -1, -1, -1], [100, 100, 256, 256, 0]]), ) def test_clip_to_image_filters_fully_out_bounding_boxes_negative_area(self): # Test xyxy format unbatched height = 256 width = 256 bounding_boxes = tf.convert_to_tensor( [[0, float("NaN"), 100, 100, 0], [100, 100, 300, 300, 0]] ) image = tf.ones(shape=(height, width, 3)) bounding_boxes = bounding_box.clip_to_image( bounding_boxes, bounding_box_format="xyxy", images=image ) self.assertAllEqual( bounding_boxes, tf.convert_to_tensor([[-1, -1, -1, -1, -1], [100, 100, 256, 256, 0]]), ) def test_clip_to_image_filters_nans(self): # Test xyxy format unbatched height = 256 width = 256 bounding_boxes = tf.convert_to_tensor( [[257, 257, 100, 100, 0], [100, 100, 300, 300, 0]] ) image = tf.ones(shape=(height, width, 3)) bounding_boxes = bounding_box.clip_to_image( bounding_boxes, bounding_box_format="xyxy", images=image ) self.assertAllEqual( bounding_boxes, tf.convert_to_tensor([[-1, -1, -1, -1, -1], [100, 100, 256, 256, 0]]), ) def test_pad_with_sentinels(self): bounding_boxes = tf.ragged.constant( [[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], [[1, 2, 3, 4, 5]]] ) padded_bounding_boxes = bounding_box.pad_with_sentinels(bounding_boxes) expected_output = tf.constant( [ [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], [[1, 2, 3, 4, 5], [-1, -1, -1, -1, -1]], ] ) self.assertAllEqual(padded_bounding_boxes, expected_output) def test_filter_sentinels(self): bounding_boxes = tf.ragged.constant( [[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, -1]], [[1, 2, 3, 4, 5]]] ) filtered_bounding_boxes = bounding_box.filter_sentinels(bounding_boxes) expected_output = tf.ragged.constant( [[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], [[1, 2, 3, 4, 5]]], ragged_rank=1 ) self.assertAllEqual(filtered_bounding_boxes, expected_output) def test_filter_sentinels_unbatched(self): bounding_boxes = tf.convert_to_tensor( [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, -1]] ) filtered_bounding_boxes = bounding_box.filter_sentinels(bounding_boxes) expected_output = tf.convert_to_tensor( [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], ) print(filtered_bounding_boxes, expected_output) self.assertAllEqual(filtered_bounding_boxes, expected_output) def test_filter_sentinels_tensor(self): bounding_boxes = tf.convert_to_tensor( [ [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], [[1, 2, 3, 4, 5], [1, 2, 3, 4, -1]], ] ) filtered_bounding_boxes = bounding_box.filter_sentinels(bounding_boxes) expected_output = tf.ragged.constant( [[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]], [[1, 2, 3, 4, 5]]], ragged_rank=1 ) self.assertAllEqual(filtered_bounding_boxes, expected_output) def test_pad_with_class_id_ragged(self): bounding_boxes = tf.ragged.constant( [[[1, 2, 3, 4], [1, 2, 3, 4]], [[1, 2, 3, 4]]] ) padded_bounding_boxes = bounding_box.add_class_id(bounding_boxes) expected_output = tf.ragged.constant( [[[1, 2, 3, 4, 0], [1, 2, 3, 4, 0]], [[1, 2, 3, 4, 0]]] ) self.assertAllEqual(padded_bounding_boxes, expected_output) def test_pad_with_class_id_unbatched(self): bounding_boxes = tf.convert_to_tensor([[1, 2, 3, 4], [1, 2, 3, 4]]) padded_bounding_boxes = bounding_box.add_class_id(bounding_boxes) expected_output = tf.convert_to_tensor([[1, 2, 3, 4, 0], [1, 2, 3, 4, 0]]) self.assertAllEqual(padded_bounding_boxes, expected_output) def test_pad_with_class_id_exists(self): bounding_boxes = tf.ragged.constant( [[[1, 2, 3, 4, 0], [1, 2, 3, 4, 0]], [[1, 2, 3, 4, 0]]] ) with self.assertRaisesRegex( ValueError, "The number of values along the final axis of `bounding_boxes` is " "expected to be 4. But got 5.", ): bounding_box.add_class_id(bounding_boxes) def test_pad_with_class_id_wrong_rank(self): bounding_boxes = tf.ragged.constant( [[[[1, 2, 3, 4], [1, 2, 3, 4]], [[1, 2, 3, 4]]]] ) with self.assertRaisesRegex( ValueError, f"`bounding_boxes` should be of rank 2 or 3. However " f"add_class_id received `bounding_boxes` of rank={4}", ): bounding_box.add_class_id(bounding_boxes)