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

Android 在不同情况下检测文档的四个角,android,opencv,image-processing,edge-detection,Android,Opencv,Image Processing,Edge Detection,我尝试了以下两种方法:- 图像到Mat的转换 应用高斯模糊 然后进行canny边缘检测 寻找轮廓 这种方法的问题是: 检测到的轮廓太多 大部分是开放轮廓 没有检测到我想要检测的东西 然后我改变了我的方法,尝试了高斯模糊/中值模糊后的自适应阈值,效果更好,我能够在50%的情况下检测到角点 我目前面临的问题是,页面检测需要对比鲜明、平淡的背景,没有任何反射。我认为它太理想化了,无法在现实世界中使用 这是我需要帮助的地方。即使是解决方案的方向也受到高度赞赏,尤其是在java中。期待中的感谢 在这样对比

我尝试了以下两种方法:-

  • 图像到Mat的转换

  • 应用高斯模糊

  • 然后进行canny边缘检测

  • 寻找轮廓

  • 这种方法的问题是:

  • 检测到的轮廓太多
  • 大部分是开放轮廓
  • 没有检测到我想要检测的东西
  • 然后我改变了我的方法,尝试了高斯模糊/中值模糊后的自适应阈值,效果更好,我能够在50%的情况下检测到角点

    我目前面临的问题是,页面检测需要对比鲜明、平淡的背景,没有任何反射。我认为它太理想化了,无法在现实世界中使用

    这是我需要帮助的地方。即使是解决方案的方向也受到高度赞赏,尤其是在java中。期待中的感谢 在这样对比鲜明的背景下,效果绝对不错

    检测到4个角 这张照片给人带来麻烦,因为背景不是最鲜明的

    发现初始最大等高线

    更新:中值模糊没有多大帮助,因此我追踪了原因,发现页面边界是以零碎的形式检测到的,而不是单个轮廓,因此它检测到最大轮廓作为页面边界的一部分,因此执行了一些形态学操作,以闭合相对较小的间隙,并最终得到最大的轮廓轮廓确实得到了改善,但不是最佳的。有什么办法可以改善这些巨大的差距吗?

    变形原图

    变形图像中发现的最大轮廓


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

    您可以使用以下一种或两种方法拾取单个轮廓:

    • 使用
      BoundingRect
      ContourArea
      来评估
      boundingRect()
      返回正交矩形,要处理任意旋转,最好使用
      minarealect()
      返回最佳旋转矩形

    • 反复使用
      Cv.ApproxPoly
      将形状缩小为四边形

              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++;
              }
      
      var近似值=1;
      while(true)
      {
      var approxCurve=Cv.ApproxPoly(最大轮廓,0,null,ApproxPolyMethod.DP,approxIter,true);
      var approxCurvePointsTmp=new[]{approxCurve.Select(p=>newcvpoint2d32f((int)p.Value.X,(int)p.Value.Y)).ToArray()}.ToArray();
      如果(approxCurvePointsTmp[0]。长度==4)
      {
      角点=approxCurvePointsTmp[0];
      打破
      }
      else if(ApproxCurvePointsMP[0]。长度<4)抛出新的InvalidOperationException(“无法抽取角点”);
      approxIter++;
      }
      
    但是,如果由于噪声/对比度,轮廓检测为您提供了两个单独的轮廓,则这两种方法都没有帮助

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


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

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


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

    如果您使用这样的方法:

    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;
    }
    
    public static RotatedRect getbestrectbyrea(列表boundingRects){
    RotatedRect bestRect=null;
    如果(boundingRects.size()>=1){
    RotatedRect boundingRect;
    点[]顶点=新点[4];
    Rect-Rect;
    双最大面积;
    int ixMaxArea=0;
    //按面积查找最佳矩形
    boundingRect=boundingRects.get(ixMaxArea);
    边界直点(顶点);
    rect=Imgproc.boundingRect(新的MatOfPoint(顶点));
    maxArea=rect.area();
    对于(int ix=1;ixmaxArea){
    maxArea=rect.area();
    ixMaxArea=ix;
    }
    }
    bestRect=boundingRects.get(ixMaxArea);
    }
    返回bestRect;
    }
    专用静态位图findROI(位图源位图){
    位图ROIBAMIT=Bitmap.createBitmap(sourceBitmap.getWidth()、sourceBitmap.getHeight()、Bitmap.Config.ARGB_8888);
    Mat sourceMat=new Mat(sourceBitmap.getWidth()、sourceBitmap.getHeight()、CV_8UC3);
    bitmapToMat(sourceBitmap,sourceMat);
    最终材料=新材料();
    sourceMat.copyTo(mat);
    Imgproc.CVT颜色(mat、mat、Imgproc.COLOR_RGB2GRAY);
    Imgproc.threshold(mat,mat,146250,Imgproc.THRESH_二进制);
    //寻找轮廓
    列表等高线=新的ArrayList();
    List boundingRects=新建ArrayList();
    Imgproc.findContours(mat、等高线、新mat()、Imgproc.RETR\u列表、Imgproc.CHAIN\u近似值\u简单值);
    //找到合适的边界矩形
    用于(点轮廓:轮廓){
    MatOfPoint2f areaPoints=新的MatOfPoint2f(contour.toArray());
    RotatedRect boundingRect=Imgproc.minareRect(面积点);
    添加(boundingRect);
    }
    RotatedRect documentRect=getBestRectByArea(boundingRects);
    if(documentRect!=null){
    点矩形点[]=新点[4];
    文件直点(直点);
    对于(int i=0;i<4;++i){
    Imgproc.line(源矩阵、矩形点[i]、矩形点[(i+1)%4]、ROI\u颜色、ROI\u宽度);
    }
    }
    Utils.matToBitmap(sourceMat、roiBitmap);
    返回位图;
    }
    
    您可以为源图像实现如下结果: