如何将四边形上的CGPoint转换到矩形坐标系并计算对应点坐标
解决方案
实现思路
你要实现的是平面透视投影变换,核心是求解单应性矩阵(Homography Matrix),无需依赖系统的CIPerspectiveCorrection接口,也不需要引入第三方库,纯原生代码即可实现:
- 首先确定目标输出矩形的四个顶点坐标(可自定义尺寸,也可以直接用原四边形的包围盒尺寸作为输出矩形大小)
- 利用原四边形4个顶点和目标矩形4个顶点的对应关系,求解3x3的单应性变换矩阵
- 用得到的变换矩阵对E点做坐标映射,即可得到其在目标矩形上的对应坐标
核心代码实现(Swift)
import CoreGraphics // 求解单应性矩阵 func computeHomography(from srcPoints: [CGPoint], to dstPoints: [CGPoint]) -> [CGFloat]? { guard srcPoints.count == 4, dstPoints.count == 4 else { return nil } var A = Array(repeating: Array(repeating: 0.0, count: 8), count: 8) var B = Array(repeating: 0.0, count: 8) for i in 0..<4 { let x = srcPoints[i].x let y = srcPoints[i].y let xPrime = dstPoints[i].x let yPrime = dstPoints[i].y A[2*i][0] = x A[2*i][1] = y A[2*i][2] = 1 A[2*i][6] = -x * xPrime A[2*i][7] = -y * xPrime B[2*i] = xPrime A[2*i + 1][3] = x A[2*i + 1][4] = y A[2*i + 1][5] = 1 A[2*i + 1][6] = -x * yPrime A[2*i + 1][7] = -y * yPrime B[2*i + 1] = yPrime } // 高斯消元求解线性方程组 guard let h = gaussianElimination(A, B) else { return nil } // 单应性矩阵共9个元素,最后一个固定为1 return h + [1.0] } // 高斯消元实现 func gaussianElimination(_ A: [[CGFloat]], _ B: [CGFloat]) -> [CGFloat]? { let n = B.count var aug = A.enumerated().map { $0.element + [B[$0.offset]] } for i in 0..<n { // 找主元 var maxRow = i for j in i..<n { if abs(aug[j][i]) > abs(aug[maxRow][i]) { maxRow = j } } aug.swapAt(i, maxRow) // 主元为0则矩阵奇异,无解 if abs(aug[i][i]) < 1e-8 { return nil } // 归一化主元行 let div = aug[i][i] for j in i...n { aug[i][j] /= div } // 消去其他行 for j in 0..<n { if j != i && abs(aug[j][i]) > 1e-8 { let factor = aug[j][i] for k in i...n { aug[j][k] -= factor * aug[i][k] } } } } return aug.map { $0[n] } } // 用单应性矩阵变换点坐标 func applyHomography(_ h: [CGFloat], point: CGPoint) -> CGPoint { let x = point.x let y = point.y let w = h[6] * x + h[7] * y + h[8] let xPrime = (h[0] * x + h[1] * y + h[2]) / w let yPrime = (h[3] * x + h[4] * y + h[5]) / w return CGPoint(x: xPrime, y: yPrime) }
使用示例
// 1. 定义原四边形的四个顶点,顺序要和目标矩形对应:左上、右上、右下、左下 let srcPoints = [A, B, C, D] // 2. 定义目标矩形的四个顶点,这里示例输出1080x1920的矩形,你也可以自定义尺寸 let dstPoints = [ CGPoint(x: 0, y: 0), CGPoint(x: 1080, y: 0), CGPoint(x: 1080, y: 1920), CGPoint(x: 0, y: 1920) ] // 3. 求解单应性矩阵 guard let homography = computeHomography(from: srcPoints, to: dstPoints) else { print("变换矩阵求解失败,请检查输入点是否共线") return } // 4. 变换E点得到目标坐标 let targetPoint = applyHomography(homography, point: E)
注意事项
- 输入的原四边形4个点和目标矩形4个点的顺序必须严格对应,否则映射结果会出错
- 如果原四边形存在三点共线的情况,会导致矩阵求解失败,需要保证输入是有效的凸四边形
- 目标矩形的尺寸可以根据业务需求自定义,也可以直接计算原四边形的外接矩形尺寸作为输出大小
内容的提问来源于stack exchange,提问作者Oritm
相关产品推荐
相关产品推荐

