OpenCV:warpAffine/warpPerspective后像素坐标映射计算方法
嘿,这就给你搞定这两个变换的像素映射计算,直接上能跑的代码和清晰解释:
计算透视变换与仿射变换后的像素对应位置
一、透视变换的像素位置计算
透视变换依赖的是3x3的齐次变换矩阵,所以必须把原始像素点(x,y)转换成齐次坐标[x, y, 1],再和变换矩阵做乘法,最后通过归一化得到目标位置。
下面是适配你代码的newPointLocation Java实现:
import org.opencv.core.Mat; import org.opencv.core.Point; import org.opencv.core.Core; public Point newPointLocation(Point p, Mat transform) { // 构造齐次坐标向量 [x, y, 1] Mat srcPoint = new Mat(3, 1, transform.type()); srcPoint.put(0, 0, p.x); srcPoint.put(1, 0, p.y); srcPoint.put(2, 0, 1.0); // 执行矩阵乘法:变换矩阵 × 齐次坐标向量 Mat dstPoint = new Mat(); Core.gemm(transform, srcPoint, 1.0, new Mat(), 0.0, dstPoint); // 透视变换需要归一化:用前两个分量除以第三个分量W double targetX = dstPoint.get(0, 0)[0] / dstPoint.get(2, 0)[0]; double targetY = dstPoint.get(1, 0)[0] / dstPoint.get(2, 0)[0]; // 释放临时创建的Mat对象,避免内存泄漏 srcPoint.release(); dstPoint.release(); return new Point(targetX, targetY); }
关键说明:
- 透视变换的矩阵是3x3结构,只有用齐次坐标计算才能保留透视投影的信息
- 乘法结果的第三个分量W不为1,必须通过归一化得到真实的像素坐标
- 记得手动释放临时Mat,OpenCV的Java版本不会自动回收这些对象
二、仿射变换的像素位置计算
仿射变换用的是2x3的矩阵,同样可以用齐次坐标逻辑计算,不过因为仿射变换保持平行性,结果的第三个分量始终为1,所以不需要归一化。这里提供两种实现方式:
方式1:补全矩阵的通用实现
import org.opencv.core.Mat; import org.opencv.core.Point; import org.opencv.core.Core; public Point newPointLocation(Point p, Mat rotImage) { // 构造齐次坐标向量 [x, y, 1] Mat srcPoint = new Mat(3, 1, rotImage.type()); srcPoint.put(0, 0, p.x); srcPoint.put(1, 0, p.y); srcPoint.put(2, 0, 1.0); // 将2x3的仿射矩阵补全为3x3的齐次矩阵(最后一行固定为[0,0,1]) Mat affineMatrix = new Mat(3, 3, rotImage.type()); rotImage.copyTo(affineMatrix.rowRange(0, 2)); affineMatrix.put(2, 0, 0.0); affineMatrix.put(2, 1, 0.0); affineMatrix.put(2, 2, 1.0); // 执行矩阵乘法 Mat dstPoint = new Mat(); Core.gemm(affineMatrix, srcPoint, 1.0, new Mat(), 0.0, dstPoint); // 直接取前两个分量作为目标坐标 double targetX = dstPoint.get(0, 0)[0]; double targetY = dstPoint.get(1, 0)[0]; // 释放临时对象 srcPoint.release(); affineMatrix.release(); dstPoint.release(); return new Point(targetX, targetY); }
方式2:手动计算的轻量实现
如果追求性能,可以跳过矩阵创建,直接用矩阵元素计算:
import org.opencv.core.Mat; import org.opencv.core.Point; public Point newPointLocation(Point p, Mat rotImage) { // 提取仿射矩阵的6个元素:第一行[a,b,c],第二行[d,e,f] double[] matrixData = rotImage.get(0, 0); double targetX = matrixData[0] * p.x + matrixData[1] * p.y + matrixData[2]; double targetY = matrixData[3] * p.x + matrixData[4] * p.y + matrixData[5]; return new Point(targetX, targetY); }
关键说明:
- 仿射变换的本质是线性变换+平移,手动计算的方式更高效,适合对性能敏感的场景
- 两种实现的计算结果完全一致,可根据代码风格和性能需求选择
内容的提问来源于stack exchange,提问作者Elia Weiss
相关产品推荐
相关产品推荐

