OpenCV getPerspectiveTransform透视变换部分场景结果异常求助
Let’s break down what’s going wrong with your bird’s-eye view transformation, and fix that wonky result you’re seeing with some samples.
The Core Issue: You’re Mapping in the Wrong Direction
Your current logic uses cv2.boundingRect(src_pts) to define the target points for your initial homography—and that’s the root of the problem. Here’s why:
- We want to unwarp the perspective-distorted quadrilateral (your known real-world rectangle) into a perfectly axis-aligned rectangle (the bird’s-eye view).
- Instead, you’re mapping that distorted quadrilateral to its own axis-aligned bounding box within the original image. That’s backwards! This misdirects the homography matrix, leading to those extreme, nonsensical corner coordinates you’re seeing (like (5415, 2218) for a corner).
A Secondary Check: Are Your Point Orders Correct?
You mentioned using TL→TR→BR→BL order, but it’s worth verifying for your problematic sample. If the points are out of order (e.g., swapping BL and BR), cv2.getPerspectiveTransform will spit out a homography that twists, flips, or stretches the image in unexpected ways. A quick way to check is to draw a polyline connecting the points and see if it forms a logical rectangle.
Corrected Implementation
Let’s rewrite the logic to map the distorted quadrilateral to a proper bird’s-eye rectangle, then adjust the output size to avoid cropping:
import numpy as np import cv2 import imutils def warpImage(image, src_pts): height, width = image.shape[:2] src_np = np.float32(src_pts) # Step 1: Define the target bird's-eye rectangle # Use the actual width/height of the real-world rectangle if you know it (e.g., [600, 400]) # If not, calculate the "corrected" dimensions from the source points target_width = np.linalg.norm(src_np[0] - src_np[1]) # TL to TR distance target_height = np.linalg.norm(src_np[0] - src_np[3]) # TL to BL distance dst_pts = np.float32([ [0, 0], # TL [target_width, 0], # TR [target_width, target_height], # BR [0, target_height] # BL ]) # Step 2: Compute initial homography to unwarp the quadrilateral M = cv2.getPerspectiveTransform(src_np, dst_pts) # Step 3: Find where the original image's corners land after transformation img_corners = np.float32([[0,0], [width-1,0], [width-1,height-1], [0,height-1]]).reshape(-1,1,2) transformed_corners = cv2.perspectiveTransform(img_corners, M) # Calculate the bounds of the transformed image to avoid cropping min_x = np.min(transformed_corners[:, 0, 0]) min_y = np.min(transformed_corners[:, 0, 1]) max_x = np.max(transformed_corners[:, 0, 0]) max_y = np.max(transformed_corners[:, 0, 1]) # Shift everything into positive coordinate space offset_x = -min_x offset_y = -min_y output_width = int(max_x - min_x) output_height = int(max_y - min_y) # Combine the homography with a translation to shift the image translate_matrix = np.float32([[1, 0, offset_x], [0, 1, offset_y], [0, 0, 1]]) final_M = translate_matrix @ M # Perform the warp warped_image = cv2.warpPerspective(image, final_M, (output_width, output_height)) return warped_image # Test with your samples img = cv2.imread("grid.png", 1) img2 = img.copy() # Working sample src1 = [[262, 129], [695, 130], [770, 350], [171, 352]] for pt in src1: cv2.circle(img, (pt[0], pt[1]), 3, (5, 140, 205), 3) cv2.imshow("img", img) warped1 = warpImage(img, src1) warped1 = imutils.resize(warped1, width=800 if warped1.shape[1] > warped1.shape[0] else height=800) cv2.imshow("warped1", warped1) # Problematic sample (add polyline to verify point order) src2 = [[263, 260], [450, 302], [364, 394], [142, 321]] cv2.polylines(img2, [np.array(src2, np.int32)], isClosed=True, color=(0,255,0), thickness=2) for pt in src2: cv2.circle(img2, (pt[0], pt[1]), 3, (5, 140, 205), 3) cv2.imshow("img2", img2) warped2 = warpImage(img2, src2) warped2 = imutils.resize(warped2, width=800 if warped2.shape[1] > warped2.shape[0] else height=800) cv2.imshow("warped2", warped2) cv2.waitKey(0) cv2.destroyAllWindows()
Quick Additional Checks
- Verify Point Order: The polyline drawn on your problematic sample will show if the points connect into a valid rectangle. If it looks twisted, reorder the points to follow TL→TR→BR→BL strictly.
- Use Real-World Dimensions: If you know the actual width and height of the rectangle in real life (e.g., 100cm x 50cm), replace
target_widthandtarget_heightwith those values (scaled to pixels if needed) for accurate distance calculations. - Inspect the Homography: Print out the initial
Mmatrix for your problematic sample. If you see extreme values (like 1000+), that’s a dead giveaway that your source/target point pairing is wrong.
内容的提问来源于stack exchange,提问作者Horizon1710

