【问题标题】:How to keep content within the square contour, while delete other shape of contours?如何将内容保留在方形轮廓内,同时删除其他形状的轮廓?
【发布时间】:2020-07-12 17:34:45
【问题描述】:

我的项目是从this的图像中扫描出一张纸

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

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)

很明显,结果很多,但我只想整理一下所有的复选框轮廓,检查是否有人打勾。

【问题讨论】:

  • 您的目标复选框具有特定的纵横比(高度除以宽度)。尝试用它来过滤轮廓,看看你会得到什么结果。

标签: python opencv image-processing contour cv2


【解决方案1】:

可能有多种解决方案,您可以采取的一种方法是:

  1. 提取轮廓/连接的组件(正如您所做的那样)

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

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

  4. 在得到盒子的 4 个坐标后,您可以根据检测到的盒子的纵横比/矩添加一个额外的过滤器(如 eldesgraciado 建议的那样)

注意:

  • 您必须进行大量实验才能获得正确的值 阈值,并且必须具有阈值的 +/- 边距
  • 您将获得多个匹配峰值,在这种情况下,您必须 拿最强的一个
  • 在填写表格时,可能会出现笔划会在框外继续的情况,因此在提取框坐标后必须应用纵横比
  • 这种方法对a非常敏感。噪音 B.表格c的偏斜。图像的比例或框的变化等,建议根据您的示例图像执行一些预处理步骤,例如偏斜校正。

Template Matching Wiki

Template Matching OpenCV

Similar Question

【讨论】:

    【解决方案2】:

    我在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() );
        }
    
      }
    

    这是输出:

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

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

    让我们创建一个“垂直线”掩码,只包含二值图像中的垂直线。

      //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) );
    

    我只是应用morphological opening8 的垂直线,结果如下:

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

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

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

    我们只需OR 这两个掩码就可以产生最终的二进制掩码:

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

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

    这是你得到的最终输出:

    【讨论】:

    • 你能用python做吗?因为,我还不够先进,无法将其解释为 Python
    猜你喜欢
    • 2016-09-25
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2021-03-17
    • 2021-09-07
    • 2014-11-22
    • 2019-01-26
    • 2021-11-22
    相关资源
    最近更新 更多