Numerical stability fix for Albumentations (#3958)
Browse files- utils/datasets.py +1 -1
- utils/general.py +11 -13
utils/datasets.py
CHANGED
@@ -550,7 +550,7 @@ class LoadImagesAndLabels(Dataset): # for training/testing
|
|
550 |
|
551 |
nl = len(labels) # number of labels
|
552 |
if nl:
|
553 |
-
labels[:, 1:5] = xyxy2xywhn(labels[:, 1:5], w=img.shape[1], h=img.shape[0]
|
554 |
|
555 |
if self.augment:
|
556 |
# Albumentations
|
|
|
550 |
|
551 |
nl = len(labels) # number of labels
|
552 |
if nl:
|
553 |
+
labels[:, 1:5] = xyxy2xywhn(labels[:, 1:5], w=img.shape[1], h=img.shape[0], clip=True, eps=1E-3)
|
554 |
|
555 |
if self.augment:
|
556 |
# Albumentations
|
utils/general.py
CHANGED
@@ -396,10 +396,10 @@ def xywhn2xyxy(x, w=640, h=640, padw=0, padh=0):
|
|
396 |
return y
|
397 |
|
398 |
|
399 |
-
def xyxy2xywhn(x, w=640, h=640, clip=False):
|
400 |
# Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] normalized where xy1=top-left, xy2=bottom-right
|
401 |
if clip:
|
402 |
-
clip_coords(x, (h, w)) # warning: inplace clip
|
403 |
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
404 |
y[:, 0] = ((x[:, 0] + x[:, 2]) / 2) / w # x center
|
405 |
y[:, 1] = ((x[:, 1] + x[:, 3]) / 2) / h # y center
|
@@ -458,18 +458,16 @@ def scale_coords(img1_shape, coords, img0_shape, ratio_pad=None):
|
|
458 |
return coords
|
459 |
|
460 |
|
461 |
-
def clip_coords(boxes,
|
462 |
# Clip bounding xyxy bounding boxes to image shape (height, width)
|
463 |
-
if isinstance(boxes, torch.Tensor):
|
464 |
-
boxes[:, 0].clamp_(0,
|
465 |
-
boxes[:, 1].clamp_(0,
|
466 |
-
boxes[:, 2].clamp_(0,
|
467 |
-
boxes[:, 3].clamp_(0,
|
468 |
-
else: # np.array
|
469 |
-
boxes[:, 0
|
470 |
-
boxes[:, 1
|
471 |
-
boxes[:, 2].clip(0, img_shape[1], out=boxes[:, 2]) # x2
|
472 |
-
boxes[:, 3].clip(0, img_shape[0], out=boxes[:, 3]) # y2
|
473 |
|
474 |
|
475 |
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False,
|
|
|
396 |
return y
|
397 |
|
398 |
|
399 |
+
def xyxy2xywhn(x, w=640, h=640, clip=False, eps=0.0):
|
400 |
# Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] normalized where xy1=top-left, xy2=bottom-right
|
401 |
if clip:
|
402 |
+
clip_coords(x, (h - eps, w - eps)) # warning: inplace clip
|
403 |
y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
|
404 |
y[:, 0] = ((x[:, 0] + x[:, 2]) / 2) / w # x center
|
405 |
y[:, 1] = ((x[:, 1] + x[:, 3]) / 2) / h # y center
|
|
|
458 |
return coords
|
459 |
|
460 |
|
461 |
+
def clip_coords(boxes, shape):
|
462 |
# Clip bounding xyxy bounding boxes to image shape (height, width)
|
463 |
+
if isinstance(boxes, torch.Tensor): # faster individually
|
464 |
+
boxes[:, 0].clamp_(0, shape[1]) # x1
|
465 |
+
boxes[:, 1].clamp_(0, shape[0]) # y1
|
466 |
+
boxes[:, 2].clamp_(0, shape[1]) # x2
|
467 |
+
boxes[:, 3].clamp_(0, shape[0]) # y2
|
468 |
+
else: # np.array (faster grouped)
|
469 |
+
boxes[:, [0, 2]] = boxes[:, [0, 2]].clip(0, shape[1]) # x1, x2
|
470 |
+
boxes[:, [1, 3]] = boxes[:, [1, 3]].clip(0, shape[0]) # y1, y2
|
|
|
|
|
471 |
|
472 |
|
473 |
def non_max_suppression(prediction, conf_thres=0.25, iou_thres=0.45, classes=None, agnostic=False, multi_label=False,
|