OpenCV warpPerspective旋转不一致

时间:2017-12-10 21:09:51

标签: android opencv image-processing

在找到最大轮廓(在这种情况下是优惠券)后,应用warpPerspective导致不一致的扭曲,它会在扭曲时向左旋转一些图像,但对于某些输入帧效果很好。

输入完美翘曲的输入图像

INPUT PERFECT WARPING

输出完美翘曲

OUTPUT PERFECT WARPING

输入翘曲声音的输入图像

INPUT MESSED-UP WARPING

输出消息翘曲 enter image description here

    private Mat processMatToFindLargestContourAndApplyWarp(Mat srcMat) {
    Mat processedMat = new Mat();
    Imgproc.cvtColor(srcMat, processedMat, Imgproc.COLOR_BGR2GRAY);
    Imgproc.GaussianBlur(processedMat, processedMat, new Size(5, 5), 5);
    Imgproc.threshold(processedMat, processedMat, 127, 255, Imgproc.THRESH_BINARY);
    List<MatOfPoint> contours = new ArrayList<>();

    Imgproc.findContours(processedMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_NONE);

    double maxVal = 0;
    int maxValIdx = 0;
    for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) {
        double contourArea = Imgproc.contourArea(contours.get(contourIdx));
        if (maxVal < contourArea) {
            maxVal = contourArea;
            maxValIdx = contourIdx;
        }
    }

    if (!contours.isEmpty()) {
        Imgproc.drawContours(processedMat, contours, maxValIdx, new Scalar(0, 255, 0), 3);
        return warp(srcMat, contours.get(maxValIdx));
    } else {
        Toast.makeText(this, "Error: Token contour not found", Toast.LENGTH_LONG).show();
    }
    return srcMat;
}

public Mat warp(Mat inputMat, MatOfPoint selectedContour) {
    Mat outputMat;
    try {
    MatOfPoint2f new_mat = new MatOfPoint2f(selectedContour.toArray());
    MatOfPoint2f approxCurve_temp = new MatOfPoint2f();
    int contourSize = (int) selectedContour.total();
    Imgproc.approxPolyDP(new_mat, approxCurve_temp, contourSize * 0.05, true);

    double[] temp_double;
    temp_double = approxCurve_temp.get(0,0);
    Point p1 = new Point(temp_double[0], temp_double[1]);
    temp_double = approxCurve_temp.get(1,0);
    Point p3 = new Point(temp_double[0], temp_double[1]);
    temp_double = approxCurve_temp.get(2,0);
    Point p4 = new Point(temp_double[0], temp_double[1]);
    temp_double = approxCurve_temp.get(3,0);
    Point p2 = new Point(temp_double[0], temp_double[1]);
    List<Point> source = new ArrayList<Point>();

    source.add(p1);
    source.add(p2);
    source.add(p3);
    source.add(p4);

    Log.e("inPoints", "" + source);
    Mat startM = Converters.vector_Point2f_to_Mat(source);

    int resultWidth = 846;
    int resultHeight = 2048;

    outputMat = new Mat(resultWidth, resultHeight, CvType.CV_8UC4);

        Point ocvPOut1 = new Point(0, 0);
        Point ocvPOut2 = new Point(resultWidth, 0);
        Point ocvPOut3 = new Point(0, resultHeight);
        Point ocvPOut4 = new Point(resultWidth, resultHeight);
    List<Point> dest = new ArrayList<Point>();
    dest.add(ocvPOut1);
    dest.add(ocvPOut2);
    dest.add(ocvPOut3);
    dest.add(ocvPOut4);
    Mat endM = Converters.vector_Point2f_to_Mat(dest);

    Mat perspectiveTransform = Imgproc.getPerspectiveTransform(startM, endM);
    Imgproc.warpPerspective(inputMat, outputMat, perspectiveTransform, new Size(resultWidth, resultHeight));
    } catch (Exception e) {
        return null;
    }
    return outputMat;
}

我尝试过切换边缘点,但是它适用于第二张图像而不适用于第一张图像。我是否必须检查并按顺序安排积分,如果是这样,任何人都可以指导我如何,或者有更好的方法来处理这个问题?

在我的日志结果中,Imgproc.approxPolyDP收集的输入源点对于具有优惠券的不同图像的排列不一致,即使是微小的角度。

1 个答案:

答案 0 :(得分:1)

我已经对这种情况进行了快速解决,我确信会有更好的方法来实现这一目标,但现在可以使用,修复方法是手动将轮廓点顺序排列到顺时针方向(即TopLeft)无论输入点排列如何,每次都是TopRight,BottomRight,BottomLeft。

如果有人遇到我的情况,这是代码

    private Point[] orderCorners(Point[] cornersUnordered) {
    Point[] cornerPoints = new Point[4];
    Point p1, p2, p3, p4;
    Point topLeft = null, topRight = null, botRight = null, botLeft = null;
    List<Point> corners = new ArrayList<Point>();
    for (int i=0; i < cornersUnordered.length; ++i)
        corners.add(cornersUnordered[i]);

    /* Top set of points */
    // find p1
    p1 = corners.get(0);
    for (Point point : corners) {
        if (point.y < p1.y) {
            p1 = point;
        }
    }
    corners.remove(p1);

    // find p2
    p2 = corners.get(0);
    for (Point point : corners) {
        if (distance(p1, point) < distance(p1, p2)) {
            p2 = point;
        }
    }
    corners.remove(p2);

    /* Identify top left and top right */
    /*
     * Note that the logic is safe if the points have equal x values. Safe
     * in the sense that different points will get assigned to topLeft and
     * topRight
     */
    topLeft = p1.x < p2.x ? p1 : p2;
    topRight = p2.x > p1.x ? p2 : p1;

    /* Bottom set of points */
    // corners only contains 2 points, the bottom ones
    p3 = corners.get(0);
    p4 = corners.get(1);
    botRight = p3.x > p4.x ? p3 : p4;
    botLeft = p4.x < p3.x ? p4 : p3;

    cornerPoints[0] = topLeft;
    cornerPoints[1] = topRight;
    cornerPoints[2] = botRight;
    cornerPoints[3] = botLeft;

    return cornerPoints;
}

private double distance(Point p1, Point p2) {
    return Math.sqrt(Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2));
}

这是上面修复的destinationPoints排列:

        Point ocvPOut1 = new Point(0, 0);
        Point ocvPOut2 = new Point(resultWidth, 0);
        Point ocvPOut3 = new Point(resultWidth, resultHeight);
        Point ocvPOut4 = new Point(0, resultHeight);

特别感谢@Alexander Reynolds