{"record":{"id":"41fa5d6e0d9de452","repo":"open-mmlab/mmdetection","slug":"x2-x1-value-in-box-swap-them","errorCode":null,"errorMessage":"X2 < X1 value in box. Swap them.","messagePattern":"X2 < X1 value in box\\. Swap them\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"mmdet/models/utils/wbf.py","lineNumber":165,"sourceCode":"            print('Error. Length of boxes arrays not equal to '\n                  'length of labels array: {} != {}'.format(\n                      len(boxes[t]), len(labels[t])))\n            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","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/open-mmlab/mmdetection/blob/cfd5d3a985b0249de009b67d04f37263e11cdf3d/mmdet/models/utils/wbf.py#L147-L183","documentation":"This warning is emitted by prefilter_boxes inside weighted_boxes_fusion (WBF) in mmdet/models/utils/wbf.py when a detection box has x2 < x1, i.e. the horizontal corners are reversed. WBF requires boxes in strict [x1, y1, x2, y2] format with x1 <= x2; the code auto-corrects the box by swapping the coordinates and continues. It is purely informational — the fusion still proceeds with the corrected box.","triggerScenarios":"Calling weighted_boxes_fusion() (or prefilter_boxes directly) with a boxes list where some entry has box[2] < box[0]. Typical causes: a model that outputs corners in reverse order, boxes converted from cx/cy/w/h with a sign mistake, or preprocessing that flips/normalizes coordinates incorrectly.","commonSituations":"Fusing detections from heterogeneous models (some output xyxy, others xywh or cxcywh without conversion); using TTA or ensemble pipelines where one detector's head produces reversed x coordinates; boxes in normalized [0,1] vs pixel coordinates mixed together.","solutions":["Convert all model outputs to xyxy format before fusion, e.g. mmcv.bbox_xyxy_to_xywh / mmdet.core.bbox_xyxy_to_cxcywh inverse conversions or torchvision.ops.box_convert(..., in_fmt='cxcywh', out_fmt='xyxy').","Sanitize boxes before calling weighted_boxes_fusion: boxes = [np.minimum(b[:, :2], b[:, 2:4]) concatenated with np.maximum(...)] to enforce x1<=x2, y1<=y2.","Check the upstream model's bbox coder (e.g. DeltaXYXYBBoxCoder vs DistancePointBBoxCoder) and test-decoding logic for a sign or axis mix-up.","If the warning is benign and expected, filter it with warnings.filterwarnings('ignore', message='X2 < X1 value in box.*')."],"exampleFix":"// before\nboxes_list = [model_a_boxes, model_b_boxes]  # model_b emits cxcywh\nlabels, scores, boxes = weighted_boxes_fusion(boxes_list, labels_list, scores_list)\n// after\nfrom torchvision.ops import box_convert\nmodel_b_xyxy = box_convert(torch.tensor(model_b_boxes), in_fmt='cxcywh', out_fmt='xyxy').tolist()\nlabels, scores, boxes = weighted_boxes_fusion([model_a_boxes, model_b_xyxy], labels_list, scores_list)","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef sanitize_xyxy(boxes):\n    boxes = np.asarray(boxes, dtype=np.float64)\n    x1, y1, x2, y2 = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]\n    boxes[:, 0], boxes[:, 2] = np.minimum(x1, x2), np.maximum(x1, x2)\n    boxes[:, 1], boxes[:, 3] = np.minimum(y1, y2), np.maximum(y1, y2)\n    return boxes","typeGuard":"def has_valid_x_order(boxes):\n    boxes = np.asarray(boxes)\n    return bool((boxes[:, 2] >= boxes[:, 0]).all())","tryCatchPattern":null,"preventionTips":["Standardize all detector outputs to xyxy immediately after inference (torchvision.ops.box_convert).","Add a unit test asserting x2>=x1 and y2>=y1 for every box fed into weighted_boxes_fusion.","Run sanitize_xyxy on each model's boxes before ensembling."],"tags":["mmdetection","wbf","bounding-box","coordinate-order","ensemble"],"backgroundTag":"invalid-bbox-coordinates","analyzedSha":"cfd5d3a985b0249de009b67d04f37263e11cdf3d","analyzedAt":"2026-08-27T20:54:20.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}