在不同情况下检测文档的四个角

时间:2017-09-13 06:47:31

标签: android opencv image-processing edge-detection

我尝试过两种方法: -

  1. 将图片转换为Mat

  2. 应用高斯模糊

  3. 然后canny边缘检测

  4. 查找轮廓

  5. 此方法的问题是:

    1. 检测到太多轮廓
    2. 大多是开放式轮廓
    3. 没有检测到我想要检测的内容
    4. 然后我改变了我的方法,并尝试了高斯模糊/中位模糊后的自适应阈值处理,它更好,我能够在50%的情况下检测角落

      我面临的当前问题是页面检测需要对比度和普通背景而没有任何反射。我认为它对于现实世界的使用来说过于理想化了。

      这是我想要帮助的地方。即使是朝向解决方案的方向也受到高度赞赏,尤其是在java中谢谢你的期待 这个

      具有明显的对比背景,效果非常好

      检测到4个角落 enter image description here 这张照片给出了麻烦,因为背景并不是最具对比度的 enter image description here

      找到最初的最大轮廓

      更新:中位数模糊并没有多大帮助,所以我追查了原因并发现页面边界是以零碎的方式检测到的,而不是单个轮廓,所以它检测到最大的轮廓作为因此,页面边界执行了一些形态学操作以关闭相对较小的间隙,所得到的最大轮廓肯定会得到改善,但它并不是最佳的。我有什么想法可以改善差距吗? enter image description here

      变形的原始图片

      enter image description here

      在变形图像中找到的最大轮廓

      PS在理想情况下变形图像导致检测到错误的轮廓边界。在变形图像之前可以检查的任何条件也是奖励。谢谢

3 个答案:

答案 0 :(得分:2)

如果你使用这样的方法:

public static RotatedRect getBestRectByArea(List<RotatedRect> boundingRects) {
    RotatedRect bestRect = null;

    if (boundingRects.size() >= 1) {
        RotatedRect boundingRect;
        Point[] vertices = new Point[4];
        Rect rect;
        double maxArea;
        int ixMaxArea = 0;

        // find best rect by area
        boundingRect = boundingRects.get(ixMaxArea);
        boundingRect.points(vertices);
        rect = Imgproc.boundingRect(new MatOfPoint(vertices));
        maxArea = rect.area();

        for (int ix = 1; ix < boundingRects.size(); ix++) {
            boundingRect = boundingRects.get(ix);
            boundingRect.points(vertices);
            rect = Imgproc.boundingRect(new MatOfPoint(vertices));

            if (rect.area() > maxArea) {
                maxArea = rect.area();
                ixMaxArea = ix;
            }
        }

        bestRect = boundingRects.get(ixMaxArea);
    }

    return bestRect;
}

private static Bitmap findROI(Bitmap sourceBitmap) {
    Bitmap roiBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(), sourceBitmap.getHeight(), Bitmap.Config.ARGB_8888);

    Mat sourceMat = new Mat(sourceBitmap.getWidth(), sourceBitmap.getHeight(), CV_8UC3);
    Utils.bitmapToMat(sourceBitmap, sourceMat);

    final Mat mat = new Mat();
    sourceMat.copyTo(mat);

    Imgproc.cvtColor(mat, mat, Imgproc.COLOR_RGB2GRAY);
    Imgproc.threshold(mat, mat, 146, 250, Imgproc.THRESH_BINARY);

    // find contours
    List<MatOfPoint> contours = new ArrayList<>();
    List<RotatedRect> boundingRects = new ArrayList<>();
    Imgproc.findContours(mat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

    // find appropriate bounding rectangles
    for (MatOfPoint contour : contours) {
        MatOfPoint2f areaPoints = new MatOfPoint2f(contour.toArray());
        RotatedRect boundingRect = Imgproc.minAreaRect(areaPoints);
        boundingRects.add(boundingRect);
    }

    RotatedRect documentRect = getBestRectByArea(boundingRects);
    if (documentRect != null) {
        Point rect_points[] = new Point[4];
        documentRect.points(rect_points);
        for (int i = 0; i < 4; ++i) {
            Imgproc.line(sourceMat, rect_points[i], rect_points[(i + 1) % 4], ROI_COLOR, ROI_WIDTH);
        }
    }
    Utils.matToBitmap(sourceMat, roiBitmap);
    return roiBitmap;
}

您可以为您的源图像获得如下结果:

enter image description here

或那:

enter image description here

如果您调整阈值并应用过滤器,则可以获得更好的效果。

答案 1 :(得分:1)

您可以使用以下一项或两项来选择一个轮廓:

  • 使用BoundingRectContourArea评估squareness of each contourboundingRect()返回正交rects。,为了处理任意旋转,请更好地使用minAreaRect()来返回最佳旋转值。

  • 迭代使用Cv.ApproxPoly缩小为4边形状

            var approxIter = 1;
            while (true)
            {
                var approxCurve = Cv.ApproxPoly(largestContour, 0, null, ApproxPolyMethod.DP, approxIter, true);
                var approxCurvePointsTmp = new[] { approxCurve.Select(p => new CvPoint2D32f((int)p.Value.X, (int)p.Value.Y)).ToArray() }.ToArray();
                if (approxCurvePointsTmp[0].Length == 4)
                {
                    corners = approxCurvePointsTmp[0];
                    break;
                }
                else if (approxCurvePointsTmp[0].Length < 4) throw new InvalidOperationException("Failed to decimate corner points");
                approxIter++;
            }
    

然而,如果轮廓检测由于噪声/对比度而为您提供两个独立的轮廓,则这些都不会有帮助。

我认为可以使用霍夫线变换来帮助检测线被分成两个轮廓的情况。

如果是这样,可以对连接轮廓的所有组合重复搜索,以查看是否找到更大/更矩形的匹配。

答案 2 :(得分:0)

停止依赖边缘检测,这是宇宙中最糟糕的方法,并切换到某种形式的图像分割。

纸张为白色,背景形成对比,这是您应该使用的信息。