假设我从 cv::watershed()
的输出创建了以下二进制图像:
现在我想找到并填充轮廓,因此我可以将相应的对象与原始图像中的背景分开(由分水岭函数分割) .
要分割图像并找到轮廓,我使用下面的代码:
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
);
这就是我得到的:
最左边的轮廓由 cv::findContours()
打开,因此它不会被 cv::drawContours()
填充 .
现在我知道这是剪掉图像周围1像素边框的结果(如documentation中所述),但该怎么办呢?放弃轮廓似乎是一种可怕的浪费,因为它碰巧刷掉了图像的边框 . 无论如何,我怎么能找到属于这个类别的轮廓? cv::isContourConvex()
在这种情况下不是解决方案;一个地区可以concave但是"closed"因此没有这个问题 .
Edit: 关于从边框复制像素的建议 . 问题是我的标记功能也在绘制"background"中的所有像素,即任何对象的一部分:
这导致在输出周围绘制边界 . 如果我以某种方式避免 cv::findContours()
剪掉那个边界:
背景的边界与最左边的对象合并:
这导致一个漂亮的白色填充框 .
2 回答
解决方案编号1:使用在每个方向上延伸一个像素的图像:
注意,轮廓是从扩展图像中提取的,即它们的x和y值比它们应该的大1 . 这就是我使用带有(-1,-1)像素偏移的drawContours的原因 .
解决方案编号2:从图像边界添加白色像素到相邻行/列:
这两种解决方案都是半肮脏的解决方法,但这是我能想到的 .
按照Burdinov的建议,我想出了下面的代码,它正确地填充了所有提取的区域,同时忽略了全部封闭的边界: