updated test util file + new BoundingBoxCheck + fixed VOCDataset annotations

Style Error fix

fixed PyLint problem

reverting testVOC2012 due to CI break for existing test

reverting testVOC2012 due to CI break for existing test-2

updated old error messages to confirm with global standard

addressing PR 2355 Comments - 1

addressing PR 2355 Comments - 2

addressing PR 2355 Comments - 3
pull/2533/head
Danish Farid 5 years ago
parent 8b7e8262a9
commit a8f392ca42

@ -41,7 +41,7 @@ Status RandomVerticalFlipWithBBoxOp::Compute(const TensorRow &input, TensorRow *
RETURN_IF_NOT_OK(input[1]->GetUnsignedIntAt(&boxHeight, {i, 3})); // get height of bbox RETURN_IF_NOT_OK(input[1]->GetUnsignedIntAt(&boxHeight, {i, 3})); // get height of bbox
// subtract (curCorner + height) from (max) for new Corner position // subtract (curCorner + height) from (max) for new Corner position
newBoxCorner_y = (imHeight - 1) - (boxCorner_y + boxHeight); newBoxCorner_y = (imHeight - 1) - ((boxCorner_y + boxHeight) - 1);
RETURN_IF_NOT_OK(input[1]->SetItemAt({i, 1}, newBoxCorner_y)); RETURN_IF_NOT_OK(input[1]->SetItemAt({i, 1}, newBoxCorner_y));
} }

