我从这个问题的最高投票作者的答案中提取了一个代码段:
https://answers.opencv.org/question/9863/fill-holes-of-a-binary-image/
改写为:
cv::Mat image = cv::imread("image.jpg", 0);
cv::Mat image_thresh;
cv::threshold(image, image_thresh, 125, 255, cv::THRESH_BINARY);
// Loop through the border pixels and if they're black, floodFill from there
cv::Mat mask;
image_thresh.copyTo(mask);
for (int i = 0; i < mask.cols; i++) {
if (mask.at<char>(0, i) == 0) {
cv::floodFill(mask, cv::Point(i, 0), 255, 0, 10, 10);
}
if (mask.at<char>(mask.rows-1, i) == 0) {
cv::floodFill(mask, cv::Point(i, mask.rows-1), 255, 0, 10, 10);
}
}
for (int i = 0; i < mask.rows; i++) {
if (mask.at<char>(i, 0) == 0) {
cv::floodFill(mask, cv::Point(0, i), 255, 0, 10, 10);
}
if (mask.at<char>(i, mask.cols-1) == 0) {
cv::floodFill(mask, cv::Point(mask.cols-1, i), 255, 0, 10, 10);
}
}
// Compare mask with original.
cv::Mat newImage;
image.copyTo(newImage);
for (int row = 0; row < mask.rows; ++row) {
for (int col = 0; col < mask.cols; ++col) {
if (mask.at<char>(row, col) == 0) {
newImage.at<char>(row, col) = 255;
}
}
}
cv::imshow("filled image", mask);
cv::imshow("Final image", newImage);
cv::imwrite("final.jpg", newImage);
cv::waitKey(0);
return 0;
我了解它使用Floodfill算法来尝试填充孔,并且已经在另一个示例图像上进行了测试:
通过检测所有9个孔,它确实可以很好地工作。
但是,我尝试了另一种复杂的图像:
这次它将不起作用,它将用白色填充整个图形,并且检测到的孔数为1700。
我认为这里可能缺少少量的形态学知识,但我想也许应该先对失败的图像进行“调整”,然后再将其插入作者的代码中?
专家能否与我分享一些想法,因为我无法在Google上找到非常相似的图形进行孔检测。那么当两个孔与二进制图像中的白色路径相连时,孔有什么特殊之处呢?
最佳答案
您的图像有问题,它的白色细条围绕着图像的3面。此栏还连接到左侧的4个白色矩形,这创建了一个额外的封闭轮廓/级别,使我猜想的“填充”感到困惑。
我个人不喜欢使用“填充”方法来解决在轮廓内发现孔的问题。我更喜欢将“findcontour”方法与“hierarchy”选项一起使用。请看看here。乍一看,它看起来有些复杂,但它提供了我们所需的所有信息。
您要寻找的孔具有两个属性:
查找这些漏洞的代码是:
auto image = cv::imread(in_img_path, cv::ImreadModes::IMREAD_GRAYSCALE);
cv::threshold(image, image, 128, 255, cv::THRESH_OTSU);
std::vector<std::vector<cv::Point>> contours, selected_contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(image, contours, hierarchy, cv::RetrievalModes::RETR_TREE, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
for (int i = 0; i < contours.size(); i++) {
if (hierarchy[i][2] == -1 && hierarchy[i][3] != -1) //the contour has no children but has a parent
selected_contours.emplace_back(std::move(contours[i]));
}
cv::Mat drawing_image(image.size(), image.type(), cv::Scalar::all(0));
for (int i = 0; i < selected_contours.size(); i++) {
cv::drawContours(drawing_image, selected_contours, i, cv::Scalar(255), 1);
}
编辑:
我尝试过,在这种情况下,第一次检查似乎是多余的。满足以下条件:
if (hierarchy[i][3] != -1) // the contour has a parent
孔的数量(selected_contours的大小)为:71
而“drawing_image”将如下所示: