2013-12-06 5 views
7

Скажем, у меня есть следующий двоичный образ, созданный с выхода cv::watershed():Как заполнить контуры, которые касаются границы изображения?

enter image description here

Теперь я хочу, чтобы найти и заполнить контуры, так что я могу отделить соответствующие объекты от фона в исходном изображении (который был сегментирован функцией водораздела).

Для сегментации изображения и найти контуры, которые я использую код ниже:

cv::Mat bgr = cv::imread("test.png"); 

// Some function that provides the rough outline for the segmented regions. 
cv::Mat markers = find_markers(bgr); 

cv::watershed(bgr, markers); 

cv::Mat_<bool> boundaries(bgr.size()); 
for (int i = 0; i < bgr.rows; i++) { 
    for (int j = 0; j < bgr.cols; j++) { 
     boundaries.at<bool>(i, j) = (markers.at<int>(i, j) == -1); 
    } 
} 

std::vector<std::vector<cv::Point> > contours; 
std::vector<cv::Vec4i> hierarchy; 

cv::findContours(
    boundaries, contours, hierarchy, 
    CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE 
); 

До сих пор так хорошо. Однако, если я прохожу контуры, полученные выше cv::drawContours(), как показано ниже:

cv::Mat regions(bgr.size(), CV_32S); 
cv::drawContours(
    regions, contours, -1, cv::Scalar::all(255), 
    CV_FILLED, 8, hierarchy, INT_MAX 
); 

Это то, что я получаю:

enter image description here

Левый контур был оставлен открытым путем cv::findContours(), и, как следствие, не заполняется cv::drawContours().

Теперь я знаю, что это следствие cv::findContours(), отсекающее 1-пиксельную границу вокруг изображения (как указано в documentation), но что делать тогда? Кажется ужасной тратой, чтобы отбросить контур только потому, что это произошло, чтобы очистить границу изображения. И вообще, как я могу найти, какой контур (ы) попадает в эту категорию? cv::isContourConvex() не является решением в этом случае; регион может быть concave, но «закрыт» и, следовательно, не имеет этой проблемы.

Редактировать: О предположении дублировать пиксели от границ. Проблема заключается в том, что моя маркировке функция также покраска всех пикселей «на фоне», то есть те регионы, я уверен, не является частью какого-либо объекта:

enter image description here

Это приводит к границе втягивается вокруг выход. Если я какой-то образом избежать cv::findContours() чтобы обрезать эту границу:

enter image description here

Граница для фона становится слита с этим левым объектом:

enter image description here

что приводит к хорошим белым заполненным коробкам ,

ответ

1

После предложения Burdinov, я придумал приведенный ниже код, который правильно заполняет всю выделенную области, игнорируя при этом все-охватывающей границе:

cv::Mat fill_regions(const cv::Mat &bgr, const cv::Mat &prospective) { 
    static cv::Scalar WHITE = cv::Scalar::all(255); 
    int rows = bgr.rows; 
    int cols = bgr.cols; 

    // For the given prospective markers, finds 
    // object boundaries on the given BGR image. 
    cv::Mat markers = prospective.clone(); 
    cv::watershed(bgr, markers); 

    // Copies the boundaries of the objetcs segmented by cv::watershed(). 
    // Ensures there is a minimum distance of 1 pixel between boundary 
    // pixels and the image border. 
    cv::Mat borders(rows + 2, cols + 2, CV_8U); 
    for (int i = 0; i < rows; i++) { 
     uchar *u = borders.ptr<uchar>(i + 1) + 1; 
     int *v = markers.ptr<int>(i); 
     for (int j = 0; j < cols; j++, u++, v++) { 
      *u = (*v == -1); 
     } 
    } 

    // Calculates contour vectors for the boundaries extracted above. 
    std::vector<std::vector<cv::Point> > contours; 
    std::vector<cv::Vec4i> hierarchy; 
    cv::findContours(
     borders, contours, hierarchy, 
     CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE 
    ); 

    int area = bgr.size().area(); 
    cv::Mat regions(borders.size(), CV_32S); 
    for (int i = 0, n = contours.size(); i < n; i++) { 
     // Ignores contours for which the bounding rectangle's 
     // area equals the area of the original image. 
     std::vector<cv::Point> &contour = contours[i]; 
     if (cv::boundingRect(contour).area() == area) { 
      continue; 
     } 

     // Draws the selected contour. 
     cv::drawContours(
      regions, contours, i, WHITE, 
      CV_FILLED, 8, hierarchy, INT_MAX 
     ); 
    } 

    // Removes the 1 pixel-thick border added when the boundaries 
    // were first copied from the output of cv::watershed(). 
    return regions(cv::Rect(1, 1, cols, rows)); 
} 
6

Решение № 1: использование изображения продлены на один пиксель в каждом направлении:

Mat extended(bgr.size()+Size(2,2), bgr.type()); 
Mat markers = extended(Rect(1, 1, bgr.cols, bgr.rows)); 

// all your calculation part 

std::vector<std::vector<Point> > contours; 
findContours(boundaries, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 

Mat regions(bgr.size(), CV_8U); 
drawContours(regions, contours, -1, Scalar(255), CV_FILLED, 8, Mat(), INT_MAX, Point(-1,-1)); 

Обратите внимание, что контуры были извлечены из расширенного изображения, то есть их х и у значения больше на 1 от того, что они должны быть. Вот почему я использую drawContours с (-1, -1) пиксельным смещением.

Решение номер 2: добавить белые пиксели от границы изображения в соседней строке/столбец:

bitwise_or(boundaries.row(0), boundaries.row(1), boundaries.row(1)); 
bitwise_or(boundaries.col(0), boundaries.col(1), boundaries.col(1)); 
bitwise_or(boundaries.row(bgr.rows()-1), boundaries.row(bgr.rows()-2), boundaries.row(bgr.rows()-2)); 
bitwise_or(boundaries.col(bgr.cols()-1), boundaries.col(bgr.cols()-2), boundaries.col(bgr.cols()-2)); 

Оба решения являются наполовину грязной обходными, но это все, что я мог думать.

+0

Не совсем работать, как планировался. См. Редактирование на мой вопрос. – xperroni

+0

Я не совсем уверен, как вы получили изображение iUYue.png. Почему эта белая граница вокруг всего изображения? –

+0

Я тоже этого не понимаю, я просто использовал функцию водораздела с изображением маркера, которое я показал (первое изображение моего редактирования). – xperroni

Смежные вопросы