From 78c370f72afb47b0319085f5c5b1e8bb9d3b97ee Mon Sep 17 00:00:00 2001 From: Danish Farid Date: Tue, 7 Jul 2020 14:50:34 -0400 Subject: [PATCH] First Commit - New Infrastructure - Python UT tests + Common Aug Files fix accidental change overwrite fix 2 updated inits from 0 to 0.0 for float improvd python ut comments updated macro --- .../dataset/kernels/image/image_utils.cc | 64 ++--- .../random_vertical_flip_with_bbox_op.cc | 11 +- mindspore/ccsrc/dataset/kernels/tensor_op.h | 18 +- tests/ut/cpp/dataset/common/bboxop_common.cc | 25 +- .../random_crop_with_bbox_01_c_result.npz | Bin 1654 -> 1654 bytes ...dom_resized_crop_with_bbox_01_c_result.npz | Bin 1654 -> 1654 bytes ...om_vertical_flip_with_bbox_01_c_result.npz | Bin 1654 -> 1654 bytes .../test_random_crop_and_resize_with_bbox.py | 214 +++++++++++++++ .../dataset/test_random_crop_with_bbox.py | 249 ++++++++++++++++++ .../test_random_vertical_flip_with_bbox.py | 220 ++++++++++++++++ tests/ut/python/dataset/util.py | 24 +- 11 files changed, 751 insertions(+), 74 deletions(-) create mode 100644 tests/ut/python/dataset/test_random_crop_and_resize_with_bbox.py create mode 100644 tests/ut/python/dataset/test_random_crop_with_bbox.py create mode 100644 tests/ut/python/dataset/test_random_vertical_flip_with_bbox.py diff --git a/mindspore/ccsrc/dataset/kernels/image/image_utils.cc b/mindspore/ccsrc/dataset/kernels/image/image_utils.cc index 1a12620714..656e44c331 100644 --- a/mindspore/ccsrc/dataset/kernels/image/image_utils.cc +++ b/mindspore/ccsrc/dataset/kernels/image/image_utils.cc @@ -740,22 +740,16 @@ Status UpdateBBoxesForCrop(std::shared_ptr *bboxList, size_t *bboxCount, int CB_Ymax) { // PASS LIST, COUNT OF BOUNDING BOXES // Also PAss X/Y Min/Max of image cropped region - normally obtained from 'GetCropBox' functions - uint32_t bb_Xmin_t, bb_Ymin_t, bb_Xmax_t, bb_Ymax_t; - + float bb_Xmin = 0.0, bb_Ymin = 0.0, bb_Xmax = 0.0, bb_Ymax = 0.0; std::vector correct_ind; - std::vector copyVals; + std::vector copyVals; dsize_t bboxDim = (*bboxList)->shape()[1]; bool retFlag = false; // true unless overlap found for (int i = 0; i < *bboxCount; i++) { - int bb_Xmin, bb_Xmax, bb_Ymin, bb_Ymax; - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&bb_Xmin_t, {i, 0})); - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&bb_Ymin_t, {i, 1})); - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&bb_Xmax_t, {i, 2})); - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&bb_Ymax_t, {i, 3})); - bb_Xmin = bb_Xmin_t; - bb_Ymin = bb_Ymin_t; - bb_Xmax = bb_Xmax_t; - bb_Ymax = bb_Ymax_t; + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&bb_Xmin, {i, 0})); + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&bb_Ymin, {i, 1})); + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&bb_Xmax, {i, 2})); + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&bb_Ymax, {i, 3})); bb_Xmax = bb_Xmin + bb_Xmax; bb_Ymax = bb_Ymin + bb_Ymax; // check for image / BB overlap @@ -766,23 +760,23 @@ Status UpdateBBoxesForCrop(std::shared_ptr *bboxList, size_t *bboxCount, correct_ind.push_back(i); // adjust BBox corners by bringing into new CropBox if beyond // Also reseting/adjusting for boxes to lie within CropBox instead of Image - subtract CropBox Xmin/YMin - bb_Xmin = bb_Xmin - (std::min(0, (bb_Xmin - CB_Xmin)) + CB_Xmin); - bb_Xmax = bb_Xmax - (std::max(0, (bb_Xmax - CB_Xmax)) + CB_Xmin); - bb_Ymin = bb_Ymin - (std::min(0, (bb_Ymin - CB_Ymin)) + CB_Ymin); - bb_Ymax = bb_Ymax - (std::max(0, (bb_Ymax - CB_Ymax)) + CB_Ymin); + bb_Xmin = bb_Xmin - (std::min(static_cast(0.0), (bb_Xmin - CB_Xmin)) + CB_Xmin); + bb_Xmax = bb_Xmax - (std::max(static_cast(0.0), (bb_Xmax - CB_Xmax)) + CB_Xmin); + bb_Ymin = bb_Ymin - (std::min(static_cast(0.0), (bb_Ymin - CB_Ymin)) + CB_Ymin); + bb_Ymax = bb_Ymax - (std::max(static_cast(0.0), (bb_Ymax - CB_Ymax)) + CB_Ymin); // reset min values and calculate width/height from Box corners - RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 0}, static_cast(bb_Xmin))); - RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 1}, static_cast(bb_Ymin))); - RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 2}, static_cast(bb_Xmax - bb_Xmin))); - RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 3}, static_cast(bb_Ymax - bb_Ymin))); + RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 0}, bb_Xmin)); + RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 1}, bb_Ymin)); + RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 2}, bb_Xmax - bb_Xmin)); + RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 3}, bb_Ymax - bb_Ymin)); } // create new tensor and copy over bboxes still valid to the image // bboxes outside of new cropped region are ignored - empty tensor returned in case of none *bboxCount = correct_ind.size(); - uint32_t temp; + float temp = 0.0; for (auto slice : correct_ind) { // for every index in the loop for (int ix = 0; ix < bboxDim; ix++) { - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&temp, {slice, ix})); + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&temp, {slice, ix})); copyVals.push_back(temp); } } @@ -794,11 +788,11 @@ Status UpdateBBoxesForCrop(std::shared_ptr *bboxList, size_t *bboxCount, Status PadBBoxes(const std::shared_ptr *bboxList, const size_t &bboxCount, int32_t pad_top, int32_t pad_left) { for (int i = 0; i < bboxCount; i++) { - uint32_t xMin, yMin; - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&xMin, {i, 0})); - RETURN_IF_NOT_OK((*bboxList)->GetUnsignedIntAt(&yMin, {i, 1})); - xMin += static_cast(pad_left); // should not be negative - yMin += static_cast(pad_top); + float xMin = 0.0, yMin = 0.0; + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&xMin, {i, 0})); + RETURN_IF_NOT_OK((*bboxList)->GetItemAt(&yMin, {i, 1})); + xMin += pad_left; + yMin += pad_top; RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 0}, xMin)); RETURN_IF_NOT_OK((*bboxList)->SetItemAt({i, 1}, yMin)); } @@ -807,16 +801,16 @@ Status PadBBoxes(const std::shared_ptr *bboxList, const size_t &bboxCoun Status UpdateBBoxesForResize(const std::shared_ptr &bboxList, const size_t &bboxCount, int32_t target_width_, int32_t target_height_, int orig_width, int orig_height) { - uint32_t bb_Xmin, bb_Ymin, bb_Xwidth, bb_Ywidth; - // cast to float to preseve fractional - double W_aspRatio = (target_width_ * 1.0) / (orig_width * 1.0); - double H_aspRatio = (target_height_ * 1.0) / (orig_height * 1.0); + float bb_Xmin = 0, bb_Ymin = 0, bb_Xwidth = 0, bb_Ywidth = 0; + // cast to float to preserve fractional + float W_aspRatio = (target_width_ * 1.0) / (orig_width * 1.0); + float H_aspRatio = (target_height_ * 1.0) / (orig_height * 1.0); for (int i = 0; i < bboxCount; i++) { // for each bounding box - RETURN_IF_NOT_OK(bboxList->GetUnsignedIntAt(&bb_Xmin, {i, 0})); - RETURN_IF_NOT_OK(bboxList->GetUnsignedIntAt(&bb_Ymin, {i, 1})); - RETURN_IF_NOT_OK(bboxList->GetUnsignedIntAt(&bb_Xwidth, {i, 2})); - RETURN_IF_NOT_OK(bboxList->GetUnsignedIntAt(&bb_Ywidth, {i, 3})); + RETURN_IF_NOT_OK(bboxList->GetItemAt(&bb_Xmin, {i, 0})); + RETURN_IF_NOT_OK(bboxList->GetItemAt(&bb_Ymin, {i, 1})); + RETURN_IF_NOT_OK(bboxList->GetItemAt(&bb_Xwidth, {i, 2})); + RETURN_IF_NOT_OK(bboxList->GetItemAt(&bb_Ywidth, {i, 3})); // update positions and widths bb_Xmin = bb_Xmin * W_aspRatio; bb_Ymin = bb_Ymin * H_aspRatio; diff --git a/mindspore/ccsrc/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc b/mindspore/ccsrc/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc index ffea851eac..7e897536e8 100644 --- a/mindspore/ccsrc/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc +++ b/mindspore/ccsrc/dataset/kernels/image/random_vertical_flip_with_bbox_op.cc @@ -34,14 +34,13 @@ Status RandomVerticalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow * // one time allocation -> updated in the loop // type defined based on VOC test dataset for (int i = 0; i < boxCount; i++) { - uint32_t boxCorner_y = 0; - uint32_t boxHeight = 0; - uint32_t newBoxCorner_y = 0; - RETURN_IF_NOT_OK(input[1]->GetUnsignedIntAt(&boxCorner_y, {i, 1})); // get min y of bbox - RETURN_IF_NOT_OK(input[1]->GetUnsignedIntAt(&boxHeight, {i, 3})); // get height of bbox + float boxCorner_y = 0.0, boxHeight = 0.0; + float newBoxCorner_y = 0.0; + RETURN_IF_NOT_OK(input[1]->GetItemAt(&boxCorner_y, {i, 1})); // get min y of bbox + RETURN_IF_NOT_OK(input[1]->GetItemAt(&boxHeight, {i, 3})); // get height of bbox // subtract (curCorner + height) from (max) for new Corner position - newBoxCorner_y = (imHeight - 1) - ((boxCorner_y + boxHeight) - 1); + newBoxCorner_y = (imHeight - 1.0) - ((boxCorner_y + boxHeight) - 1.0); RETURN_IF_NOT_OK(input[1]->SetItemAt({i, 1}, newBoxCorner_y)); } diff --git a/mindspore/ccsrc/dataset/kernels/tensor_op.h b/mindspore/ccsrc/dataset/kernels/tensor_op.h index 9aae50d6b0..5be4592b39 100644 --- a/mindspore/ccsrc/dataset/kernels/tensor_op.h +++ b/mindspore/ccsrc/dataset/kernels/tensor_op.h @@ -62,14 +62,16 @@ uint32_t img_h = input[0]->shape()[0]; \ uint32_t img_w = input[0]->shape()[1]; \ for (uint32_t i = 0; i < num_of_boxes; i++) { \ - uint32_t min_x = 0; \ - uint32_t min_y = 0; \ - uint32_t b_w = 0; \ - uint32_t b_h = 0; \ - input[1]->GetItemAt(&min_x, {i, 0}); \ - input[1]->GetItemAt(&min_y, {i, 1}); \ - input[1]->GetItemAt(&b_w, {i, 2}); \ - input[1]->GetItemAt(&b_h, {i, 3}); \ + float min_x = 0.0, min_y = 0.0, b_w = 0.0, b_h = 0.0; \ + bool passing_data_fetch = true; \ + passing_data_fetch &= input[1]->GetItemAt(&min_x, {i, 0}).IsOk(); \ + passing_data_fetch &= input[1]->GetItemAt(&min_y, {i, 1}).IsOk(); \ + passing_data_fetch &= input[1]->GetItemAt(&b_w, {i, 2}).IsOk(); \ + passing_data_fetch &= input[1]->GetItemAt(&b_h, {i, 3}).IsOk(); \ + if (!passing_data_fetch) { \ + return Status(StatusCode::kUnexpectedError, __LINE__, __FILE__, \ + "Fetching BBox values failed in BOUNDING_BOX_CHECK."); \ + } \ if ((min_x + b_w > img_w) || (min_y + b_h > img_h)) { \ return Status(StatusCode::kBoundingBoxOutOfBounds, __LINE__, __FILE__, \ "At least one of the bounding boxes is out of bounds of the image."); \ diff --git a/tests/ut/cpp/dataset/common/bboxop_common.cc b/tests/ut/cpp/dataset/common/bboxop_common.cc index 70e6b5a339..edd457a82d 100644 --- a/tests/ut/cpp/dataset/common/bboxop_common.cc +++ b/tests/ut/cpp/dataset/common/bboxop_common.cc @@ -118,14 +118,11 @@ void BBoxOpCommon::SaveImagesWithAnnotations(BBoxOpCommon::FileType type, const bool passing_data_fetch = true; // For each bounding box draw on the image. for (uint32_t i = 0; i < num_of_boxes; i++) { - uint32_t x = 0; - uint32_t y = 0; - uint32_t w = 0; - uint32_t h = 0; - passing_data_fetch &= row[1]->GetUnsignedIntAt(&x, {i, 0}).IsOk(); - passing_data_fetch &= row[1]->GetUnsignedIntAt(&y, {i, 1}).IsOk(); - passing_data_fetch &= row[1]->GetUnsignedIntAt(&w, {i, 2}).IsOk(); - passing_data_fetch &= row[1]->GetUnsignedIntAt(&h, {i, 3}).IsOk(); + float x = 0.0, y = 0.0, w = 0.0, h = 0.0; + passing_data_fetch &= row[1]->GetItemAt(&x, {i, 0}).IsOk(); + passing_data_fetch &= row[1]->GetItemAt(&y, {i, 1}).IsOk(); + passing_data_fetch &= row[1]->GetItemAt(&w, {i, 2}).IsOk(); + passing_data_fetch &= row[1]->GetItemAt(&h, {i, 3}).IsOk(); if (!passing_data_fetch) { MS_LOG(ERROR) << "Fetching bbox coordinates failed in SaveImagesWithAnnotations."; EXPECT_TRUE(passing_data_fetch); @@ -193,24 +190,24 @@ bool BBoxOpCommon::LoadAnnotationFile(const std::string &path, std::shared_ptr return_value_list; + std::vector return_value_list; dsize_t bbox_count = 0; // keep track of number of bboxes in file dsize_t bbox_val_count = 4; // creating bboxes of size 4 to test function // FILE OK TO READ while (object != nullptr) { bbox_count += 1; std::string label_name; - uint32_t xmin = 0, ymin = 0, xmax = 0, ymax = 0; + float xmin = 0.0, ymin = 0.0, xmax = 0.0, ymax = 0.0; XMLElement *bbox_node = object->FirstChildElement("bndbox"); if (bbox_node != nullptr) { XMLElement *xmin_node = bbox_node->FirstChildElement("xmin"); - if (xmin_node != nullptr) xmin = xmin_node->UnsignedText(); + if (xmin_node != nullptr) xmin = xmin_node->FloatText(); XMLElement *ymin_node = bbox_node->FirstChildElement("ymin"); - if (ymin_node != nullptr) ymin = ymin_node->UnsignedText(); + if (ymin_node != nullptr) ymin = ymin_node->FloatText(); XMLElement *xmax_node = bbox_node->FirstChildElement("xmax"); - if (xmax_node != nullptr) xmax = xmax_node->UnsignedText(); + if (xmax_node != nullptr) xmax = xmax_node->FloatText(); XMLElement *ymax_node = bbox_node->FirstChildElement("ymax"); - if (ymax_node != nullptr) ymax = ymax_node->UnsignedText(); + if (ymax_node != nullptr) ymax = ymax_node->FloatText(); } else { MS_LOG(ERROR) << "bndbox dismatch in " + path; return false; diff --git a/tests/ut/data/dataset/golden/random_crop_with_bbox_01_c_result.npz b/tests/ut/data/dataset/golden/random_crop_with_bbox_01_c_result.npz index 0c220fd09d2f82888b93437e370325ab758da34d..bb33f1beceee1404dbf4dc8e696b5c4a01b8ebda 100644 GIT binary patch delta 267 zcmeyy^NmL+z?+#xgaHB+86=Orh}kIgiCJJZpO?Wa;p-n4TF2fOcZ-*vEW)B9@G*Xw zzQEZ}d@p`%*rHzG>M+>@DCoTSSj6>&I92WCtAko%pNmee0SacG-_yGLsgRBFd5bkF zhXk@FZvYCmyejtE%M?`bM&+@i?Un1|lb-+uSwDFdSV?ocU5bm_7gOroFj<0CMZopM zbMyEJ=d{L+p0_2d^oL7|DSQuL}698QO1@l0koyJfNrn=A{+ Jzmsj*ECKQ#X#W5J delta 267 zcmeyy^NmL+z?+#xgaHB+8E%=^Rc;je#4I3wms5O)w7|I+r!N)lsNRx1S%gJJVDef% z$AsMQb1eELA?J!@4@~v|3UWNFnRxoS$o$JvJT&D$e;WID44za_3GG8x7GY7r6!ZWJ zp6$DPQt5Dfh{&`+>*djNw@)0_LNQD02H+6J2qQ}SK~2% zzlwRt#vRiqKLH8~wcZUY$X;INQYXfi(r|`WC>Okf#wO7J2%H>2)atuZo2iPHGHxU zQ1C>@G?^H$1)Qh&h1OO+`E_)115ohDwuqdqD@|F-TAs|``sID> diff --git a/tests/ut/data/dataset/golden/random_vertical_flip_with_bbox_01_c_result.npz b/tests/ut/data/dataset/golden/random_vertical_flip_with_bbox_01_c_result.npz index aba6fe97b02e36a241ae5389633b08dc06e0de08..e0e0eb282320b1bd8e1bc1f6d14f7c6127796aa2 100644 GIT binary patch delta 267 zcmeyy^NmL+z?+#xgaHB+8BCdY6E_NdVitH3BB0>IEgXHMqT^aaQue0FA}lJHf*wG@ zy^A+BU0Q#mX~rj3Bfp}%TPD{41vfj+oFDkyMs$vpz-+I>%a=^v02F*^%kXRVo2ACy z-||1n<)42#`3X>PovF#kW#un>53V*fzG3XLaIyreiU8}$@*s8VrB7ZSIIlMSEK|^A zAE01sQ*)I`#KlG7Pv1S9FE77nasyEC$A!;bGrB(gUUBurl-9}Hx+ZS{3Tnw-t4mop wyD(Mh_`cJ3zjaN10Tkq({jvTIcjArdtCn5<_j)VKWEnPD7Lb1@+p<{#0J-FIQ~&?~ delta 267 zcmeyy^NmL+z?+#xgaHB+88)?@6WA#9iCJJVd%!&Dp4~#6QufZbd*x~;i?FC*3VHwq zU*>CDF0{BgbC>%I<4%LtSCeaig7rbQT91}r3ySAEJVRzi#nj0gfP$_qrySqv&B*7x z9ND%}^vOk0om|dT#5&qe5+@cm$AEa%TM+J z3Nkizym|IIW|rTBb*FxW-;0^t02I79f7&C{Ybi^!Jg%AC+U|C9@)n?Al=IF&#;o(} t^Yb>}_0+!edh!dPpiZzs?+-J!q%Z$GW3>tkwoR5{lVt(;FMYq|gc diff --git a/tests/ut/python/dataset/test_random_crop_and_resize_with_bbox.py b/tests/ut/python/dataset/test_random_crop_and_resize_with_bbox.py new file mode 100644 index 0000000000..46c45ecc36 --- /dev/null +++ b/tests/ut/python/dataset/test_random_crop_and_resize_with_bbox.py @@ -0,0 +1,214 @@ +# 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 RandomCropAndResizeWithBBox op in DE +""" +import numpy as np +import mindspore.dataset as ds +import mindspore.dataset.transforms.vision.c_transforms as c_vision + +from mindspore import log as logger +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 + +# Updated VOC dataset with correct annotations - DATA_DIR +DATA_DIR_VOC = "../data/dataset/testVOC2012_2" +# COCO dataset - DATA_DIR, ANNOTATION_DIR +DATA_DIR_COCO = ["../data/dataset/testCOCO/train/", "../data/dataset/testCOCO/annotations/train.json"] + + +def test_random_resized_crop_with_bbox_op_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomResizedCropWithBBox Op applied, + tests with MD5 check, expected to pass + """ + logger.info("test_random_resized_crop_with_bbox_op_c") + + original_seed = config_get_set_seed(23415) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + test_op = c_vision.RandomResizedCropWithBBox((256, 512), (0.5, 0.5), (0.5, 0.5)) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + filename = "random_resized_crop_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_resized_crop_with_bbox_op_coco_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomResizedCropWithBBox Op applied, + Testing with Coco dataset + """ + logger.info("test_random_resized_crop_with_bbox_op_coco_c") + # load dataset + dataCoco1 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + dataCoco2 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + test_op = c_vision.RandomResizedCropWithBBox((512, 512), (0.5, 1), (0.5, 1)) + + dataCoco2 = dataCoco2.map(input_columns=["image", "bbox"], + output_columns=["image", "bbox"], + columns_order=["image", "bbox"], + operations=[test_op]) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataCoco1.create_dict_iterator(), dataCoco2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp, "bbox") + + +def test_random_resized_crop_with_bbox_op_edge_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomResizedCropWithBBox Op applied, + tests on dynamically generated edge case, expected to pass + """ + logger.info("test_random_resized_crop_with_bbox_op_edge_c") + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + test_op = c_vision.RandomResizedCropWithBBox((256, 512), (0.5, 0.5), (0.5, 0.5)) + + # maps to convert data into valid edge case data + dataVoc1 = dataVoc1.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype))]) + + # Test Op added to list of Operations here + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype)), 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_resized_crop_with_bbox_op_invalid_c(): + """ + Tests RandomResizedCropWithBBox on invalid constructor parameters, expected to raise ValueError + """ + logger.info("test_random_resized_crop_with_bbox_op_invalid_c") + + # Load dataset, only Augmented Dataset as test will raise ValueError + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + try: + # If input range of scale is not in the order of (min, max), ValueError will be raised. + test_op = c_vision.RandomResizedCropWithBBox((256, 512), (1, 0.5), (0.5, 0.5)) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + for _ in dataVoc2.create_dict_iterator(): + break + + except ValueError as err: + logger.info("Got an exception in DE: {}".format(str(err))) + assert "Input range is not valid" in str(err) + + +def test_random_resized_crop_with_bbox_op_invalid2_c(): + """ + Tests RandomResizedCropWithBBox Op on invalid constructor parameters, expected to raise ValueError + """ + logger.info("test_random_resized_crop_with_bbox_op_invalid2_c") + # Load dataset # only loading the to AugDataset as test will fail on this + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + try: + # If input range of ratio is not in the order of (min, max), ValueError will be raised. + test_op = c_vision.RandomResizedCropWithBBox((256, 512), (1, 1), (1, 0.5)) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + for _ in dataVoc2.create_dict_iterator(): + break + + except ValueError as err: + logger.info("Got an exception in DE: {}".format(str(err))) + assert "Input range is not valid" in str(err) + + +def test_random_resized_crop_with_bbox_op_bad_c(): + """ + Test RandomCropWithBBox op with invalid bounding boxes, expected to catch multiple errors. + """ + logger.info("test_random_resized_crop_with_bbox_op_bad_c") + test_op = c_vision.RandomResizedCropWithBBox((256, 512), (0.5, 0.5), (0.5, 0.5)) + + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WidthOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.HeightOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.NegativeXY, "min_x") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WrongShape, "4 features") + + +if __name__ == "__main__": + test_random_resized_crop_with_bbox_op_c(plot_vis=True) + test_random_resized_crop_with_bbox_op_coco_c(plot_vis=True) + test_random_resized_crop_with_bbox_op_edge_c(plot_vis=True) + test_random_resized_crop_with_bbox_op_invalid_c() + test_random_resized_crop_with_bbox_op_invalid2_c() + test_random_resized_crop_with_bbox_op_bad_c() diff --git a/tests/ut/python/dataset/test_random_crop_with_bbox.py b/tests/ut/python/dataset/test_random_crop_with_bbox.py new file mode 100644 index 0000000000..b93c638f41 --- /dev/null +++ b/tests/ut/python/dataset/test_random_crop_with_bbox.py @@ -0,0 +1,249 @@ +# 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 RandomCropWithBBox op in DE +""" +import numpy as np +import mindspore.dataset as ds +import mindspore.dataset.transforms.vision.c_transforms as c_vision +import mindspore.dataset.transforms.vision.utils as mode + +from mindspore import log as logger +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 + +# Updated VOC dataset with correct annotations - DATA_DIR +DATA_DIR_VOC = "../data/dataset/testVOC2012_2" +# COCO dataset - DATA_DIR, ANNOTATION_DIR +DATA_DIR_COCO = ["../data/dataset/testCOCO/train/", "../data/dataset/testCOCO/annotations/train.json"] + + +def test_random_crop_with_bbox_op_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomCropWithBBox Op applied + """ + logger.info("test_random_crop_with_bbox_op_c") + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + # define test OP with values to match existing Op UT + test_op = c_vision.RandomCropWithBBox([512, 512], [200, 200, 200, 200]) + + # 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" + + 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_crop_with_bbox_op_coco_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomCropWithBBox Op applied, + Testing with Coco dataset + """ + logger.info("test_random_crop_with_bbox_op_coco_c") + # load dataset + dataCoco1 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + dataCoco2 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + test_op = c_vision.RandomCropWithBBox([512, 512], [200, 200, 200, 200]) + + dataCoco2 = dataCoco2.map(input_columns=["image", "bbox"], + output_columns=["image", "bbox"], + columns_order=["image", "bbox"], + operations=[test_op]) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataCoco1.create_dict_iterator(), dataCoco2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp, "bbox") + + +def test_random_crop_with_bbox_op2_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomCropWithBBox Op applied, + with md5 check, expected to pass + """ + logger.info("test_random_crop_with_bbox_op2_c") + original_seed = config_get_set_seed(593447) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + # define test OP with values to match existing Op unit - test + test_op = c_vision.RandomCropWithBBox(512, [200, 200, 200, 200], fill_value=(255, 255, 255)) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + filename = "random_crop_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_crop_with_bbox_op3_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomCropWithBBox Op applied, + with Padding Mode explicitly passed + """ + logger.info("test_random_crop_with_bbox_op3_c") + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + # define test OP with values to match existing Op unit - test + test_op = c_vision.RandomCropWithBBox(512, [200, 200, 200, 200], padding_mode=mode.Border.EDGE) + + # 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_crop_with_bbox_op_edge_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomCropWithBBox Op applied, + applied on dynamically generated edge case, expected to pass + """ + logger.info("test_random_crop_with_bbox_op_edge_c") + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + # define test OP with values to match existing Op unit - test + test_op = c_vision.RandomCropWithBBox(512, [200, 200, 200, 200], padding_mode=mode.Border.EDGE) + + # maps to convert data into valid edge case data + dataVoc1 = dataVoc1.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype))]) + + # Test Op added to list of Operations here + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype)), 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_crop_with_bbox_op_invalid_c(): + """ + Test RandomCropWithBBox Op on invalid constructor parameters, expected to raise ValueError + """ + logger.info("test_random_crop_with_bbox_op_invalid_c") + + # Load dataset + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + + try: + # define test OP with values to match existing Op unit - test + test_op = c_vision.RandomCropWithBBox([512, 512, 375]) + + # 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" + + for _ in dataVoc2.create_dict_iterator(): + break + except TypeError as err: + logger.info("Got an exception in DE: {}".format(str(err))) + assert "Size should be a single integer" in str(err) + + +def test_random_crop_with_bbox_op_bad_c(): + """ + Tests RandomCropWithBBox Op with invalid bounding boxes, expected to catch multiple errors. + """ + logger.info("test_random_crop_with_bbox_op_bad_c") + test_op = c_vision.RandomCropWithBBox([512, 512], [200, 200, 200, 200]) + + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WidthOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.HeightOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.NegativeXY, "min_x") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WrongShape, "4 features") + + +if __name__ == "__main__": + test_random_crop_with_bbox_op_c(plot_vis=True) + test_random_crop_with_bbox_op_coco_c(plot_vis=True) + test_random_crop_with_bbox_op2_c(plot_vis=True) + test_random_crop_with_bbox_op3_c(plot_vis=True) + test_random_crop_with_bbox_op_edge_c(plot_vis=True) + test_random_crop_with_bbox_op_invalid_c() + test_random_crop_with_bbox_op_bad_c() diff --git a/tests/ut/python/dataset/test_random_vertical_flip_with_bbox.py b/tests/ut/python/dataset/test_random_vertical_flip_with_bbox.py new file mode 100644 index 0000000000..be6778b1c6 --- /dev/null +++ b/tests/ut/python/dataset/test_random_vertical_flip_with_bbox.py @@ -0,0 +1,220 @@ +# 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 RandomVerticalFlipWithBBox op in DE +""" +import numpy as np +import mindspore.dataset as ds +import mindspore.dataset.transforms.vision.c_transforms as c_vision + +from mindspore import log as logger +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 + +# Updated VOC dataset with correct annotations - DATA_DIR +DATA_DIR_VOC = "../data/dataset/testVOC2012_2" +# COCO dataset - DATA_DIR, ANNOTATION_DIR +DATA_DIR_COCO = ["../data/dataset/testCOCO/train/", "../data/dataset/testCOCO/annotations/train.json"] + + +def test_random_vertical_flip_with_bbox_op_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomVerticalFlipWithBBox Op applied + """ + logger.info("test_random_vertical_flip_with_bbox_op_c") + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + test_op = c_vision.RandomVerticalFlipWithBBox(1) + + # 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_vertical_flip_with_bbox_op_coco_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomVerticalFlipWithBBox Op applied, + Testing with Coco dataset + """ + logger.info("test_random_vertical_flip_with_bbox_op_coco_c") + # load dataset + dataCoco1 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + dataCoco2 = ds.CocoDataset(DATA_DIR_COCO[0], annotation_file=DATA_DIR_COCO[1], task="Detection", + decode=True, shuffle=False) + + test_op = c_vision.RandomVerticalFlipWithBBox(1) + + dataCoco2 = dataCoco2.map(input_columns=["image", "bbox"], + output_columns=["image", "bbox"], + columns_order=["image", "bbox"], + operations=[test_op]) + + test_op = c_vision.RandomVerticalFlipWithBBox(1) + + unaugSamp, augSamp = [], [] + + for unAug, Aug in zip(dataCoco1.create_dict_iterator(), dataCoco2.create_dict_iterator()): + unaugSamp.append(unAug) + augSamp.append(Aug) + + if plot_vis: + visualize_with_bounding_boxes(unaugSamp, augSamp, "bbox") + + +def test_random_vertical_flip_with_bbox_op_rand_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomVerticalFlipWithBBox Op applied, + tests with MD5 check, expected to pass + """ + logger.info("test_random_vertical_flip_with_bbox_op_rand_c") + original_seed = config_get_set_seed(29847) + original_num_parallel_workers = config_get_set_num_parallel_workers(1) + + # Load dataset + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + test_op = c_vision.RandomVerticalFlipWithBBox(0.8) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + filename = "random_vertical_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_vertical_flip_with_bbox_op_edge_c(plot_vis=False): + """ + Prints images and bboxes side by side with and without RandomVerticalFlipWithBBox Op applied, + applied on dynamically generated edge case, expected to pass + """ + logger.info("test_random_vertical_flip_with_bbox_op_edge_c") + dataVoc1 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + test_op = c_vision.RandomVerticalFlipWithBBox(1) + + # maps to convert data into valid edge case data + dataVoc1 = dataVoc1.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype))]) + + # Test Op added to list of Operations here + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[lambda img, bboxes: (img, np.array([[0, 0, img.shape[1], img.shape[0]]]).astype(bboxes.dtype)), 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_vertical_flip_with_bbox_op_invalid_c(): + """ + Test RandomVerticalFlipWithBBox Op on invalid constructor parameters, expected to raise ValueError + """ + logger.info("test_random_vertical_flip_with_bbox_op_invalid_c") + dataVoc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", + decode=True, shuffle=False) + + try: + test_op = c_vision.RandomVerticalFlipWithBBox(2) + + # map to apply ops + dataVoc2 = dataVoc2.map(input_columns=["image", "annotation"], + output_columns=["image", "annotation"], + columns_order=["image", "annotation"], + operations=[test_op]) + + for _ in dataVoc2.create_dict_iterator(): + break + + except ValueError as err: + logger.info("Got an exception in DE: {}".format(str(err))) + assert "Input is not" in str(err) + + +def test_random_vertical_flip_with_bbox_op_bad_c(): + """ + Tests RandomVerticalFlipWithBBox Op with invalid bounding boxes, expected to catch multiple errors + """ + logger.info("test_random_vertical_flip_with_bbox_op_bad_c") + test_op = c_vision.RandomVerticalFlipWithBBox(1) + + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WidthOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.HeightOverflow, "bounding boxes is out of bounds of the image") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.NegativeXY, "min_x") + data_voc2 = ds.VOCDataset(DATA_DIR_VOC, task="Detection", mode="train", decode=True, shuffle=False) + check_bad_bbox(data_voc2, test_op, InvalidBBoxType.WrongShape, "4 features") + + +if __name__ == "__main__": + test_random_vertical_flip_with_bbox_op_c(plot_vis=True) + test_random_vertical_flip_with_bbox_op_coco_c(plot_vis=True) + test_random_vertical_flip_with_bbox_op_rand_c(plot_vis=True) + test_random_vertical_flip_with_bbox_op_edge_c(plot_vis=True) + test_random_vertical_flip_with_bbox_op_invalid_c() + test_random_vertical_flip_with_bbox_op_bad_c() diff --git a/tests/ut/python/dataset/util.py b/tests/ut/python/dataset/util.py index 2a8e93cd0b..432c01ef46 100644 --- a/tests/ut/python/dataset/util.py +++ b/tests/ut/python/dataset/util.py @@ -288,12 +288,13 @@ def config_get_set_num_parallel_workers(num_parallel_workers_new): return num_parallel_workers_original -def visualize_with_bounding_boxes(orig, aug, plot_rows=3): +def visualize_with_bounding_boxes(orig, aug, annot_name="annotation", plot_rows=3): """ Take a list of un-augmented and augmented images with "annotation" bounding boxes Plot images to compare test correct BBox augment functionality :param orig: list of original images and bboxes (without aug) :param aug: list of augmented images and bboxes + :param annot_name: the dict key for bboxes in data, e.g "bbox" (COCO) / "annotation" (VOC) :param plot_rows: number of rows on plot (rows = samples on one plot) :return: None """ @@ -301,9 +302,10 @@ def visualize_with_bounding_boxes(orig, aug, plot_rows=3): def add_bounding_boxes(ax, bboxes): for bbox in bboxes: rect = patches.Rectangle((bbox[0], bbox[1]), - bbox[2], bbox[3], - linewidth=1, edgecolor='r', facecolor='none') + bbox[2]*0.997, bbox[3]*0.997, + linewidth=1.80, edgecolor='r', facecolor='none') # Add the patch to the Axes + # Params to Rectangle slightly modified to prevent drawing overflow ax.add_patch(rect) # Quick check to confirm correct input parameters @@ -337,15 +339,15 @@ def visualize_with_bounding_boxes(orig, aug, plot_rows=3): (axA, axB) = (axs[x, 0], axs[x, 1]) if (curPlot > 1) else (axs[0], axs[1]) # select plotting axes based on number of image rows on plot - else case when 1 row axA.imshow(dataA["image"]) - add_bounding_boxes(axA, dataA["annotation"]) + add_bounding_boxes(axA, dataA[annot_name]) axA.title.set_text("Original" + str(cur_ix+1)) axB.imshow(dataB["image"]) - add_bounding_boxes(axB, dataB["annotation"]) + add_bounding_boxes(axB, dataB[annot_name]) axB.title.set_text("Augmented" + str(cur_ix+1)) - logger.info("Original **\n{} : {}".format(str(cur_ix+1), dataA["annotation"])) - logger.info("Augmented **\n{} : {}\n".format(str(cur_ix+1), dataB["annotation"])) + logger.info("Original **\n{} : {}".format(str(cur_ix+1), dataA[annot_name])) + logger.info("Augmented **\n{} : {}\n".format(str(cur_ix+1), dataB[annot_name])) plt.show() @@ -381,19 +383,19 @@ def check_bad_bbox(data, test_op, invalid_bbox_type, expected_error): width = img.shape[1] if invalid_bbox_type_ == InvalidBBoxType.WidthOverflow: # use box that overflows on width - return img, np.array([[0, 0, width + 1, height, 0, 0, 0]]).astype(np.uint32) + return img, np.array([[0, 0, width + 1, height, 0, 0, 0]]).astype(np.float32) if invalid_bbox_type_ == InvalidBBoxType.HeightOverflow: # use box that overflows on height - return img, np.array([[0, 0, width, height + 1, 0, 0, 0]]).astype(np.uint32) + return img, np.array([[0, 0, width, height + 1, 0, 0, 0]]).astype(np.float32) if invalid_bbox_type_ == InvalidBBoxType.NegativeXY: # use box with negative xy - return img, np.array([[-10, -10, width, height, 0, 0, 0]]).astype(np.uint32) + return img, np.array([[-10, -10, width, height, 0, 0, 0]]).astype(np.float32) if invalid_bbox_type_ == InvalidBBoxType.WrongShape: # use box that has incorrect shape - return img, np.array([[0, 0, width - 1]]).astype(np.uint32) + return img, np.array([[0, 0, width - 1]]).astype(np.float32) return img, bboxes try: