计算透视变换目标图像的宽高比

我最近在OpenCV中实现了透视转换到我的Android应用程序。 几乎所有的工作都没有问题,但一方面需要做更多的工作。

问题是我不知道如何计算透视变换的目标图像的正确高宽比(不需要手动设置),以便它可以计算图像的高宽比与真实的大小东西/图像, 尽pipe相机的angular度 。 请注意,起始坐标不形成梯形,它确实形成一个四边形。

如果我有一张从大约45度拍摄的书的照片,我希望目标图像的纵横比与本书的纵横比几乎相同。 有2D照片很难,但CamScanner应用程序完美。 我已经做了非常简单的方法来计算我的目标图像的大小(没有期望它的工作,因为我想),但它使图像从45度angular约20%,当降低angular度的图像高度减less很显然,虽然CamScanner可以完美的完成angular度,

在这里输入图像说明

在这里,CamScanner保持目标图像(第二个)的纵横比与书本相同,即使在20度的angular度下,它也相当准确。

同时,我的代码看起来像这样(虽然计数目标图像的大小,我没有打算它的工作,因为我问这个问题):

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; } 

从相同的angular度我的方法产生这个结果:

在这里输入图像说明

我想知道的是如何做到这一点? 对我来说,有趣的是他们是如何通过具有4个angular的坐标来计算对象的长度的。 另外,如果可能,请提供一些代码/math解释或类似/相同的东西的文章。

先谢谢你。

Solutions Collecting From Web of "计算透视变换目标图像的宽高比"

这已经出现了几次之前,但我从来没有看到一个完整的答案,所以在这里。 这里显示的实现是基于这个文件,它可以得到完整的等式: http : //research.microsoft.com/en-us/um/people/zhang/papers/tr03-39.pdf

从本质上讲,它表明,假设一个针孔照相机模型,有可能计算投影矩形的长宽比(但不是比例尺,毫不意外)。 从本质上讲,可以解决焦距问题,从而获得高宽比。 以下是使用OpenCV的python示例实现。 请注意,您需要以正确的顺序检测到4个angular落,否则将无法工作(请注意顺序,它是曲折的)。 报告的错误率在3-5%的范围内。

 import math import cv2 import scipy.spatial.distance import numpy as np img = cv2.imread('img.png') (rows,cols,_) = img.shape #image center u0 = (cols)/2.0 v0 = (rows)/2.0 #detected corners on the original image p = [] p.append((67,74)) p.append((270,64)) p.append((10,344)) p.append((343,331)) #widths and heights of the projected image w1 = scipy.spatial.distance.euclidean(p[0],p[1]) w2 = scipy.spatial.distance.euclidean(p[2],p[3]) h1 = scipy.spatial.distance.euclidean(p[0],p[2]) h2 = scipy.spatial.distance.euclidean(p[1],p[3]) w = max(w1,w2) h = max(h1,h2) #visible aspect ratio ar_vis = float(w)/float(h) #make numpy arrays and append 1 for linear algebra m1 = np.array((p[0][0],p[0][1],1)).astype('float32') m2 = np.array((p[1][0],p[1][1],1)).astype('float32') m3 = np.array((p[2][0],p[2][1],1)).astype('float32') m4 = np.array((p[3][0],p[3][1],1)).astype('float32') #calculate the focal disrance k2 = np.dot(np.cross(m1,m4),m3) / np.dot(np.cross(m2,m4),m3) k3 = np.dot(np.cross(m1,m4),m2) / np.dot(np.cross(m3,m4),m2) n2 = k2 * m2 - m1 n3 = k3 * m3 - m1 n21 = n2[0] n22 = n2[1] n23 = n2[2] n31 = n3[0] n32 = n3[1] n33 = n3[2] f = math.sqrt(- (1.0/(n23*n33)) * ((n21*n31 - (n21*n33 + n23*n31)*u0 + n23*n33*u0*u0) + (n22*n32 - (n22*n33+n23*n32)*v0 + n23*n33*v0*v0))) A = np.array([[f,0,u0],[0,f,v0],[0,0,1]]).astype('float32') At = np.transpose(A) Ati = np.linalg.inv(At) Ai = np.linalg.inv(A) #calculate the real aspect ratio ar_real = math.sqrt(np.dot(np.dot(np.dot(n2,Ati),Ai),n2)/np.dot(np.dot(np.dot(n3,Ati),Ai),n3)) if ar_real < ar_vis: W = int(w) H = int(W / ar_real) else: H = int(h) W = int(ar_real * H) pts1 = np.array(p).astype('float32') pts2 = np.float32([[0,0],[W,0],[0,H],[W,H]]) #project the image with the new w/h M = cv2.getPerspectiveTransform(pts1,pts2) dst = cv2.warpPerspective(img,M,(W,H)) cv2.imshow('img',img) cv2.imshow('dst',dst) cv2.imwrite('orig.png',img) cv2.imwrite('proj.png',dst) cv2.waitKey(0) 

原版的:

在这里输入图像说明

预计(从我的截图中裁剪图像的分辨率是非常低的,但长宽比似乎是正确的):

在这里输入图像说明

感谢y300和这个职位https://stackoverflow.com/a/1222855/8746860我得到它在Java中实现&#x3002; 我会离开这里,以防有人遇到同样的问题,我把它转换成Java …

 public float getRealAspectRatio(int imageWidth, int imageHeight) { double u0 = imageWidth/2; double v0 = imageHeight/2; double m1x = mTopLeft.x - u0; double m1y = mTopLeft.y - v0; double m2x = mTopRight.x - u0; double m2y = mTopRight.y - v0; double m3x = mBottomLeft.x - u0; double m3y = mBottomLeft.y - v0; double m4x = mBottomRight.x - u0; double m4y = mBottomRight.y - v0; double k2 = ((m1y - m4y)*m3x - (m1x - m4x)*m3y + m1x*m4y - m1y*m4x) / ((m2y - m4y)*m3x - (m2x - m4x)*m3y + m2x*m4y - m2y*m4x) ; double k3 = ((m1y - m4y)*m2x - (m1x - m4x)*m2y + m1x*m4y - m1y*m4x) / ((m3y - m4y)*m2x - (m3x - m4x)*m2y + m3x*m4y - m3y*m4x) ; double f_squared = -((k3*m3y - m1y)*(k2*m2y - m1y) + (k3*m3x - m1x)*(k2*m2x - m1x)) / ((k3 - 1)*(k2 - 1)) ; double whRatio = Math.sqrt( (Math.pow((k2 - 1),2) + Math.pow((k2*m2y - m1y),2)/f_squared + Math.pow((k2*m2x - m1x),2)/f_squared) / (Math.pow((k3 - 1),2) + Math.pow((k3*m3y - m1y),2)/f_squared + Math.pow((k3*m3x - m1x),2)/f_squared) ) ; if (k2==1 && k3==1 ) { whRatio = Math.sqrt( (Math.pow((m2y-m1y),2) + Math.pow((m2x-m1x),2)) / (Math.pow((m3y-m1y),2) + Math.pow((m3x-m1x),2))); } return (float)(whRatio); }