diff --git a/tests/ut/data/dataset/golden/bounding_box_augment_crop_c_result.npz b/tests/ut/data/dataset/golden/bounding_box_augment_crop_c_result.npz new file mode 100644 index 0000000000..e4e92210d7 Binary files /dev/null and b/tests/ut/data/dataset/golden/bounding_box_augment_crop_c_result.npz differ diff --git a/tests/ut/data/dataset/golden/bounding_box_augment_rotation_c_result.npz b/tests/ut/data/dataset/golden/bounding_box_augment_rotation_c_result.npz new file mode 100644 index 0000000000..8cc7e15e31 Binary files /dev/null and b/tests/ut/data/dataset/golden/bounding_box_augment_rotation_c_result.npz differ diff --git a/tests/ut/data/dataset/golden/bounding_box_augment_valid_edge_c_result.npz b/tests/ut/data/dataset/golden/bounding_box_augment_valid_edge_c_result.npz new file mode 100644 index 0000000000..dafea520fe Binary files /dev/null and b/tests/ut/data/dataset/golden/bounding_box_augment_valid_edge_c_result.npz differ diff --git a/tests/ut/data/dataset/golden/bounding_box_augment_valid_ratio_c_result.npz b/tests/ut/data/dataset/golden/bounding_box_augment_valid_ratio_c_result.npz new file mode 100644 index 0000000000..71e58406ac Binary files /dev/null and b/tests/ut/data/dataset/golden/bounding_box_augment_valid_ratio_c_result.npz differ diff --git a/tests/ut/data/dataset/golden/random_horizontal_flip_with_bbox_01_c_result.npz b/tests/ut/data/dataset/golden/random_horizontal_flip_with_bbox_01_c_result.npz new file mode 100644 index 0000000000..d360bb98ec Binary files /dev/null and b/tests/ut/data/dataset/golden/random_horizontal_flip_with_bbox_01_c_result.npz differ diff --git a/tests/ut/python/dataset/test_bounding_box_augment.py b/tests/ut/python/dataset/test_bounding_box_augment.py index 2b0d89239f..fcdfbe7928 100644 --- a/tests/ut/python/dataset/test_bounding_box_augment.py +++ b/tests/ut/python/dataset/test_bounding_box_augment.py @@ -15,92 +15,18 @@ """ Testing the bounding box augment op in DE """ -from enum import Enum +from util import visualize_with_bounding_boxes, InvalidBBoxType, check_bad_bbox, \ + config_get_set_seed, config_get_set_num_parallel_workers, save_and_check_md5 +import numpy as np import mindspore.log as logger import mindspore.dataset as ds import mindspore.dataset.transforms.vision.c_transforms as c_vision -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import numpy as np GENERATE_GOLDEN = False DATA_DIR = "../data/dataset/testVOC2012_2" -class BoxType(Enum): - """ - Defines box types for test cases - """ - WidthOverflow = 1 - HeightOverflow = 2 - NegativeXY = 3 - OnEdge = 4 - WrongShape = 5 - - -def add_bad_annotation(img, bboxes, box_type): - """ - Used to generate erroneous bounding box examples on given img. - :param img: image where the bounding boxes are. - :param bboxes: in [x_min, y_min, w, h, label, truncate, difficult] format - :param box_type: type of bad box - :return: bboxes with bad examples added - """ - height = img.shape[0] - width = img.shape[1] - if box_type == BoxType.WidthOverflow: - # use box that overflows on width - return img, np.array([[0, 0, width + 1, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.HeightOverflow: - # use box that overflows on height - return img, np.array([[0, 0, width, height + 1, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.NegativeXY: - # use box with negative xy - return img, np.array([[-10, -10, width, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.OnEdge: - # use box that covers the whole image - return img, np.array([[0, 0, width, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.WrongShape: - # use box that covers the whole image - return img, np.array([[0, 0, width - 1]]).astype(np.uint32) - return img, bboxes - - -def check_bad_box(data, box_type, expected_error): - """ - :param data: de object detection pipeline - :param box_type: type of bad box - :param expected_error: error expected to get due to bad box - :return: None - """ - try: - test_op = c_vision.BoundingBoxAugment(c_vision.RandomHorizontalFlip(1), - 1) # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) - data = data.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - # map to use width overflow - data = data.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=lambda img, bboxes: add_bad_annotation(img, bboxes, box_type)) - # map to apply ops - data = data.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - for _, _ in enumerate(data.create_dict_iterator()): - break - except RuntimeError as error: - logger.info("Got an exception in DE: {}".format(str(error))) - assert expected_error in str(error) - - def fix_annotate(bboxes): """ Fix annotations to format followed by mindspore. @@ -117,153 +43,217 @@ def fix_annotate(bboxes): return bboxes -def add_bounding_boxes(axis, bboxes): - """ - :param axis: axis to modify - :param bboxes: bounding boxes to draw on the axis - :return: None - """ - for bbox in bboxes: - rect = patches.Rectangle((bbox[0], bbox[1]), - bbox[2], bbox[3], - linewidth=1, edgecolor='r', facecolor='none') - # Add the patch to the Axes - axis.add_patch(rect) - - -def visualize(unaugmented_data, augment_data): +def test_bounding_box_augment_with_rotation_op(plot_vis=False): """ - :param unaugmented_data: original data - :param augment_data: data after augmentations - :return: None - """ - for idx, (un_aug_item, aug_item) in \ - enumerate(zip(unaugmented_data.create_dict_iterator(), - augment_data.create_dict_iterator())): - axis = plt.subplot(141) - plt.imshow(un_aug_item["image"]) - add_bounding_boxes(axis, un_aug_item["annotation"]) # add Orig BBoxes - plt.title("Original" + str(idx + 1)) - logger.info("Original ", str(idx + 1), " :", un_aug_item["annotation"]) - - axis = plt.subplot(142) - plt.imshow(aug_item["image"]) - add_bounding_boxes(axis, aug_item["annotation"]) # add AugBBoxes - plt.title("Augmented" + str(idx + 1)) - logger.info("Augmented ", str(idx + 1), " ", aug_item["annotation"], "\n") - plt.show() - - -def test_bounding_box_augment_with_rotation_op(plot=False): - """ - Test BoundingBoxAugment op + Test BoundingBoxAugment op (passing rotation op as transform) Prints images side by side with and without Aug applied + bboxes to compare and test """ logger.info("test_bounding_box_augment_with_rotation_op") - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + original_seed = config_get_set_seed(0) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + # Ratio is set to 1 to apply rotation on all bounding boxes. test_op = c_vision.BoundingBoxAugment(c_vision.RandomRotation(90), 1) - # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - if plot: - visualize(data_voc1, data_voc2) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + filename = "bounding_box_augment_rotation_c_result.npz" + save_and_check_md5(dataVoc2, filename, generate_golden=GENERATE_GOLDEN) -def test_bounding_box_augment_with_crop_op(plot=False): + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + # Restore config setting + ds.config.set_seed(original_seed) + ds.config.set_num_parallel_workers(original_num_parallel_workers) + + +def test_bounding_box_augment_with_crop_op(plot_vis=False): """ - Test BoundingBoxAugment op + Test BoundingBoxAugment op (passing crop op as transform) Prints images side by side with and without Aug applied + bboxes to compare and test """ logger.info("test_bounding_box_augment_with_crop_op") - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + original_seed = config_get_set_seed(1) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + # Ratio is set to 1 to apply rotation on all bounding boxes. test_op = c_vision.BoundingBoxAugment(c_vision.RandomCrop(90), 1) # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - if plot: - visualize(data_voc1, data_voc2) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + filename = "bounding_box_augment_crop_c_result.npz" + save_and_check_md5(dataVoc2, filename, generate_golden=GENERATE_GOLDEN) + unaugSamp, augSamp = [], [] -def test_bounding_box_augment_valid_ratio_c(plot=False): + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + # Restore config setting + ds.config.set_seed(original_seed) + ds.config.set_num_parallel_workers(original_num_parallel_workers) + + +def test_bounding_box_augment_valid_ratio_c(plot_vis=False): """ - Test RandomHorizontalFlipWithBBox op + Test BoundingBoxAugment op (testing with valid ratio, less than 1. Prints images side by side with and without Aug applied + bboxes to compare and test """ logger.info("test_bounding_box_augment_valid_ratio_c") - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + original_seed = config_get_set_seed(1) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) test_op = c_vision.BoundingBoxAugment(c_vision.RandomHorizontalFlip(1), 0.9) - # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) # Add column for "annotation" + filename = "bounding_box_augment_valid_ratio_c_result.npz" + save_and_check_md5(dataVoc2, filename, generate_golden=GENERATE_GOLDEN) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + # Restore config setting + ds.config.set_seed(original_seed) + ds.config.set_num_parallel_workers(original_num_parallel_workers) + + +def test_bounding_box_augment_valid_edge_c(plot_vis=False): + """ + Test BoundingBoxAugment op (testing with valid edge case, box covering full image). + Prints images side by side with and without Aug applied + bboxes to compare and test + """ + logger.info("test_bounding_box_augment_valid_edge_c") + + original_seed = config_get_set_seed(1) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + + test_op = c_vision.BoundingBoxAugment(c_vision.RandomHorizontalFlip(1), 1) + + # maps to fix annotations to minddata standard + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - if plot: - visualize(data_voc1, data_voc2) + # Add column for "annotation" + dataVoc1 = dataVoc1.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=lambda img, bbox: + (img, np.array([[0, 0, img.shape[1], img.shape[0], 0, 0, 0]]).astype(np.uint32))) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=lambda img, bbox: + (img, np.array([[0, 0, img.shape[1], img.shape[0], 0, 0, 0]]).astype(np.uint32))) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + filename = "bounding_box_augment_valid_edge_c_result.npz" + save_and_check_md5(dataVoc2, filename, generate_golden=GENERATE_GOLDEN) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + # Restore config setting + ds.config.set_seed(original_seed) + ds.config.set_num_parallel_workers(original_num_parallel_workers) def test_bounding_box_augment_invalid_ratio_c(): """ - Test RandomHorizontalFlipWithBBox op with invalid input probability + Test BoundingBoxAugment op with invalid input ratio """ logger.info("test_bounding_box_augment_invalid_ratio_c") - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) try: # ratio range is from 0 - 1 test_op = c_vision.BoundingBoxAugment(c_vision.RandomHorizontalFlip(1), 1.5) # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) # Add column for "annotation" except ValueError as error: logger.info("Got an exception in DE: {}".format(str(error))) assert "Input is not" in str(error) @@ -275,20 +265,24 @@ def test_bounding_box_augment_invalid_bounds_c(): """ logger.info("test_bounding_box_augment_invalid_bounds_c") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.WidthOverflow, "bounding boxes is out of bounds of the image") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.HeightOverflow, "bounding boxes is out of bounds of the image") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.NegativeXY, "min_x") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.WrongShape, "4 features") + test_op = c_vision.BoundingBoxAugment(c_vision.RandomHorizontalFlip(1), + 1) + + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.WidthOverflow, "bounding boxes is out of bounds of the image") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.HeightOverflow, "bounding boxes is out of bounds of the image") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.NegativeXY, "min_x") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.WrongShape, "4 features") if __name__ == "__main__": # set to false to not show plots - test_bounding_box_augment_with_rotation_op(False) - test_bounding_box_augment_with_crop_op(False) - test_bounding_box_augment_valid_ratio_c(False) + test_bounding_box_augment_with_rotation_op(plot_vis=False) + test_bounding_box_augment_with_crop_op(plot_vis=False) + test_bounding_box_augment_valid_ratio_c(plot_vis=False) + test_bounding_box_augment_valid_edge_c(plot_vis=False) test_bounding_box_augment_invalid_ratio_c() test_bounding_box_augment_invalid_bounds_c() diff --git a/tests/ut/python/dataset/test_random_horizontal_flip_bbox.py b/tests/ut/python/dataset/test_random_horizontal_flip_bbox.py deleted file mode 100644 index f7f14fd160..0000000000 --- a/tests/ut/python/dataset/test_random_horizontal_flip_bbox.py +++ /dev/null @@ -1,266 +0,0 @@ -# Copyright 2020 Huawei Technologies Co., Ltd -# -# 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 -# -# http://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. -# ============================================================================== -""" -Testing the random horizontal flip with bounding boxes op in DE -""" -from enum import Enum -import matplotlib.pyplot as plt -import matplotlib.patches as patches -import numpy as np -import mindspore.log as logger -import mindspore.dataset as ds -import mindspore.dataset.transforms.vision.c_transforms as c_vision - -GENERATE_GOLDEN = False - -DATA_DIR = "../data/dataset/testVOC2012_2" - - -class BoxType(Enum): - """ - Defines box types for test cases - """ - WidthOverflow = 1 - HeightOverflow = 2 - NegativeXY = 3 - OnEdge = 4 - WrongShape = 5 - -def add_bad_annotation(img, bboxes, box_type): - """ - Used to generate erroneous bounding box examples on given img. - :param img: image where the bounding boxes are. - :param bboxes: in [x_min, y_min, w, h, label, truncate, difficult] format - :param box_type: type of bad box - :return: bboxes with bad examples added - """ - height = img.shape[0] - width = img.shape[1] - if box_type == BoxType.WidthOverflow: - # use box that overflows on width - return img, np.array([[0, 0, width + 1, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.HeightOverflow: - # use box that overflows on height - return img, np.array([[0, 0, width, height + 1, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.NegativeXY: - # use box with negative xy - return img, np.array([[-10, -10, width, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.OnEdge: - # use box that covers the whole image - return img, np.array([[0, 0, width, height, 0, 0, 0]]).astype(np.uint32) - - if box_type == BoxType.WrongShape: - # use box that covers the whole image - return img, np.array([[0, 0, width - 1]]).astype(np.uint32) - return img, bboxes - - -def h_flip(image): - """ - Apply the random_horizontal - """ - # that's why we flip here too - image = image[:, ::-1, :] - return image - - -def check_bad_box(data, box_type, expected_error): - """ - :param data: de object detection pipeline - :param box_type: type of bad box - :param expected_error: error expected to get due to bad box - :return: None - """ - # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) - try: - test_op = c_vision.RandomHorizontalFlipWithBBox(1) - data = data.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - # map to use width overflow - data = data.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=lambda img, bboxes: add_bad_annotation(img, bboxes, box_type)) - # map to apply ops - data = data.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - for _, _ in enumerate(data.create_dict_iterator()): - break - except RuntimeError as error: - logger.info("Got an exception in DE: {}".format(str(error))) - assert expected_error in str(error) - - -def fix_annotate(bboxes): - """ - Fix annotations to format followed by mindspore. - :param bboxes: in [label, x_min, y_min, w, h, truncate, difficult] format - :return: annotation in [x_min, y_min, w, h, label, truncate, difficult] format - """ - for bbox in bboxes: - tmp = bbox[0] - bbox[0] = bbox[1] - bbox[1] = bbox[2] - bbox[2] = bbox[3] - bbox[3] = bbox[4] - bbox[4] = tmp - return bboxes - - -def add_bounding_boxes(axis, bboxes): - """ - :param axis: axis to modify - :param bboxes: bounding boxes to draw on the axis - :return: None - """ - for bbox in bboxes: - rect = patches.Rectangle((bbox[0], bbox[1]), - bbox[2], bbox[3], - linewidth=1, edgecolor='r', facecolor='none') - # Add the patch to the Axes - axis.add_patch(rect) - - -def visualize(unaugmented_data, augment_data): - """ - :param unaugmented_data: original data - :param augment_data: data after augmentations - :return: None - """ - for idx, (un_aug_item, aug_item) in \ - enumerate(zip(unaugmented_data.create_dict_iterator(), - augment_data.create_dict_iterator())): - axis = plt.subplot(141) - plt.imshow(un_aug_item["image"]) - add_bounding_boxes(axis, un_aug_item["annotation"]) # add Orig BBoxes - plt.title("Original" + str(idx + 1)) - logger.info("Original ", str(idx + 1), " :", un_aug_item["annotation"]) - - axis = plt.subplot(142) - plt.imshow(aug_item["image"]) - add_bounding_boxes(axis, aug_item["annotation"]) # add AugBBoxes - plt.title("Augmented" + str(idx + 1)) - logger.info("Augmented ", str(idx + 1), " ", aug_item["annotation"], "\n") - plt.show() - - -def test_random_horizontal_bbox_op(plot=False): - """ - Test RandomHorizontalFlipWithBBox op - Prints images side by side with and without Aug applied + bboxes to compare and test - """ - logger.info("test_random_horizontal_bbox_c") - - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - - # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) - test_op = c_vision.RandomHorizontalFlipWithBBox(1) - - # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - if plot: - visualize(data_voc1, data_voc2) - - -def test_random_horizontal_bbox_valid_prob_c(plot=False): - """ - Test RandomHorizontalFlipWithBBox op - Prints images side by side with and without Aug applied + bboxes to compare and test - """ - logger.info("test_random_horizontal_bbox_valid_prob_c") - - data_voc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - # DEFINE TEST OP HERE -- (PROB 1 IN CASE OF RANDOM) - test_op = c_vision.RandomHorizontalFlipWithBBox(0.3) - - # maps to fix annotations to minddata standard - data_voc1 = data_voc1.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - if plot: - visualize(data_voc1, data_voc2) - - -def test_random_horizontal_bbox_invalid_prob_c(): - """ - Test RandomHorizontalFlipWithBBox op with invalid input probability - """ - logger.info("test_random_horizontal_bbox_invalid_prob_c") - - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - - try: - # Note: Valid range of prob should be [0.0, 1.0] - test_op = c_vision.RandomHorizontalFlipWithBBox(1.5) - data_voc2 = data_voc2.map(input_columns=["annotation"], - output_columns=["annotation"], - operations=fix_annotate) - # map to apply ops - data_voc2 = data_voc2.map(input_columns=["image", "annotation"], - output_columns=["image", "annotation"], - columns_order=["image", "annotation"], - operations=[test_op]) # Add column for "annotation" - except ValueError as error: - logger.info("Got an exception in DE: {}".format(str(error))) - assert "Input is not" in str(error) - - -def test_random_horizontal_bbox_invalid_bounds_c(): - """ - Test RandomHorizontalFlipWithBBox op with invalid bounding boxes - """ - logger.info("test_random_horizontal_bbox_invalid_bounds_c") - - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.WidthOverflow, "bounding boxes is out of bounds of the image") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.HeightOverflow, "bounding boxes is out of bounds of the image") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.NegativeXY, "min_x") - data_voc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) - check_bad_box(data_voc2, BoxType.WrongShape, "4 features") - -if __name__ == "__main__": - # set to false to not show plots - test_random_horizontal_bbox_op(False) - test_random_horizontal_bbox_valid_prob_c(False) - test_random_horizontal_bbox_invalid_prob_c() - test_random_horizontal_bbox_invalid_bounds_c() diff --git a/tests/ut/python/dataset/test_random_horizontal_flip_with_bbox.py b/tests/ut/python/dataset/test_random_horizontal_flip_with_bbox.py new file mode 100644 index 0000000000..37e9fa710f --- /dev/null +++ b/tests/ut/python/dataset/test_random_horizontal_flip_with_bbox.py @@ -0,0 +1,229 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# 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 +# +# http://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. +# ============================================================================== +""" +Testing the random horizontal flip with bounding boxes op in DE +""" +import numpy as np +import mindspore.log as logger +import mindspore.dataset as ds +import mindspore.dataset.transforms.vision.c_transforms as c_vision +from util import visualize_with_bounding_boxes, InvalidBBoxType, check_bad_bbox, \ + config_get_set_seed, config_get_set_num_parallel_workers, save_and_check_md5 + +GENERATE_GOLDEN = False + +DATA_DIR = "../data/dataset/testVOC2012_2" + + +def fix_annotate(bboxes): + """ + Fix annotations to format followed by mindspore. + :param bboxes: in [label, x_min, y_min, w, h, truncate, difficult] format + :return: annotation in [x_min, y_min, w, h, label, truncate, difficult] format + """ + for bbox in bboxes: + tmp = bbox[0] + bbox[0] = bbox[1] + bbox[1] = bbox[2] + bbox[2] = bbox[3] + bbox[3] = bbox[4] + bbox[4] = tmp + return bboxes + + +def test_random_horizontal_flip_with_bbox_op_c(plot_vis=False): + """ + Prints images side by side with and without Aug applied + bboxes to + compare and test + """ + logger.info("test_random_horizontal_flip_with_bbox_op_c") + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", + decode=True, shuffle=False) + + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", + decode=True, shuffle=False) + + test_op = c_vision.RandomHorizontalFlipWithBBox(1) + + # maps to fix annotations to minddata standard + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + +def test_random_horizontal_bbox_with_bbox_valid_rand_c(plot_vis=False): + """ + Uses a valid non-default input, expect to pass + Prints images side by side with and without Aug applied + bboxes to + compare and test + """ + logger.info("test_random_horizontal_bbox_valid_rand_c") + + original_seed = config_get_set_seed(1) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", + decode=True, shuffle=False) + + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", + decode=True, shuffle=False) + + test_op = c_vision.RandomHorizontalFlipWithBBox(0.6) + + # maps to fix annotations to minddata standard + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + filename = "random_horizontal_flip_with_bbox_01_c_result.npz" + save_and_check_md5(dataVoc2, filename, generate_golden=GENERATE_GOLDEN) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + # Restore config setting + ds.config.set_seed(original_seed) + ds.config.set_num_parallel_workers(original_num_parallel_workers) + + +def test_random_horizontal_flip_with_bbox_valid_edge_c(plot_vis=False): + """ + Test RandomHorizontalFlipWithBBox op (testing with valid edge case, box covering full image). + Prints images side by side with and without Aug applied + bboxes to compare and test + """ + logger.info("test_horizontal_flip_with_bbox_valid_edge_c") + + dataVoc1 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + + test_op = c_vision.RandomHorizontalFlipWithBBox(1) + + # maps to fix annotations to minddata standard + dataVoc1 = dataVoc1.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + # map to apply ops + # Add column for "annotation" + dataVoc1 = dataVoc1.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=lambda img, bbox: + (img, np.array([[0, 0, img.shape[1], img.shape[0], 0, 0, 0]]).astype(np.uint32))) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=lambda img, bbox: + (img, np.array([[0, 0, img.shape[1], img.shape[0], 0, 0, 0]]).astype(np.uint32))) + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataVoc1.create_dict_iterator(), dataVoc2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp) + + +def test_random_horizontal_flip_with_bbox_invalid_prob_c(): + """ + Test RandomHorizontalFlipWithBBox op with invalid input probability + """ + logger.info("test_random_horizontal_bbox_invalid_prob_c") + + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + + try: + # Note: Valid range of prob should be [0.0, 1.0] + test_op = c_vision.RandomHorizontalFlipWithBBox(1.5) + dataVoc2 = dataVoc2.map(input_columns=["annotation"], + output_columns=["annotation"], + operations=fix_annotate) + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) # Add column for "annotation" + except ValueError as error: + logger.info("Got an exception in DE: {}".format(str(error))) + assert "Input is not" in str(error) + + +def test_random_horizontal_flip_with_bbox_invalid_bounds_c(): + """ + Test RandomHorizontalFlipWithBBox op with invalid bounding boxes + """ + logger.info("test_random_horizontal_bbox_invalid_bounds_c") + + test_op = c_vision.RandomHorizontalFlipWithBBox(1) + + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.WidthOverflow, "bounding boxes is out of bounds of the image") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.HeightOverflow, "bounding boxes is out of bounds of the image") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.NegativeXY, "min_x") + dataVoc2 = ds.VOCDataset(DATA_DIR, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(dataVoc2, test_op, InvalidBBoxType.WrongShape, "4 features") + + +if __name__ == "__main__": + # set to false to not show plots + test_random_horizontal_flip_with_bbox_op_c(plot_vis=False) + test_random_horizontal_bbox_with_bbox_valid_rand_c(plot_vis=False) + test_random_horizontal_flip_with_bbox_valid_edge_c(plot_vis=False) + test_random_horizontal_flip_with_bbox_invalid_prob_c() + test_random_horizontal_flip_with_bbox_invalid_bounds_c()