C++ 如何识别二元图中的线连通孔

C++ 如何识别二元图中的线连通孔,c++,opencv,graphics,computer-vision,binary-image,C++,Opencv,Graphics,Computer Vision,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 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;
cv::Mat image=cv::imread(“image.jpg”,0);
cv::Mat图像_阈值;
cv::threshold(图像,图像阈值,125,255,cv::阈值二进制);
//循环通过边界像素,如果它们是黑色的,则从那里进行泛光填充
cv::Mat面罩;
图像_thresh.copyTo(掩码);
对于(int i=0;i
我知道它使用了泛光填充算法来填充孔洞,我已经在另一个示例图像上进行了测试:

通过检测所有9个孔,它的效果非常好

然而,我尝试了另一个略显复杂的图像:

这一次它将不起作用,它将用白色填充整个图形,并且它检测到的孔数是1700

我想我可能在这里缺乏一点重要的形态学知识,但我想也许我应该先对失败的图像进行“隐藏”,然后再将其插入作者的代码中


专家们能和我分享一些想法吗?因为我在谷歌上找不到类似的孔检测图。那么,当两个孔在二值图像中以白色路径连接时,孔的特殊之处是什么呢?请提前感谢

您的图像中有一个问题,它在图像的三个面周围有一个薄的白色条。该条还连接到左侧的4个白色矩形,这创建了一个额外的封闭轮廓/标高,我猜这会混淆“泛光填充”

我个人不喜欢使用“洪水填充”方法来解决在等高线内寻找孔洞的问题。我更喜欢将“findcontour”方法与“hierarchy”选项一起使用。请看一看。乍一看,它可能看起来有点复杂,但它提供了我们需要的所有信息

要查找的孔具有两个特性:

  • 它们是一个孩子的轮廓(一个洞)
  • 它们内部没有其他轮廓(不是父轮廓)
  • 查找这些孔的代码为:

    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);
    }
    
    孔的数量(选定的_轮廓的大小)为:71

    “绘图图像”将如下所示:
    另一种简单的方法是使用轮廓区域过滤。其思想是,然后使用最小阈值区域执行轮廓区域过滤。如果轮廓通过此阈值过滤器,则我们将其视为有效轮廓。以下是带有
    500
    阈值的结果。您可能需要根据您的图像进行更改


    我用Python实现了它,但是您可以很容易地将相同的方法应用到C中++

    import cv2
    import numpy as np
    
    # Load image, grayscale, Otsu's threshold
    image = cv2.imread('1.png')
    mask = np.zeros(image.shape[:2], dtype=np.uint8)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    # Find contours and filter using contour area
    cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 500:
            cv2.drawContours(image,[c],0,(36,255,12),-1)
            cv2.drawContours(mask,[c],0,255,1)
    
    cv2.imshow('mask', mask)
    cv2.imshow('image', image)
    cv2.waitKey()
    
    导入cv2
    将numpy作为np导入
    #加载图像、灰度、大津阈值
    image=cv2.imread('1.png')
    掩码=np.zero(image.shape[:2],dtype=np.uint8)
    灰色=cv2.CVT颜色(图像,cv2.COLOR\u BGR2GRAY)
    thresh=cv2.阈值(灰色,0,255,cv2.thresh\u二进制\u INV+cv2.thresh\u OTSU)[1]
    #查找轮廓并使用轮廓区域进行过滤
    cnts=cv2.查找对象(阈值、cv2.RETR\u树、cv2.链近似值、简单值)
    如果len(cnts)==2个其他cnts[1],则cnts=cnts[0]
    对于碳纳米管中的碳:
    面积=cv2。轮廓面积(c)
    如果面积小于500:
    cv2.等高线图(图[c],0,(36255,12),-1)
    cv2.绘制轮廓(掩模[c],0255,1)
    cv2.imshow(“面具”,面具)
    cv2.imshow(“图像”,图像)
    cv2.waitKey()
    
    这非常有用。非常好,谢谢你的提示和修改过的算法!
    import cv2
    import numpy as np
    
    # Load image, grayscale, Otsu's threshold
    image = cv2.imread('1.png')
    mask = np.zeros(image.shape[:2], dtype=np.uint8)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    
    # Find contours and filter using contour area
    cnts = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    for c in cnts:
        area = cv2.contourArea(c)
        if area < 500:
            cv2.drawContours(image,[c],0,(36,255,12),-1)
            cv2.drawContours(mask,[c],0,255,1)
    
    cv2.imshow('mask', mask)
    cv2.imshow('image', image)
    cv2.waitKey()