{"record":{"id":"9a98c35555320a72","repo":"roboflow/supervision","slug":"only-unshifted-drawing-coordinates-are-supported","errorCode":null,"errorMessage":"Only unshifted drawing coordinates are supported","messagePattern":"Only unshifted drawing coordinates are supported","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/supervision/_cv2/_drawing.py","lineNumber":65,"sourceCode":"    return round(point[0]), round(point[1])\n\n\ndef _points(points: npt.NDArray[Any], offset: tuple[int, int] = (0, 0)) -> list[_Point]:\n    \"\"\"Normalize OpenCV polygon shapes to integer Pillow coordinates.\"\"\"\n    values = np.asarray(points)\n    if values.size == 0:\n        return []\n    if values.ndim not in (2, 3) or values.shape[-1] != 2:\n        raise ValueError(\"Drawing points must have shape (N, 2) or (N, 1, 2)\")\n    normalized = np.rint(values.reshape(-1, 2)).astype(np.int64)\n    normalized += np.asarray(offset, dtype=np.int64)\n    return [(int(x), int(y)) for x, y in normalized]\n\n\ndef _validate_shift(shift: int) -> None:\n    \"\"\"Reject fixed-point coordinates not supported by the fallback.\"\"\"\n    if shift != 0:\n        raise ValueError(\"Only unshifted drawing coordinates are supported\")\n\n\ndef _line(\n    img: _ImageArray,\n    pt1: Sequence[int | float],\n    pt2: Sequence[int | float],\n    color: Any,\n    thickness: int = 1,\n    lineType: int = 8,\n    shift: int = 0,\n) -> _ImageArray:\n    \"\"\"Draw a line in place using Pillow's integer rasterization.\"\"\"\n    del lineType\n    _validate_shift(shift)\n    width = max(1, thickness)\n    mask = _drawing_mask(\n        img,\n        lambda draw: draw.line([_point(pt1), _point(pt2)], fill=1, width=width),","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/roboflow/supervision/blob/7f254d9784d4c37e0f03cd89ddee164c8db099c0/src/supervision/_cv2/_drawing.py#L47-L83","documentation":"OpenCV drawing functions accept a `shift` parameter meaning coordinates are fixed-point with `shift` fractional bits. The Pillow-based fallback cannot reproduce sub-pixel fixed-point rasterization, so `_validate_shift` at src/supervision/_cv2/_drawing.py:65 rejects any non-zero shift rather than silently drawing at wrong positions.","triggerScenarios":"Calling `cv2.line`/`rectangle`/`circle`/`polylines`/`fillPoly` fallback equivalents with `shift > 0` (e.g. `cv2.line(img, pt1, pt2, color, 1, 8, shift=4)`), typically when porting OpenCV sample code that uses fixed-point coordinates for anti-aliased sub-pixel precision.","commonSituations":"Environments without opencv-python where copied OpenCV snippets keep the `shift` argument; high-precision overlay code that multiplies coordinates by 2^shift.","solutions":["Remove the `shift` argument (use default 0) and pass integer pixel coordinates","Pre-divide fixed-point coordinates: `(pt / (1 << shift)).round().astype(int)` before drawing","Install `opencv-python` if sub-pixel fixed-point drawing is a hard requirement"],"exampleFix":"// before\ncv2.line(img, (160, 320), (480, 640), color, 2, cv2.LINE_8, shift=1)\n\n// after\nstart = (160 >> 1, 320 >> 1)\nend = (480 >> 1, 640 >> 1)\ncv2.line(img, start, end, color, 2, cv2.LINE_8, shift=0)","handlingStrategy":"validation","validationCode":"def unshift_points(points, shift: int):\n    \"\"\"Convert fixed-point coordinates to pixel coordinates before drawing.\"\"\"\n    if shift == 0:\n        return points\n    return [(int(x) >> shift, int(y) >> shift) for x, y in points]","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Avoid the shift parameter entirely in supervision-based drawing code","When porting OpenCV snippets, strip shift and pre-scale coordinates yourself"],"tags":["cv2-fallback","drawing","fixed-point","pillow"],"backgroundTag":null,"analyzedSha":"7f254d9784d4c37e0f03cd89ddee164c8db099c0","analyzedAt":"2026-08-15T05:13:01.950Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}