1

I am using albumentations for a set of images and bboxes.

My bounding box is in "yolo" format, i.e., (x_mid, y_mid, width, height), all normalised.

While running albumentations for a set of bounding errors, above error comes (ValueError: x_max is less than or equal to x_min for bbox) when bounding box takes this value : [0.00017655367231635, 0.0002155172413793, 0.0003531073446327, 0.0004310344827586]. Here, x_min, y_min=[0,0].

This error doesn't come for other bounding box - eg: [0.3060659468984962, 0.4418239134174312, 0.2412257095864662, 0.5496854701834863].

This error is solved when I slightly increase all dimensions of ERROR bounding boxes, i.e. : Increase x_mid, y_mid, x_width and y_width by 0.01 for this ERROR BB [0.00017655367231635, 0.0002155172413793, 0.0003531073446327, 0.0004310344827586].

Resulting ERROR FREE BB: [0.01017655367231635, 0.0102155172413793, 0.0103531073446327, 0.010431034482758601].

Can anybody tell me a way to solve this error without changing dimensions of BB?

I am using below code for albumentations:

import albumentations as A

    A.Compose(
    [
        A.LongestMaxSize(max_size=IMAGE_SIZE),
        A.PadIfNeeded(
            min_height=IMAGE_SIZE, min_width=IMAGE_SIZE, border_mode=cv2.BORDER_CONSTANT
        ),
        A.Normalize(mean=[0, 0, 0], std=[1, 1, 1], max_pixel_value=255,),
        ToTensorV2(),
    ],
    bbox_params=A.BboxParams(format="yolo", min_visibility=0.4, label_fields=[]),
)

Detailed error:

---> 62                 augmentations_norm = self.transform_norm(image=image, bboxes=bboxes)
 63                 #print('Aug done')
 64                 image = augmentations_norm["image"]

/opt/conda/lib/python3.7/site-packages/albumentations/core/composition.py in __call__(self, force_apply, *args, **data)
    178             if dual_start_end is not None and idx == dual_start_end[0]:
    179                 for p in self.processors.values():
--> 180                     p.preprocess(data)
    181 
    182             data = t(force_apply=force_apply, **data)

/opt/conda/lib/python3.7/site-packages/albumentations/core/utils.py in preprocess(self, data)
     60         rows, cols = data["image"].shape[:2]
     61         for data_name in self.data_fields:
---> 62             data[data_name] = self.check_and_convert(data[data_name], rows, cols, direction="to")
     63 
     64     def check_and_convert(self, data, rows, cols, direction="to"):

/opt/conda/lib/python3.7/site-packages/albumentations/core/utils.py in check_and_convert(self, data, rows, cols, direction)
     68 
     69         if direction == "to":
---> 70             return self.convert_to_albumentations(data, rows, cols)
     71 
     72         return self.convert_from_albumentations(data, rows, cols)

/opt/conda/lib/python3.7/site-packages/albumentations/augmentations/bbox_utils.py in convert_to_albumentations(self, data, rows, cols)
     49 
     50     def convert_to_albumentations(self, data, rows, cols):
---> 51         return convert_bboxes_to_albumentations(data, self.params.format, rows, cols, check_validity=True)
     52 
     53 

/opt/conda/lib/python3.7/site-packages/albumentations/augmentations/bbox_utils.py in convert_bboxes_to_albumentations(bboxes, source_format, rows, cols, check_validity)
    300 def convert_bboxes_to_albumentations(bboxes, source_format, rows, cols, check_validity=False):
    301     """Convert a list bounding boxes from a format specified in `source_format` to the format used by albumentations"""
--> 302     return [convert_bbox_to_albumentations(bbox, source_format, rows, cols, check_validity) for bbox in bboxes]
    303 
    304 

/opt/conda/lib/python3.7/site-packages/albumentations/augmentations/bbox_utils.py in <listcomp>(.0)
    300 def convert_bboxes_to_albumentations(bboxes, source_format, rows, cols, check_validity=False):
    301     """Convert a list bounding boxes from a format specified in `source_format` to the format used by albumentations"""
--> 302     return [convert_bbox_to_albumentations(bbox, source_format, rows, cols, check_validity) for bbox in bboxes]
    303 
    304 

/opt/conda/lib/python3.7/site-packages/albumentations/augmentations/bbox_utils.py in convert_bbox_to_albumentations(bbox, source_format, rows, cols, check_validity)
    249     bbox = normalize_bbox(bbox, rows, cols)
    250     if check_validity:
--> 251         check_bbox(bbox)
    252     return bbox
    253 

/opt/conda/lib/python3.7/site-packages/albumentations/augmentations/bbox_utils.py in check_bbox(bbox)
    331     x_min, y_min, x_max, y_max = bbox[:4]
    332     if x_max <= x_min:
--> 333         raise ValueError("x_max is less than or equal to x_min for bbox {bbox}.".format(bbox=bbox))
    334     if y_max <= y_min:
    335         raise ValueError("y_max is less than or equal to y_min for bbox {bbox}.".format(bbox=bbox))

ValueError: x_max is less than or equal to x_min for bbox (0.00390625, 0.00390625, 0.00390625, 0.00390625, 0.0).
Mins
  • 109
  • 1
  • 8
  • Bounding box [0.00017655367231635, 0.0002155172413793, 0.0003531073446327, 0.0004310344827586] (x_mid, y_mid, width, height) has x_min =0.0 and y_min=0.0. They are exactly zeros and not negative. You obersved negative x_min, how? Error resolves when I add 0.1 to all fields, however, if I just add 0.1 to x_mid & y_mid, error still prevails (Though by this application, x_min and y_min are increases by 0.5) – Mins Aug 16 '21 at 04:00
  • 1
    ignore previous comment (oops). My guess is that the bounding box width you're doing is getting to be 0 pixels wide. For example, with a width of 0.04%, with an image of width 1000 pixels, that gives you a bounding box of size 0.4, which will likely be cast to an int and therefore 0. – jhso Aug 16 '21 at 06:46
  • 1
    Thanks. You are correct, albumentation is NOt considering width as low as 0.005 (considering it as 0 ) and hence x_min and x_max are same (same for height & y coordinates) Is this thing documented anywhere ? I spent 3 days to resolve this error – Mins Aug 17 '21 at 01:37

1 Answers1

2

Adapted from comments:

When you are using bounding boxes in the albumentations library, you need to ensure that the boxes have a width of more than 0 when looking at your image size. This might not be obvious in the yolo bounding box representation, but you can do a quick check if you do this:

pix_w = int(bb[2]*img_w)
pix_h = int(bb[3]*img_w)
np.testing.assert_equal(np.all([pix_w>0,pix_h>0]), True)
jhso
  • 3,103
  • 1
  • 5
  • 13
  • Thanks. pix_w and pix_h would be always >= 1 when width n height were normalized (bb[2] and bb[3]) wrt to same image. However, if we've normalized width n height wrt one image and images are then resized, this thing(width < 1, height < 1) can then happen. I think we can use Normalized bounding box (normalized wrt 1 image size) for another image(Image of same context but different size), as BB are normalized, what do you think? – Mins Aug 17 '21 at 01:54
  • I'm not sure if that's the case here, but in general the bounding box should always be normalized according to the exact image that they're taken from. – jhso Aug 17 '21 at 03:36