I have implemented the connected component identification algorithm from here, but it seems, that the cv::floodFill(...) fills unconnected regions in some cases.

First of all, here is the code:

void ImageMatchingOpenCV::getConnectedComponents(const cv::Mat& binImg, vector<vector<cv::Point>>& components, vector<vector<cv::Point>>& contours, const int minSize)
  {
      cv::Mat ccImg;
      binImg.convertTo(ccImg, CV_32FC1);

      int gap=startPointParams.gap;
      int label = 1;
      for(int y=gap; y<binImg.rows-gap; ++y)
      {
          for(int x=gap; x<binImg.cols-gap; ++x)
          {
              if((int)ccImg.at<float>(y, x)!=255) continue;
              cv::Rect bBox;
              cv::floodFill(ccImg, cv::Point(x, y), cv::Scalar(label), &bBox, cv::Scalar(0), cv::Scalar(0), 4 /*| cv::FLOODFILL_FIXED_RANGE*/);
              if(bBox.x<gap || bBox.y<gap || bBox.x+bBox.width>=binImg.cols-gap || bBox.y+bBox.height>=binImg.rows-gap) continue;
              components.push_back(vector<cv::Point>()); contours.push_back(vector<cv::Point>());
              for(int i=bBox.y; i<bBox.y+bBox.height; ++i)
              {
                  for(int j=bBox.x; j<bBox.x+bBox.width; ++j)
                  {
                      if((int)ccImg.at<float>(i, j)!=label) continue;
                      components.back().push_back(cv::Point(j, i));
                      if(    (int)ccImg.at<float>(i+1, j)!=label
                          || (int)ccImg.at<float>(i-1, j)!=label
                          || (int)ccImg.at<float>(i, j+1)!=label
                          || (int)ccImg.at<float>(i, j-1)!=label) contours.back().push_back(cv::Point(j, i));
                  }
              }
              if(components.back().size()<minSize)
              {
                  components.pop_back();
                  contours.pop_back();
              }
              else
              {
                  ++label;
                  if(label==255) ++label;
                  break;
              }
          }
          if(label!=1) break;
      }       
  }

The input cv::Mat contains 2448x2050 pixels of size CV_8U. The pixel values are either 0 (background) or 255 (foreground). There are 17 connected components in the image. All components but the first are identified correctly. The erroneous component is by far largest one (~1.5 million pixels) and contains some small disconnected pixel-groups. It encompasses all of the other components. The small disconnected pixel-groups, which are wrongly assigned to the first component are all connected to the top of the components bounding box.

EDIT: I added some images to visualize the problem. The first image shows all identified connected components. The second image shows only the erroneous component (notice the small disconnected pixel groups at the top). The third images zooms a part of the second image:

All components

The erroneous component

Zoomed erroneous component

If someone has an idea, where the error might be, I would be thankful.

有帮助吗?

解决方案

I found the bug myself. At the end of the method small components are thrown away. In this case the component's number (label) is not increased:

if(components.back().size()<minSize)
{
    components.pop_back();
    contours.pop_back();
}
else
{
    ++label;
    if(label==255) ++label;        
}

This means, the label number is used again to mark the next component in the image. Hence, several small components and a sufficiantly large component might have the same label number. If now the bounding box of the large component is iterated, then this bounding box might contain some small previously identified, but unused components with the same label number.

The solution is to remove the else branch and instead increase the label number always.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top