I've recently implemented Perspective Transform in OpenCV to my app in Android. Almost everything works without issues but one aspect needs much more work to be done.
The problem is that I do not know how to count the right aspect ratio of the destination image of Perspective Transform (it does not have to be set manually), so that it could count the aspect ratio of the image to the size of the real thing/image despite the angle of a camera. Note that the starting coordinates do not form trapezoid, it does form a quadrangle.
If I have a photograph of a book taken from approximately 45 degrees and I want the destination image aspect ratio to be pretty much the same as this book's aspect ratio is. It is hard to do having a 2D photo, but CamScanner app does it perfectly. I've made very simple way to count the size of my destination image (with no expectations for it to work as I want), but it makes the image from 45 degree angle about 20% shorter and when lowering the angle the image height reduces significantly, while CamScanner does it perfectly despite the angle:
Here, CamScanner maintains the aspect ratio of the destination image (second one) the same as the book's, it did pretty accurately even at ~20 degree angle.
Meanwhile, my code looks like this (while counting sizes of destination image I have no intention for it to work as I ask in this question):
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;
}
And from relatively the same angle my method produces this result:
What I want to know is how it is possible to do? It is interesting for me how did they manage to count the length of the object just by having coordinates of 4 corners. Also, if it is possible, please provide some code/ mathematical explanations or articles of similar/same thing.
Thank you in advance.