opencv纠正轮廓 [英] opencv deskewing a contour

查看:170
本文介绍了opencv纠正轮廓的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

InputImage

InputImage

ResultImage

ResultImage

我已经能够过滤最大的轮廓了用于检测令牌的图像。

I have been able to filter the largest contour in the image to detect the token.

我已经应用了扭曲感知,但它只是在轮廓边缘裁剪图像,没有别的。

I have applied warp perception but it is only cropping the image at the edges of the contour, nothing else.

我希望将检测到的令牌从图像的其余部分中裁剪出来,在保持比例的同时对其进行去偏斜,这样结果图像应该是直立的。然后我将继续寻找令牌中的blob以检测其中标记的日期。

I want the detected token to be cropped out of the rest of the image entireley, de-skew it while keeping proportions so the result image should be upright, straight. Then I will move forward with finding the blobs in the token to detect the dates marked inside it.

    private Mat processMat(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<>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(processedMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

    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(srcMat, contours, maxValIdx, new Scalar(0,255,0),  3);
        Rect rect = Imgproc.boundingRect(contours.get(maxValIdx));
        Log.e("rect", "" + rect);
        int top = srcMat.height();
        int left = srcMat.width();
        int right = 0;
        int bottom = 0;

        if(rect.x < left) {
            left = rect.x;
        }
        if(rect.x+rect.width > right){
            right = rect.x+rect.width;
        }
        if(rect.y < top){
            top = rect.y;
        }
        if(rect.y+rect.height > bottom){
            bottom = rect.y+rect.height;
        }

        Point topLeft = new Point(left, top);
        Point topRight = new Point(right, top);
        Point bottomRight = new Point(right, bottom);
        Point bottomLeft = new Point(left, bottom);

        return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight);
    }
    return null;
}

Mat warp(Mat inputMat, Point topLeft, Point topRight, Point bottomLeft, Point bottomRight) {
    int resultWidth = (int)(topRight.x - topLeft.x);
    int bottomWidth = (int)(bottomRight.x - bottomLeft.x);
    if(bottomWidth > resultWidth)
        resultWidth = bottomWidth;

    int resultHeight = (int)(bottomLeft.y - topLeft.y);
    int bottomHeight = (int)(bottomRight.y - topRight.y);
    if (bottomHeight > resultHeight) {
        resultHeight = bottomHeight;
    }

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

    List<Point> source = new ArrayList<>();
    source.add(topLeft);
    source.add(topRight);
    source.add(bottomLeft);
    source.add(bottomRight);
    Mat startM = Converters.vector_Point2f_to_Mat(source);

    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<>();
    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));
    return outputMat;
}

更新1

替换为:

        return warp(srcMat, topLeft, topRight, bottomLeft, bottomRight);

使用此:

        return warp(srcMat, topLeft, topRight, bottomRight, bottomLeft);

结果更新1:

更新2

    public Mat warp(Mat inputMat, MatOfPoint selectedContour) {
    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 p2 = new Point(temp_double[0], temp_double[1]);
    temp_double = approxCurve_temp.get(2,0);
    Point p3 = new Point(temp_double[0], temp_double[1]);
    temp_double = approxCurve_temp.get(3,0);
    Point p4 = 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);
    Mat startM = Converters.vector_Point2f_to_Mat(source);

    int resultWidth = 846;
    int resultHeight = 2048;

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

    Point ocvPOut1 = new Point(0, 0);
    Point ocvPOut2 = new Point(0, resultHeight);
    Point ocvPOut3 = new Point(resultWidth, resultHeight);
    Point ocvPOut4 = new Point(resultWidth, 0);
    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),
            Imgproc.INTER_CUBIC);
    return outputMat;
}

结果更新2:

我已经改变了我的warp函数并附加了代码。
但是生成的图像以某种方式以错误的方向旋转。你可以指导我这是正确的方法。

I have changed my warp function a bit and the code is attached. However the resultant image is rotated somehow in the wrong direction. Can you guide me which is the correct way to do this.

Android设备方向设置为:portrait,输入图像也是纵向。

Android device orientation is set to: portrait and the input image is in portrait as well.

更新3

我设法通过对角落进行排序来拉直令牌:

I have managed to straighten the token by sorting the corners like so:

    List<Point> source = new ArrayList<Point>();
    source.add(p2);
    source.add(p3);
    source.add(p4);
    source.add(p1);
    Mat startM = Converters.vector_Point2f_to_Mat(source);

结果更新3:

然而,从左侧裁剪得到的图像,我不知道如何解决这个问题。
如果令牌向右或向左倾斜并且输出图像是直的,我已设法拉直输入图像。但是,如果输入图像已经使令牌居中并且直线上升。它使用相同的代码旋转令牌:

However the resultant image is cropped from the left side which I have no idea how to tackle that. I have managed to straighten the input image if the token is tilted to the right or left and the output image is straight nonetheless. However if the input image already has the token centred and straight up. it rotates the token like so, using the same code:

问题更新3:

推荐答案

对票证进行校正的转换接近于仿制票。您可以通过用平行四边形近似轮廓来获得它。您会发现平行四边形的顶点是最左侧,最顶部,最右侧和最底部的点。

The transformation to deskew the ticket is close to an affine one. You can obtain it by approximating the outline with a parallelogram. You find the vertices of the parallelogram as the leftmost, topmost, rightmost and bottommost points.

实际上,您只需要三个顶点(第四个可以从这些顶点重新计算) 。也许平行四边形的最小二乘拟合是可能的,我不知道。

Actually, you just need three vertices (and the fourth can be recomputed from these). Maybe a least-square fitting of the parallelogram is possible, I don't know.

另一个选择是考虑一个单应变换,它是从四个点定义的(但是计算要复杂得多)。它将考虑到视角。 (您可能会在此处获得一些见解: https:// www.codeproject.com/Articles/674433/Perspective-Projection-of-a-Rectangle-Homography 。)

Another option is to consider an homographic transform, which is defined from four points (but the computation is much more complex). It will take perspective into account. (You might get some insight here: https://www.codeproject.com/Articles/674433/Perspective-Projection-of-a-Rectangle-Homography.)

为了理顺图像,它就足够了应用逆变换并检索矩形。无论如何,您会注意到这个矩形的大小是未知的,因此您可以任意缩放它。最难的问题是找到合适的宽高比。

To straighten up the image, it suffice to apply the inverse transform and retrieve a rectangle. Anyway, you will notice that the size of this rectangle is unknown, so that you can scale it arbitrarily. The hardest issue is to find a suitable aspect ratio.

这篇关于opencv纠正轮廓的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