@ -45,14 +45,18 @@
#define BOUNDING_BOX_CHECK(input) \ #define BOUNDING_BOX_CHECK(input) \
do { \ do { \
if (input.size() != 2) { \
return Status(StatusCode::kBoundingBoxInvalidShape, __LINE__, __FILE__, \
"Requires Image and Bounding Boxes, likely missed bounding boxes."); \
} \
if (input[1]->shape().Size() < 2) { \ if (input[1]->shape().Size() < 2) { \
return Status(StatusCode::kBoundingBoxInvalidShape, __LINE__, __FILE__, \ return Status(StatusCode::kBoundingBoxInvalidShape, __LINE__, __FILE__, \
"Bounding boxes shape should have at least two dims"); \ "Bounding boxes shape should have at least two dimensions."); \
} \ } \
uint32_t num_of_features = input[1]->shape()[1]; \ uint32_t num_of_features = input[1]->shape()[1]; \
if (num_of_features < 4) { \ if (num_of_features < 4) { \
return Status(StatusCode::kBoundingBoxInvalidShape, __LINE__, __FILE__, \ return Status(StatusCode::kBoundingBoxInvalidShape, __LINE__, __FILE__, \
"Bounding boxes should be have at least 4 features"); \ "Bounding boxes should be have at least 4 features."); \
} \ } \
uint32_t num_of_boxes = input[1]->shape()[0]; \ uint32_t num_of_boxes = input[1]->shape()[0]; \
uint32_t img_h = input[0]->shape()[0]; \ uint32_t img_h = input[0]->shape()[0]; \

@ -0,0 +1,27 @@
<annotation>
<folder>VOC2012</folder>
<filename>129.jpg</filename>
<source>
<database>simulate VOC2007 Database</database>
<annotation>simulate VOC2007</annotation>
<image>flickr</image>
</source>
<size>
<width>500</width>
<height>375</height>
<depth>3</depth>
</size>
<segmented>1</segmented>
<object>
<name>dog</name>
<pose>Frontal</pose>
<truncated>0</truncated>
<difficult>0</difficult>
<bndbox>
<xmin>1124</xmin>
<ymin>437</ymin>
<xmax>1684</xmax>
<ymax>2669</ymax>
</bndbox>
</object>
</annotation>

@ -0,0 +1,2 @@
Custom VOC2012-like dataset with valid annotations for images.
Created to test BoundingBox Augmentation Ops - June 2020.

@ -305,8 +305,8 @@ def test_c_random_resized_crop_with_bbox_op_bad():
if __name__ == "__main__": if __name__ == "__main__":
test_c_random_resized_crop_with_bbox_op(False) test_c_random_resized_crop_with_bbox_op(plot_vis=True)
test_c_random_resized_crop_with_bbox_op_edge(False) test_c_random_resized_crop_with_bbox_op_edge(plot_vis=True)
test_c_random_resized_crop_with_bbox_op_invalid() test_c_random_resized_crop_with_bbox_op_invalid()
test_c_random_resized_crop_with_bbox_op_invalid2() test_c_random_resized_crop_with_bbox_op_invalid2()
test_c_random_resized_crop_with_bbox_op_bad() test_c_random_resized_crop_with_bbox_op_bad()

@ -142,7 +142,7 @@ def gen_bbox_edge(im, bbox):
return im, bbox return im, bbox
def c_random_crop_with_bbox_op(plot_vis=False): def test_random_crop_with_bbox_op_c(plot_vis=False):
""" """
Prints images side by side with and without Aug applied + bboxes Prints images side by side with and without Aug applied + bboxes
""" """
@ -176,7 +176,7 @@ def c_random_crop_with_bbox_op(plot_vis=False):
visualize(unaugSamp, augSamp) visualize(unaugSamp, augSamp)
def c_random_crop_with_bbox_op2(plot_vis=False): def test_random_crop_with_bbox_op2_c(plot_vis=False):
""" """
Prints images side by side with and without Aug applied + bboxes Prints images side by side with and without Aug applied + bboxes
With Fill Value With Fill Value
@ -212,7 +212,7 @@ def c_random_crop_with_bbox_op2(plot_vis=False):
visualize(unaugSamp, augSamp) visualize(unaugSamp, augSamp)
def c_random_crop_with_bbox_op3(plot_vis=False): def test_random_crop_with_bbox_op3_c(plot_vis=False):
""" """
Prints images side by side with and without Aug applied + bboxes Prints images side by side with and without Aug applied + bboxes
With Padding Mode passed With Padding Mode passed
@ -247,7 +247,7 @@ def c_random_crop_with_bbox_op3(plot_vis=False):
visualize(unaugSamp, augSamp) visualize(unaugSamp, augSamp)
def c_random_crop_with_bbox_op_edge(plot_vis=False): def test_random_crop_with_bbox_op_edge_c(plot_vis=False):
""" """
Prints images side by side with and without Aug applied + bboxes Prints images side by side with and without Aug applied + bboxes
Testing for an Edge case Testing for an Edge case
@ -289,7 +289,7 @@ def c_random_crop_with_bbox_op_edge(plot_vis=False):
visualize(unaugSamp, augSamp) visualize(unaugSamp, augSamp)
def c_random_crop_with_bbox_op_invalid(): def test_random_crop_with_bbox_op_invalid_c():
""" """
Checking for invalid params passed to Aug Constructor Checking for invalid params passed to Aug Constructor
""" """
@ -319,7 +319,7 @@ def c_random_crop_with_bbox_op_invalid():
assert "Size should be a single integer" in str(err) assert "Size should be a single integer" in str(err)
def c_random_crop_with_bbox_op_bad(): def test_random_crop_with_bbox_op_bad_c():
# Should Fail - Errors logged to logger # Should Fail - Errors logged to logger
for ix, badFunc in enumerate(badGenFuncs): for ix, badFunc in enumerate(badGenFuncs):
try: try:
@ -352,9 +352,9 @@ def c_random_crop_with_bbox_op_bad():
if __name__ == "__main__": if __name__ == "__main__":
c_random_crop_with_bbox_op(False) test_random_crop_with_bbox_op_c(plot_vis=True)
c_random_crop_with_bbox_op2(False) test_random_crop_with_bbox_op2_c(plot_vis=True)
c_random_crop_with_bbox_op3(False) test_random_crop_with_bbox_op3_c(plot_vis=True)
c_random_crop_with_bbox_op_edge(False) test_random_crop_with_bbox_op_edge_c(plot_vis=True)
c_random_crop_with_bbox_op_invalid() test_random_crop_with_bbox_op_invalid_c()
c_random_crop_with_bbox_op_bad() test_random_crop_with_bbox_op_bad_c()

@ -16,7 +16,9 @@
import hashlib import hashlib
import json import json
import os import os
from enum import Enum
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np import numpy as np
# import jsbeautifier # import jsbeautifier
import mindspore.dataset as ds import mindspore.dataset as ds
@ -284,3 +286,124 @@ def config_get_set_num_parallel_workers(num_parallel_workers_new):
logger.info("num_parallel_workers: original = {} new = {} ".format(num_parallel_workers_original, logger.info("num_parallel_workers: original = {} new = {} ".format(num_parallel_workers_original,
num_parallel_workers_new)) num_parallel_workers_new))
return num_parallel_workers_original return num_parallel_workers_original
def visualize_with_bounding_boxes(orig, aug, 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 plot_rows: number of rows on plot (rows = samples on one plot)
:return: None
"""
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')
# Add the patch to the Axes
ax.add_patch(rect)
# Quick check to confirm correct input parameters
if not isinstance(orig, list) or not isinstance(aug, list):
return
if len(orig) != len(aug) or not orig:
return
comp_set = int(len(orig)/plot_rows)
orig, aug = np.array(orig), np.array(aug)
if len(orig) > plot_rows:
orig = np.split(orig[:comp_set*plot_rows], comp_set) + [orig[comp_set*plot_rows:]]
aug = np.split(aug[:comp_set*plot_rows], comp_set) + [aug[comp_set*plot_rows:]]
else:
orig = [orig]
aug = [aug]
for ix, allData in enumerate(zip(orig, aug)):
base_ix = ix * plot_rows # will signal what base level we're on
sub_plot_count = 2 if (len(allData[0]) < 2) else len(allData[0]) # if 1 image remains, create subplot for 2 to simplify axis selection
fig, axs = plt.subplots(sub_plot_count, 2)
fig.tight_layout(pad=1.5)
for x, (dataA, dataB) in enumerate(zip(allData[0], allData[1])):
cur_ix = base_ix + x
axs[x, 0].imshow(dataA["image"])
add_bounding_boxes(axs[x, 0], dataA["annotation"])
axs[x, 0].title.set_text("Original" + str(cur_ix+1))
logger.info("Original **\n{} : {}".format(str(cur_ix+1), dataA["annotation"]))
axs[x, 1].imshow(dataB["image"])
add_bounding_boxes(axs[x, 1], dataB["annotation"])
axs[x, 1].title.set_text("Augmented" + str(cur_ix+1))
logger.info("Augmented **\n{} : {}\n".format(str(cur_ix+1), dataB["annotation"]))
plt.show()
class InvalidBBoxType(Enum):
"""
Defines Invalid Bounding Bbox types for test cases
"""
WidthOverflow = 1
HeightOverflow = 2
NegativeXY = 3
WrongShape = 4
def check_bad_bbox(data, test_op, invalid_bbox_type, expected_error):
"""
:param data: de object detection pipeline
:param test_op: Augmentation Op to test on image
:param invalid_bbox_type: type of bad box
:param expected_error: error expected to get due to bad box
:return: None
"""
def add_bad_annotation(img, bboxes, invalid_bbox_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 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)
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)
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)
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, bboxes
try:
# map to use selected invalid bounding box type
data = data.map(input_columns=["image", "annotation"],
output_columns=["image", "annotation"],
columns_order=["image", "annotation"],
operations=lambda img, bboxes: add_bad_annotation(img, bboxes, invalid_bbox_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)

Loading…
Cancel
Save