我最近在我的安卓应用中使用OpenCV实现了透视变换(Perspective Transform)。几乎一切都没有问题,但是其中一个方面需要更多的工作。
问题在于我不知道如何计算透视变换中目标图像的正确宽高比(无需手动设置),使其可以根据摄像头的角度计算出图像和真实物体/图像尺寸的宽高比(不考虑摄像头角度)。请注意,起始坐标并不形成梯形,而是形成四边形。
如果我有一本书以约45度角拍摄的照片,并且我想要目标图像的宽高比与这本书的宽高比相同。由于只有2D照片很难做到,但是CamScanner应用程序完美地实现了这一点。我已经找到了一种非常简单的方法来计算我的目标图像的大小(并没有期望它可以按照我想要的方式工作),但是它使得从45度角的图像短了大约20%,当角度降低时,图像高度显著降低,而CamScanner尽管角度不一样也能完美实现:
在这里,CamScanner保持目标图像(第二个)的宽高比与书本一样,即使在约20度的角度下也基本准确。
同时,我的代码看起来像这样(在计算目标图像大小时我并没有打算按照这个问题所要求的方式工作):
public static Mat PerspectiveTransform(Point[] cropCoordinates, float ratioW, float ratioH, Bitmap croppedImage)
{
if (cropCoordinates.length != 4) return null;
double width1, width2, height1, height2, avgw, avgh;
Mat src = new Mat();
List<Point> startCoords = new ArrayList<>();
List<Point> resultCoords = new ArrayList<>();
Utils.bitmapToMat(croppedImage, src);
for (int i = 0; i < 4; i++)
{
if (cropCoordinates[i].y < 0 ) new Point(cropCoordinates[i].x, 0);
startCoords.add(new Point(cropCoordinates[i].x * ratioW, cropCoordinates[i].y * ratioH));
}
width1 = Math.sqrt(Math.pow(startCoords.get(2).x - startCoords.get(3).x,2) + Math.pow(startCoords.get(2).y - startCoords.get(3).y,2));
width2 = Math.sqrt(Math.pow(startCoords.get(1).x - startCoords.get(0).x,2) + Math.pow(startCoords.get(1).y - startCoords.get(0).y,2));
height1 = Math.sqrt(Math.pow(startCoords.get(1).x - startCoords.get(2).x, 2) + Math.pow(startCoords.get(1).y - startCoords.get(2).y, 2));
height2 = Math.sqrt(Math.pow(startCoords.get(0).x - startCoords.get(3).x, 2) + Math.pow(startCoords.get(0).y - startCoords.get(3).y, 2));
avgw = (width1 + width2) / 2;
avgh = (height1 + height2) / 2;
resultCoords.add(new Point(0, 0));
resultCoords.add(new Point(avgw-1, 0));
resultCoords.add(new Point(avgw-1, avgh-1));
resultCoords.add(new Point(0, avgh-1));
Mat start = Converters.vector_Point2f_to_Mat(startCoords);
Mat result = Converters.vector_Point2d_to_Mat(resultCoords);
start.convertTo(start, CvType.CV_32FC2);
result.convertTo(result,CvType.CV_32FC2);
Mat mat = new Mat();
Mat perspective = Imgproc.getPerspectiveTransform(start, result);
Imgproc.warpPerspective(src, mat, perspective, new Size(avgw, avgh));
return mat;
}
而从相对同一角度,我的方法产生了这个结果:
我想知道的是如何实现?我很感兴趣,他们是如何通过仅有4个角的坐标来计算对象的长度。如果可能的话,请提供一些代码、数学说明或类似/相同的文章。
提前致谢。