Java OpenCV纠正轮廓

我在检测特定类型的对象方面取得了一些进展。 实际上是一张卡片,就像钱包中的任何其他卡片一样。

现在我一直在纠结这张照片。 看到:

在此处输入图像描述

蓝色(圆角)矩形表示检测到的轮廓。 紫色旋转矩形表示从检测到的轮廓中提取的RotatedRect 。 绿线就是边界框。

好吧,我不需要那些矩形。 矩形都有90度角。 哪个不会让我看到这个观点。

我的问题:

如何从轮廓中获得所有四边形角的尽可能准确?

我创建了一个类Quadrangle ,它创建了4个最大的连接多边形顶点的四边形,这些顶点将在某个点相互交叉。 这几乎适用于任何情况。

如果您使用此代码,请记住在Quadrangle.warp调整宽度和高度。 请注意,它不是100%完整,如果它们可能是连接的,则第一个和最后一个多边形顶点将不会连接。

 import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.opencv.core.*; import org.opencv.imgproc.Imgproc; class Line { public Point offset; public double angle; public Line(Point offset, double angle) { this.offset = offset.clone(); this.angle = angle; } public Point get(int length) { Point result = offset.clone(); result.x += Math.cos(angle) * length; result.y += Math.sin(angle) * length; return result; } public Point getStart() { return get(-5000); } public Point getEnd() { return get(5000); } public void scale(double factor) { offset.x *= factor; offset.y *= factor; } public static Point intersect(Line l1, Line l2) { return getLineLineIntersection(l1.getStart().x, l1.getStart().y, l1.getEnd().x, l1.getEnd().y, l2.getStart().x, l2.getStart().y, l2.getEnd().x, l2.getEnd().y ); } public static Point getLineLineIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { double det1And2 = det(x1, y1, x2, y2); double det3And4 = det(x3, y3, x4, y4); double x1LessX2 = x1 - x2; double y1LessY2 = y1 - y2; double x3LessX4 = x3 - x4; double y3LessY4 = y3 - y4; double det1Less2And3Less4 = det(x1LessX2, y1LessY2, x3LessX4, y3LessY4); if (det1Less2And3Less4 == 0){ // the denominator is zero so the lines are parallel and there's either no solution (or multiple solutions if the lines overlap) so return null. return null; } double x = (det(det1And2, x1LessX2, det3And4, x3LessX4) / det1Less2And3Less4); double y = (det(det1And2, y1LessY2, det3And4, y3LessY4) / det1Less2And3Less4); return new Point(x, y); } protected static double det(double a, double b, double c, double d) { return a * d - b * c; } } class LineSegment extends Line implements Comparable { public double length; public LineSegment(Point offset, double angle, double length) { super(offset, angle); this.length = length; } public void melt(LineSegment segment) { Point point = new Point(); point.x += Math.cos(angle) * length; point.y += Math.sin(angle) * length; point.x += Math.cos(segment.angle) * segment.length; point.y += Math.sin(segment.angle) * segment.length; angle = Math.atan2(point.y, point.x); offset.x = (offset.x * length + segment.offset.x * segment.length) / (length + segment.length); offset.y = (offset.y * length + segment.offset.y * segment.length) / (length + segment.length); length += segment.length; } @Override public int compareTo(Object other) throws ClassCastException { if (!(other instanceof LineSegment)) { throw new ClassCastException("A LineSegment object expected."); } return (int) (((LineSegment) other).length - this.length); } } class Quadrangle { static int TOP = 0, RIGHT = 1, BOTTOM = 2, LEFT = 3; public Line[] lines = new Line[4]; public Quadrangle() { } private static double getAngle(Point p1, Point p2) { return Math.atan2(p2.y - p1.y, p2.x - p1.x); } private static double getLength(Point p1, Point p2) { return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)); } private static double roundAngle(double angle) { return angle - (2*Math.PI) * Math.round(angle / (2 * Math.PI)); } public static Quadrangle fromContour(MatOfPoint contour) { List points = contour.toList(); List segments = new ArrayList<>(); // Create line segments for (int i = 0; i < points.size(); i++) { double a = getAngle(points.get(i), points.get((i + 1) % points.size())); double l = getLength(points.get(i), points.get((i + 1) % points.size())); segments.add(new LineSegment(points.get(i), a, l)); } // Connect line segments double angleDiffMax = 2 * Math.PI / 100; List output = new ArrayList<>(); for (LineSegment segment : segments) { if (output.isEmpty()) { output.add(segment); } else { LineSegment top = output.get(output.size() - 1); double d = roundAngle(segment.angle - top.angle); if (Math.abs(d) < angleDiffMax) { top.melt(segment); } else { output.add(segment); } } } Collections.sort(output); Quadrangle quad = new Quadrangle(); for (int o = 0; o < 4; o += 1) { for (int i = 0; i < 4; i++) { if (Math.abs(roundAngle(output.get(i).angle - (2 * Math.PI * o / 4))) < Math.PI / 4) { quad.lines[o] = output.get(i); } } } return quad; } public void scale(double factor) { for (int i = 0; i < 4; i++) { lines[i].scale(factor); } } public Mat warp(Mat src) { Mat result = src.clone(); Core.line(result, lines[TOP].get(-5000), lines[TOP].get(5000), new Scalar(200, 100, 100), 8); Core.line(result, lines[RIGHT].get(-5000), lines[RIGHT].get(5000), new Scalar(0, 255, 0), 8); Core.line(result, lines[BOTTOM].get(-5000), lines[BOTTOM].get(5000), new Scalar(255, 0, 0), 8); Core.line(result, lines[LEFT].get(-5000), lines[LEFT].get(5000), new Scalar(0, 0, 255), 8); Point p = Line.intersect(lines[TOP], lines[LEFT]); System.out.println(p); if (p != null) { Core.circle(result, p, 30, new Scalar(0, 0, 255), 8); } double width = 1400; double height = width / 2.15; Point[] srcProjection = new Point[4], dstProjection = new Point[4]; srcProjection[0] = Line.intersect(lines[TOP], lines[LEFT]); srcProjection[1] = Line.intersect(lines[TOP], lines[RIGHT]); srcProjection[2] = Line.intersect(lines[BOTTOM], lines[LEFT]); srcProjection[3] = Line.intersect(lines[BOTTOM], lines[RIGHT]); dstProjection[0] = new Point(0, 0); dstProjection[1] = new Point(width - 1, 0); dstProjection[2] = new Point(0, height - 1); dstProjection[3] = new Point(width - 1, height - 1); Mat warp = Imgproc.getPerspectiveTransform(new MatOfPoint2f(srcProjection), new MatOfPoint2f(dstProjection)); Mat rotated = new Mat(); Size size = new Size(width, height); Imgproc.warpPerspective(src, rotated, warp, size, Imgproc.INTER_LINEAR); return rotated; } }