如何将内容保留在方形轮廓内,同时删除其他形状的轮廓?

How to keep content within the square contour, while delete other shape of contours?

我的项目是从这张图片中扫描出一张纸

经过大量处理,图像质量下降。我 运行 一些代码来找到所有的轮廓

img = cv2.imread("scan1.jpg")

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret,bw = cv2.threshold(gray,220,255,cv2.THRESH_BINARY_INV) contours,hierarchy = cv2.findContours(bw, cv2.RETR_CCOMP,1) cntLen = 10 ct = 0 #number of contours for cnt in contours:
    if len(cnt) > cntLen: #eliminate the noises
        ct += 1
        newimg = img.copy()
        cv2.drawContours(newimg,[cnt],0,(0,0,255),2)
        cv2.imshow('Win', newimg)
        cv2.waitKey(0) print('Total contours: ',ct)

当然有很多结果,但我只想整理所有复选框轮廓并检查是否有人勾选它。

我在 C++ 中给出了我的答案,但在 Python 中可以使用相同的操作。

让我们来看看两种可能的解决方案。第一个涉及直接在您提供的输入图像上应用我建议的解决方案。我正在根据 aspect ratiominimum width/height 阈值过滤轮廓。

首先,读取输入图像并将其转换为灰度:

  std::string imageName = "C://opencvImages//survey.jpg";
  cv::Mat imageInput = cv::imread( imageName );

  //compute gray scale image:
  cv::cvtColor(imageInput, grayImage, cv::COLOR_RGB2GRAY );

接下来,通过Otsu thresholding获取二值图像。非常简单的东西:

  //get binary image via Otsu:
  cv::Mat binImage;
  cv::threshold( grayImage, binImage, 0, 255, cv::THRESH_OTSU );
  
  //Invert the image:
  binImage = 255 - binImage;

现在,只需遍历二进制图像中的每个 contour 并应用相应的 “轮廓过滤器”。我将寻找在 0.91.1 之间具有 minimum width/heightaspect ratio 的轮廓。这些参数几乎是手动设置的。让我们看看代码:

  //contour filter:
  for( int i = 0; i< contours.size(); i++ ){

    //get the bounding box for each parent countour found:
    cv::Rect bBox = cv::boundingRect( contours[i] );

    //compute aspect ratio:
    float aspectRatio = bBox.height / bBox.width;

    //set the aspect ratio thresholds:
    float lowerAspectRatio = 0.9;
    float upperAspectRatio = 1.1;

    //set the width/height thresholds:
    float minWidth = 8;
    float minHeight = 8;

    if ( (bBox.height > minHeight) && (bBox.width > minWidth) &&
         (aspectRatio >= lowerAspectRatio) && (aspectRatio <= upperAspectRatio) ) {
          cv::Scalar color = cv::Scalar( 0, 255, 0 );
          cv::drawContours( imageInput, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
    }

  }

这是输出:

如您所见,过滤器遗漏了一些复选框。特别是,过滤规范可能过于严格,一些复选框似乎由其他字符连接。

让我们看看是否可以通过首先应用一些形态学来改善结果,以去除不属于复选框的轮廓。我将利用目标轮廓由 水平 垂直 线组成的事实。

让我们创建一个 “垂直线” 蒙版,仅包含二值图像中的垂直线。

  //create a vertical structuring element of size 8:
  cv::Mat verticalStructure = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(1, 8) );
  
  //apply the morphology operations to isolate the vertical lines:
  cv::Mat verticalMask = binImage.clone();
  cv::erode( verticalMask, verticalMask, verticalStructure, cv::Point(-1, -1) );
  cv::dilate( verticalMask, verticalMask, verticalStructure, cv::Point(-1, -1) );

我只是应用了带有 8 垂直线的 morphological opening,这是结果:

我将使用相同的操作来生成 “水平遮罩”。这次的结构元素如下:

  cv::Mat horizontalStructure = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(8, 1) );

相同的形态学操作产生这个掩码:

我们只是 OR 两个掩码来生成最终的二进制掩码:

注意所有的复选框是如何在形态学过滤器中幸存下来的。非常酷,现在,计算轮廓并相应地过滤它们。我已经更改了过滤器参数,让我们使用 blob area 看看我们得到什么样的结果。我将搜索特定区域范围上方和下方的斑点。

  //contour filter:
  for( int i = 0; i< contours.size(); i++ ){

    //get the bounding box for each parent countour found:
    cv::Rect bBox = cv::boundingRect( contours[i] );

    //compute blob area:
    float blobArea = bBox.area();

    //set the area thresholds:
    float minBlobArea = 25;
    float maxBlobArea = 300;

    //set the width/height thresholds:
    float minWidth = 5;
    float minHeight = 5;

    if ( (bBox.height > minHeight) && (bBox.width > minWidth) &&
         (blobArea > minBlobArea ) && (blobArea < maxBlobArea) ) {
          cv::Scalar color = cv::Scalar( 0, 0, 255 );
          cv::drawContours( imageInput, contours, i, color, 2, 8, hierarchy, 0, cv::Point() );
    }

  }

这是您得到的最终输出:

可能有多种解决方案,其中一种方法是:

  1. 提取 contours/connected 个组件(正如您所做的那样)

  2. 对您可以定义的正方形掩码进行基于相关性的模板匹配,如下所示: mask from the sample image

  3. 然后进行阈值处理以获取应用蒙版的峰的坐标(因此您可以计算框的 4 个坐标)

  4. 得到box的4个坐标后,可以根据检测到的box的aspectratio/moment添加一个额外的filter(参考eldesgraciado)

注:

  • 您必须进行大量实验才能获得正确的值 阈值,并且必须具有阈值的 +/- 余量
  • 您将获得比赛的多个高峰,在这种情况下,您必须 拿最强的
  • 填写表格时,可能会出现笔划在框外继续的情况,所以在提取框的坐标后必须应用纵横比
  • 这个方法很灵敏了。噪音 B.形式的倾斜 C.图像或框的比例变化等,建议根据您的示例图像进行一些预处理步骤,例如倾斜校正。

Template Matching Wiki

Template Matching OpenCV