find contours 在模拟图像上发现太多轮廓

find contours finds too many contours on simulated image

我想找到分割岩石的二值图像的轮廓。 opencv 的 findContours 函数存在一些问题。

  1. 轮廓大小约为 1000,而二值图像的轮廓可能约为 30-50。

  2. 当我绘制所有轮廓时,它们似乎是二进制图像中黑色边界的良好表示。但是当我只绘制一些随机索引的一个轮廓时,它会显示一个小轮廓。

图片如下:

我想要与二值图像中的轮廓一样的确切数量。

代码:

std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
cv::findContours(input_image, contours,hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
  for( int i = 0; i < (int)contours.size(); i++)
  {

      cv::drawContours(input_rgb_image, contours, 512 , cv::Scalar(0,255,0), 1, 8, hierarchy,1);


  }


您的代码有两个问题。如果您反转和模糊图像,您将获得更好的结果。这些是我在找到轮廓之前应用这两个操作后的结果:

OpenCV findContours() 函数在浅色背景上找到深色轮廓。如果你想找到白色的空间,也就是岩石,你需要先反转二值图像。您可以像这样反转二进制图像 invertedImage = 255 - binaryImage。模糊也有帮助,因为它连接了应该连接但由于分辨率低而没有连接的像素。模糊是用代码 blurredImage = cv2.blur(img, (2,2)) 完成的。这是倒置的模糊图像:

这是我使用的代码:

import cv2
import random
# Read image
gray = 255-cv2.imread('/home/stephen/Desktop/image.png', 0)
gray = cv2.blur(gray, (2,2))
# Find contours in image
contours, _ = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))
img = cv2.imread('/home/stephen/Desktop/image.png')
for cnt in contours:
    color = random.randint(0,255),random.randint(0,255),random.randint(0,255)
    img = cv2.drawContours(img, [cnt], 0, color, cv2.FILLED)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

我会尝试一些事情:

  • 双边过滤器而不是模糊。它以类似的方式使事情变得平滑 模糊但也试图保留边界,这有利于分割。缺点 - 它在计算上很昂贵,但你可以 免费找到 "your" 个运行良好的参数
  • blur + meanshift 分水岭前分割。模糊会起作用 像预期的那样,meanshift 将平均并加入轮廓 相似的颜色,因此使轮廓的数量更小。 根据参数的不同,meanshift 也很昂贵。只是玩 它。

更高级的是之后的轮廓分析。您可以根据以下条件团结一些邻居:

  • 部分hsv通道直方图的相似度;
  • 轮廓属性,例如圆度。如果两个统一的圆度 邻舍胜过任何一方的圆度,才能合一。像这样。

圆度计算:

float calcRoundness(std::vector<cv::Point> &contour, double area)
{
        float p = cv::arcLength(contour, true);
        if (p == 0)
                return 0;
        float k = (4 * M_PI * area) / pow(p, 2);

        /* 1 is circle, 0.75 - squared area, etc. */
        return k;
}