OpenCVfloodFill() заполняет несвязанные области

Я реализовал алгоритм идентификации подключенных компонентов из здесь, но он кажется, что cv::floodFill(...) в некоторых случаях заполняет несвязанные области.

Прежде всего, вот код:

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;
      }       
  }

Входной файл cv::Mat содержит 2448x2050 пикселей размера CV_8U. Значения пикселей равны 0 (фон) или 255 (передний план). На изображении 17 связанных компонентов. Все компоненты, кроме первого, определены правильно. Ошибочный компонент намного больше (~ 1,5 миллиона пикселей) и содержит несколько небольших несвязанных групп пикселей. Он включает в себя все остальные компоненты. Небольшие несвязанные группы пикселей, которые ошибочно назначены первому компоненту, все связаны с верхней частью ограничивающей рамки компонентов.

РЕДАКТИРОВАТЬ: я добавил несколько изображений, чтобы визуализировать проблему. На первом изображении показаны все идентифицированные подключенные компоненты. На втором изображении показан только ошибочный компонент (обратите внимание на небольшие несвязанные группы пикселей вверху). Третье изображение увеличивает часть второго изображения:

Все компоненты

Ошибочный компонент

Увеличенный ошибочный компонент

Если у кого-то есть идея, где может быть ошибка, я был бы благодарен.


person Simon Oelmann    schedule 20.02.2014    source источник
comment
Прикрепите выходное изображение тоже, чтобы лучше понять.   -  person Anoop K. Prabhu    schedule 20.02.2014
comment
Как вы выбираете начальную точку для затопления? Я пытаюсь понять цикл, похоже, вы перебираете все двоичное изображение и выбираете исходную точку, которая находится на контуре?   -  person David Nilosek    schedule 21.02.2014
comment
Кроме того, в OpenCV есть алгоритм CC, он скрыт в cv::findContours. Вы можете использовать общий алгоритм CC, используя флаг CV_RETR_CCOMP. У них есть пример CC здесь: docs.opencv.org/modules/imgproc /док/   -  person David Nilosek    schedule 21.02.2014
comment
@David Nilosek: Начальными точками являются те, значение которых равно 255. Изображение содержит только значения пикселей 0 и 255. Компоненты имеют значение 255.   -  person Simon Oelmann    schedule 21.02.2014
comment
@David Nilosek: я уже определил подключенные компоненты, применив cv::findContours(...) с флагом CV_RETR_CCOMP и вызвав впоследствии cv::drawContours(...). Однако мне нужен для каждого связного компонента вектор всех точек, принадлежащих компоненту. С помощью cv::drawContours(...) я получаю только помеченные изображения.   -  person Simon Oelmann    schedule 21.02.2014


Ответы (1)


Баг нашел сам. По окончании метода мелкие компоненты выбрасываются. В этом случае номер компонента (метка) не увеличивается:

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

Это означает, что номер метки снова используется для обозначения следующего компонента изображения. Следовательно, несколько небольших компонентов и достаточно большой компонент могут иметь одинаковый номер метки. Если теперь итерировать ограничительную рамку большого компонента, то эта ограничительная рамка может содержать несколько ранее идентифицированных, но неиспользуемых небольших компонентов с тем же номером метки.

Решение состоит в том, чтобы удалить ветвь else и вместо этого всегда увеличивать номер метки.

person Simon Oelmann    schedule 25.02.2014