透视变换需要源图像和目标图像各四个对应点,顺序严格为左上、右上、右下、左下。

透视变换需要哪四个点?
OpenCV 的 cv::getPerspectiveTransform 要求你提供源图像上的四个点(src_pts)和目标图像上对应的四个点(dst_pts),顺序必须严格一致:左上、右上、右下、左下。漏掉这个顺序,变换后内容会扭曲甚至翻转。
- 实际中这四个点通常来自轮廓检测后的最大四边形(比如用
cv::findContours+cv::approxPolyDP),但要注意:不是所有四边形都适合做透视源——如果检测出的点顺序是顺时针或乱序,cv::getPerspectiveTransform仍会计算,但结果不可控。 - 建议手动按逆时针排序(或用
cv::rotatedRect.boundingRect()辅助校验),或者直接用cv::minAreaRect获取旋转矩形再取角点,再按左上→右上→右下→左下重排。 - 如果四个点共线或接近共线(比如拍文档时只歪了一条边),
cv::getPerspectiveTransform返回的矩阵行列式接近零,后续cv::warpPerspective可能崩溃或输出全黑图。
cv::warpPerspective 输出全黑或错位怎么办?
最常见原因是目标尺寸设得太小,或 ROI 超出目标图像边界。
-
cv::warpPerspective的第三个参数是输出图像尺寸(cv::Size(width, height)),必须显式指定;若传入原图尺寸,而透视后内容被“拉伸”到画布外,就只剩黑边。 - 正确做法是先用
cv::perspectiveTransform把四个dst_pts映射到新坐标系,再用cv::boundingRect算出最小包围矩形,以此作为输出尺寸。 - 另一个坑:输入图像类型必须是连续内存(
img.isContinuous()为 true),否则warpPerspective可能静默失败。可用img.clone()强制连续。
如何自动提取文档四角点?
纯靠 cv::Canny + cv::findContours 容易受阴影、折痕、背景干扰,导致选错轮廓。
- 先做自适应阈值(
cv::adaptiveThreshold)比全局阈值更鲁棒;再用cv::morphologyEx开运算去噪,闭运算补断线。 - 轮廓筛选条件要具体:面积不能太小(排除噪点),也不能太大(排除整张图),周长/面积比接近 4(正方形)或按宽高比限定(如 0.5–2.0)。
-
cv::approxPolyDP的epsilon参数很关键:设为轮廓弧长的 1%~3%,太小会拟合成几十个点,太大则无法收敛成四边形。建议从0.02 * cv::arcLength(contour, true)开始试。
OpenCV 4.x 和 C++17 下的内存与类型陷阱
cv::Point2f 数组必须用 std::vector<:point2f></:point2f> 传给 cv::getPerspectiveTransform,不能用原始数组指针(C 风格),否则 OpenCV 内部可能越界读取。
- 所有
cv::Mat输入需确保是CV_8UC1(灰度)或CV_8UC3(彩色),不能是CV_32F—— 即使你做了归一化,warpPerspective默认不支持浮点输入(除非显式指定插值标志和深度)。 - 插值方式选
cv::INTER_LINEAR(默认)即可,cv::INTER_CUBIC对文档纠偏无实质提升,反而慢;若目标图要缩放,记得同步调整dst_pts坐标比例。 - 多次 warp 后累积误差明显,别反复对同一图做透视变换;应始终基于原始图计算变换矩阵。
实际调试时,把 src_pts 和 dst_pts 用 cv::circle 和 cv::line 画在原图上,肉眼确认顺序和位置——这点比调参重要得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











