【问题标题】:Remove bounding rect with an area < n OpenCV删除面积 < n OpenCV 的边界矩形
【发布时间】:2014-05-23 11:51:49
【问题描述】:

我正在腐蚀带有文本块的图像,然后使用findContours() 查找所有文本块,然后绘制它们的边界矩形。然而,有时图像中的噪声会产生非常小的矩形,它们要么位于较大的矩形中,要么位于没有文本的地方。

我正在使用此代码查找轮廓并绘制它们。

double element_size = 20;
RNG rng(12345);
Mat element = getStructuringElement( cv::MORPH_ELLIPSE,cv::Size( 2*element_size + 1, 2*element_size+1 ),cv::Point( element_size, element_size ) );
erode(quad, quad, element);
vector<vector<cv::Point> > contours;
vector<Vec4i> hierarchy;
quad.convertTo(quad, CV_8UC1);
findContours( quad, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0) );

vector<vector<cv::Point> > contours_poly( contours.size() );
vector<cv::Rect> boundRect( contours.size() );

for( int i = 0; i < contours.size(); i++ )
{
    approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );
    boundRect[i] = boundingRect( Mat(contours_poly[i]) );
}

Mat drawing = Mat::zeros( quad.size(), CV_8UC3 );
for( int i = 0; i< contours.size(); i++ )
{
    Scalar color = Scalar(0,255, 0 );
    rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0 );
}

在运行示例后,我得到了返回结果:

如何修改我的代码,以便我可以删除不大于 n 的任何矩形,这样我只能保留完整的文本块,我还需要删除围绕整个卡片的最大轮廓。

【问题讨论】:

    标签: c++ opencv bounding-box


    【解决方案1】:

    你可以使用contourArea 来消除轮廓,在找到边界矩形之前使用下面的代码,这将删除所有面积小于阈值的轮廓。

    double min_area=100; // area threshold
    
     for( int i = 0; i< contours.size(); i++ ) // iterate through each contour.
     {
       double area=contourArea( contours[i],false);  //  Find the area of contour
       if(area<min_area)
        contours.erase(contours.begin() + i);
     }
    

    编辑:-

    对于任何打算使用上述代码的人,请参阅下面的评论。

    【讨论】:

    • 在代码中的 for 循环期间从 C++ std::vector 中删除项目或将项目插入是不安全的。你有三个选择:(1)向后迭代,这样删除item不会导致待处理item的索引发生偏移; (2) 将您打算保留的项目复制到新的vector 中,或 (3) 将布尔结果存储在单独的标志向量中,以便下次您可以使用这两个向量来过滤掉项目。
    • @rwong 感谢您的信息。
    【解决方案2】:

    要从vector 中删除cv::Rect 元素的更安全方法,您可以使用erase-remove idiom 删除低于特定区域阈值的元素。此方法比按索引逐个删除元素(如 Haris 的回答)更安全,因为您不会冒着跑出向量末端的风险。

    boundRect.erase(std::remove_if(boundRect.begin(), boundRect.end(),
                                   [] (cv::Rect r)
                                   {
                                       const int min_area = 100;
                                       return r.area() < min_area;
                                   }), boundRect.end());
    

    这里我使用 C++11 lambda 进行比较。如果你没有 C++11,创建一个仿函数类很容易。

    至于删除最大轮廓,您可以使用标准库中的另一个函数max_element(再次使用 lambda 进行比较):

    boundRect.erase(std::max_element(boundRect.begin(), boundRect.end(),
                                     [] (cv::Rect left, cv::Rect right)
                                     {
                                         return left.area() < right.area();
                                     }));
    

    【讨论】:

    • 那么第一个代码 sn-p 会删除所有低于min_area 的矩形吗?我对 C++11 的这些特性不是很熟悉。
    • 当在包含boundRect[i]for loop 之后使用代码sn-ps 似乎没有去除任何非常小的轮廓,我使用了阈值并且它没有影响结果.而对于删除最大的轮廓,它似乎并没有这样做。有什么想法吗?
    • @Nick 是的,第一个 sn-p 符合您的预期。对于您的第二条评论,我不能说为什么您会在没有看到更新代码的情况下得到意外结果。也许将其编辑到您的问题中或提出新问题。我确实注意到您的第二个 for 循环有一个错误。您的迭代限制是contours.size(),但实际上应该是boundRect.size(),因为boundRect 将比contours 短,您再次冒着越界访问的风险。
    • 感谢您的回复,我修复了for循环中的错误。我发现我需要一个非常高的最小区域来完成我正在做的事情。 15000感谢代码sn-ps!
    猜你喜欢
    • 2011-06-30
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 1970-01-01
    • 1970-01-01
    • 2014-09-22
    相关资源
    最近更新 更多