Я реализовал алгоритм идентификации подключенного компонента из Вот, но кажется, что в некоторых случаях 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 содержит 2448×2050 пикселей размером CV_8U. Значения пикселей: 0 (фон) или 255 (передний план). На изображении 17 подключенных компонентов. Все компоненты, кроме первого, определены правильно. Ошибочный компонент, безусловно, самый большой (~ 1,5 миллиона пикселей) и содержит несколько небольших разрозненных групп пикселей. Он охватывает все другие компоненты. Небольшие несвязанные группы пикселей, которые ошибочно назначены первому компоненту, все связаны с верхней частью ограничительной рамки компонентов.
РЕДАКТИРОВАТЬ: я добавил несколько изображений для визуализации проблемы. На первом изображении показаны все идентифицированные подключенные компоненты. Второе изображение показывает только ошибочный компонент (обратите внимание на небольшие разъединенные группы пикселей вверху). Третье изображение увеличивает часть второго изображения:
Если у кого-то есть идея, где может быть ошибка, я был бы благодарен.
Я нашел ошибку сам. В конце метода мелкие компоненты выбрасываются. В этом случае номер компонента (метка) не увеличивается:
if(components.back().size()<minSize)
{
components.pop_back();
contours.pop_back();
}
else
{
++label;
if(label==255) ++label;
}
Это означает, что номер метки снова используется для маркировки следующего компонента на изображении. Следовательно, несколько небольших компонентов и достаточно большой компонент могут иметь одинаковый номер этикетки. Если теперь ограничивающий прямоугольник большого компонента повторяется, то этот ограничивающий прямоугольник может содержать несколько ранее идентифицированных небольших, но неиспользуемых компонентов с тем же номером метки.
Решение состоит в том, чтобы удалить ветку else и вместо этого всегда увеличивать номер метки.
Других решений пока нет …