{"record":{"id":"5fdef1681566f3c9","repo":"Comfy-Org/ComfyUI","slug":"at-least-4-points-are-required-to-compute-a-homogr","errorCode":null,"errorMessage":"At least 4 points are required to compute a homography.","messagePattern":"At least 4 points are required to compute a homography\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"comfy/ldm/depth_anything_3/ray_pose.py","lineNumber":45,"sourceCode":"    Q = Q * sign[None, :]  # scale columns of Q\n    L = L * sign[:, None]  # scale rows of L\n    return Q, L\n\n\ndef _homogenize_points(points: torch.Tensor) -> torch.Tensor:\n    return torch.cat([points, torch.ones_like(points[..., :1])], dim=-1)\n\n\n# -----------------------------------------------------------------------------\n# Weighted-LSQ + RANSAC homography (batched)\n# -----------------------------------------------------------------------------\n\n\ndef _find_homography_weighted_lsq(src_pts: torch.Tensor, dst_pts: torch.Tensor, confident_weight: torch.Tensor,) -> torch.Tensor:\n    \"\"\"Solve a single H with weighted least-squares (DLT).\"\"\"\n    N = src_pts.shape[0]\n    if N < 4:\n        raise ValueError(\"At least 4 points are required to compute a homography.\")\n    w = confident_weight.sqrt().unsqueeze(1)  # (N, 1)\n    x = src_pts[:, 0:1]\n    y = src_pts[:, 1:2]\n    u = dst_pts[:, 0:1]\n    v = dst_pts[:, 1:2]\n    zeros = torch.zeros_like(x)\n    A1 = torch.cat([-x * w, -y * w, -w, zeros, zeros, zeros, x * u * w, y * u * w, u * w], dim=1)\n    A2 = torch.cat([zeros, zeros, zeros, -x * w, -y * w, -w, x * v * w, y * v * w, v * w], dim=1)\n    A = torch.cat([A1, A2], dim=0)        # (2N, 9)\n    # CUDA SVD is not implemented for fp16/bf16; upcast just for this call.\n    _, _, Vh = torch.linalg.svd(A.float())\n    Vh = Vh.to(A.dtype)\n    H = Vh[-1].reshape(3, 3)\n    return H / H[-1, -1]\n\n\ndef _find_homography_weighted_lsq_batched(src_pts_batch: torch.Tensor, dst_pts_batch: torch.Tensor, confident_weight_batch: torch.Tensor) -> torch.Tensor:\n    \"\"\"Batched DLT solver. Inputs (B, K, 2) / (B, K); output (B, 3, 3).\"\"\"","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/Comfy-Org/ComfyUI/blob/1c6d8d45b3693bfbb32385b410d813a7fd6be216/comfy/ldm/depth_anything_3/ray_pose.py#L27-L63","documentation":"Ray/pose utilities in DepthAnything 3 estimate a homography via weighted DLT, which needs at least 4 2D point correspondences to constrain the 8-DOF matrix. _find_homography_weighted_lsq() raises ValueError when fewer than 4 point pairs are supplied. This is a geometric impossibility, not a bug — with 3 or fewer pairs the solution is underdetermined.","triggerScenarios":"Calling the weighted-LSQ homography helper (directly or through the RANSAC wrapper) with a correspondence set of 0-3 points — e.g. when matching two views produced too few confident matches, or when confident-weight thresholding filtered nearly all correspondences away.","commonSituations":"Multi-view depth runs on image pairs with little overlap, low-texture scenes, or overly strict match-confidence filters; degenerate inputs like identical frames or pure-color images.","solutions":["Check the correspondence count before calling: require >= 4 point pairs (in practice far more for RANSAC to be stable).","Loosen the match-confidence threshold or increase the number of proposed matches so more correspondences survive filtering.","Skip homography-based pose estimation for view pairs with insufficient overlap and fall back to an identity/known-pose assumption."],"exampleFix":"# before\nH = _find_homography_weighted_lsq(src, dst, w)\n\n# after\nif src.shape[0] < 4:\n    raise SkipViewPair(f\"only {src.shape[0]} correspondences\")\nH = _find_homography_weighted_lsq(src, dst, w)","handlingStrategy":"validation","validationCode":"if src_pts.shape[0] < 4 or dst_pts.shape[0] < 4:\n    raise ValueError(f\"need >=4 correspondences, got {src_pts.shape[0]}\")","typeGuard":"def has_min_correspondences(src_pts: \"torch.Tensor\", minimum: int = 4) -> bool:\n    return src_pts.ndim == 2 and src_pts.shape[0] >= minimum and src_pts.shape[1] >= 2","tryCatchPattern":"try:\n    H = _find_homography_weighted_lsq(src, dst, w)\nexcept ValueError:\n    H = None  # skip pose estimation for this view pair","preventionTips":["Count surviving correspondences after confidence filtering before pose estimation.","Require well above 4 (e.g. 16+) matches for stable RANSAC homography.","Handle low-overlap view pairs explicitly instead of letting geometry code fail."],"tags":["depth-anything-3","homography","geometry","input-validation"],"backgroundTag":null,"analyzedSha":"1c6d8d45b3693bfbb32385b410d813a7fd6be216","analyzedAt":"2026-08-14T19:37:18.893Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}