首页 > 解决方案 > 为什么 cv::findContours 返回这么多轮廓?

问题描述

我读过这篇文章,但即使在使用cv::threshold创建一个真正的二值图像之后,我仍然得到大约 500 个轮廓。我究竟做错了什么?

cv::findContours既然有 13 个清晰的斑点,不应该只返回 13 个轮廓吗?

Mat img = imread("img.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat img_thresh;
threshold(img, img_thresh, 0, 255, CV_THRESH_BINARY);

vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::findContours(img_thresh, contours, hierarchy, RetrievalModes::RETR_TREE, ContourApproximationModes::CHAIN_APPROX_SIMPLE);

RNG rng(12345);
Mat drawing = Mat::zeros(img_thresh.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++)
{
    Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
    drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
imshow("drawing", drawing);
waitKey();

binary_img 在此处输入图像描述

UPDATE1 使用cv::RETR_EXTERNAL而不是cv::RETR_TREE,但仍然返回比应有的更多轮廓。
在此处输入图像描述

标签: c++opencvimage-thresholding

解决方案


如果你检查你的二值图像,你会看到有很多独立的轮廓:

在此处输入图像描述

所以你首先需要通过腐蚀膨胀来清理它们,如下代码:

你会得到这个结果:

在此处输入图像描述

比原版更干净。

这是所有的代码:

cv::namedWindow("result", cv::WINDOW_FREERATIO);
cv::Mat img = cv::imread(R"(rUYLL.png)");

// to gray
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

cv::threshold(gray, gray, 0, 255, cv::THRESH_BINARY);
cv::erode(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
cv::dilate(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));

std::vector<std::vector<cv::Point> > contours;
cv::findContours(gray, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
cv::drawContours(img, contours, -1, cv::Scalar(0, 255, 0), 2, 8);

cv::imshow("result", img);
cv::waitKey();

它是输出:

在此处输入图像描述

希望能帮助到你!


还有一种最简单的方法,您也可以考虑它是否适合您,只需将下阈值从0增加到80,然后 DONE

cv::threshold(gray, gray, 80, 255, cv::THRESH_BINARY);

只需使用 THRESHOLD并检查结果。

相同的输出只是改变阈值:

在此处输入图像描述


推荐阅读