{"record":{"id":"fc1108d31b6f1ce5","repo":"open-mmlab/mmdetection","slug":"y2-y1-value-in-box-swap-them","errorCode":null,"errorMessage":"Y2 < Y1 value in box. Swap them.","messagePattern":"Y2 < Y1 value in box\\. Swap them\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mmdet/models/utils/wbf.py","lineNumber":168,"sourceCode":"            exit()\n\n        for j in range(len(boxes[t])):\n            score = scores[t][j]\n            if score < thr:\n                continue\n            label = int(labels[t][j])\n            box_part = boxes[t][j]\n            x1 = float(box_part[0])\n            y1 = float(box_part[1])\n            x2 = float(box_part[2])\n            y2 = float(box_part[3])\n\n            # Box data checks\n            if x2 < x1:\n                warnings.warn('X2 < X1 value in box. Swap them.')\n                x1, x2 = x2, x1\n            if y2 < y1:\n                warnings.warn('Y2 < Y1 value in box. Swap them.')\n                y1, y2 = y2, y1\n            if (x2 - x1) * (y2 - y1) == 0.0:\n                warnings.warn('Zero area box skipped: {}.'.format(box_part))\n                continue\n\n            # [label, score, weight, model index, x1, y1, x2, y2]\n            b = [\n                int(label),\n                float(score) * weights[t], weights[t], t, x1, y1, x2, y2\n            ]\n\n            if label not in new_boxes:\n                new_boxes[label] = []\n            new_boxes[label].append(b)\n\n    # Sort each list in dict by score and transform it to numpy array\n    for k in new_boxes:\n        current_boxes = np.array(new_boxes[k])","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/open-mmlab/mmdetection/blob/cfd5d3a985b0249de009b67d04f37263e11cdf3d/mmdet/models/utils/wbf.py#L150-L186","documentation":"Emitted by prefilter_boxes in WBF when a box has y2 < y1 — the vertical corners are reversed. The code swaps y1 and y2 so the box becomes valid and fusion continues. Like its X counterpart it is a data-hygiene warning, not a failure.","triggerScenarios":"Calling weighted_boxes_fusion() with a boxes entry where box[3] < box[1]. Happens when image-space y axis is flipped (e.g. top-left vs bottom-left origin conventions), when height is subtracted instead of added (y2 = cy - h/2 instead of cy + h/2), or with flipped images in TTA where coordinates are not un-flipped.","commonSituations":"Mixing coordinate systems from different detection backends (COCO vs Albumentations vs pixel coordinates); h-flip TTA augmentations where the inverse transform was not applied to y; hand-written cxcywh→xyxy converters with a typo'd sign.","solutions":["Fix the cx,cy,w,h → x1,y1,x2,y2 conversion: y1 = cy - h/2, y2 = cy + h/2 (signs, not both minus).","Clamp/swap coordinates defensively before fusion: y1, y2 = min(y1, y2), max(y1, y2).","Verify flip/TTA post-processing restores the original coordinate frame before feeding WBF.","Silence expected noise with warnings.filterwarnings('ignore', message='Y2 < Y1 value in box.*')."],"exampleFix":"# before\ny1, y2 = cy - h / 2, cy - h / 2  # typo: both minus\n# after\ny1, y2 = cy - h / 2, cy + h / 2","handlingStrategy":"validation","validationCode":"def sanitize_y(boxes):\n    boxes = np.asarray(boxes, dtype=np.float64).copy()\n    y1, y2 = boxes[:, 1], boxes[:, 3]\n    boxes[:, 1], boxes[:, 3] = np.minimum(y1, y2), np.maximum(y1, y2)\n    return boxes","typeGuard":"def has_valid_y_order(boxes):\n    boxes = np.asarray(boxes)\n    return bool((boxes[:, 3] >= boxes[:, 1]).all())","tryCatchPattern":null,"preventionTips":["Double-check sign in cxcywh→xyxy conversions (y2 = cy + h/2).","Apply inverse geometric transforms after flip/TTA before fusion.","Use one shared bbox-format conversion utility across the ensemble pipeline."],"tags":["mmdetection","wbf","bounding-box","coordinate-order","y-axis-flip"],"backgroundTag":"invalid-bbox-coordinates","analyzedSha":"cfd5d3a985b0249de009b67d04f37263e11cdf3d","analyzedAt":"2026-08-27T20:54:20.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}