确定阈值参数的正确方法

时间:2015-03-21 06:32:14

标签: c++ opencv

我试图实时找到三角形(蓝色轮廓)和梯形(黄色轮廓)。一般来说没关系。 enter image description here
但是有一些问题。首先,这是一个误报。三角形成为梯形,反之亦然。我不知道如何解决这个问题。 enter image description here  其次是“噪音”。 enter image description here。我试图检查图中的区域,但噪音可以等于该区域。所以它没有那么多帮助。噪声取决于阈值参数。 enter image description here cv::adaptiveThreshold根本没有帮助。它会增加更多噪音(并且它如此慢)erodedilate无法以正确的方式修复它enter image description here

这是我的代码。

cv::Mat detect(cv::Mat imageRGB)
{
    //RGB -> GRAY
    cv::Mat imageGray;
    cv::cvtColor(imageRGB, imageGray, CV_BGR2GRAY);
    //Bluring it
    cv::Mat image;
    cv::GaussianBlur(imageGray, image, cv::Size(5,5), 2);
    //Thresholding
    cv::threshold(image, image, 100, 255, CV_THRESH_BINARY_INV);

    //SLOW and NOISE
    //cv::adaptiveThreshold(image, image, 255.0, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 21, 0);

    //Calculating canny params.
    cv::Scalar mu;
    cv::Scalar sigma;
    cv::meanStdDev(image, mu, sigma);

    cv::Mat imageCanny;

    cv::Canny(image,
              imageCanny,
              mu.val[0] + sigma.val[0],
              mu.val[0] - sigma.val[0]);

    //Detecting conturs.
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Vec4i> hierarchy;
    cv::findContours(imageCanny, contours, hierarchy,CV_RETR_TREE, CV_CHAIN_APPROX_NONE);

    //Hierarchy is not needed here so clear it.
    hierarchy.clear();

    for (std::size_t i = 0; i < contours.size(); i++)
    {
        //fitEllipse need at last 5 points.
        if (contours.at(i).size() < 5)
        {
            continue;
        }
        //Skip small contours.
        if (std::fabs(cv::contourArea(contours.at(i))) < 800.0)
        {
            continue;
        }
        //Calculating RotatedRect from contours NOT from hull
        //because fitEllipse need at last 5 points.

        cv::RotatedRect bEllipse = cv::fitEllipse(contours.at(i));

        //Finds the convex hull of a point set.
        std::vector<cv::Point> hull;
        cv::convexHull(contours.at(i), hull, true);
        //Approx it, so we'll get 3 point for triangles
        //and 4 points for trapez.
        cv::approxPolyDP(hull, hull, 15, true);
        //Is our contour convex. It's mast be.
        if (!cv::isContourConvex(hull))
        {
            continue;
        }
        //Triangle
        if (hull.size() == 3)
        {
            cv::drawContours(imageRGB, contours, i, cv::Scalar(255, 0, 0), 2);
            cv::circle(imageRGB, bEllipse.center, 3, cv::Scalar(0, 255, 0), 2);
        }
        //trapez
        if (hull.size() == 4)
        {
            cv::drawContours(imageRGB, contours, i, cv::Scalar(0, 255, 255), 2);
            cv::circle(imageRGB, bEllipse.center, 3, cv::Scalar(0, 0, 255), 2);
        }
    }
    return imageRGB;
}

所以...一般来说,所有问题都由错误的阈值参数决定,我怎样才能以正确的方式计算(当然是自动的)?我怎么能(哈哈,对不起我的英语)防止误报?

1 个答案:

答案 0 :(得分:1)

Thesholding - 我认为你应该尝试Otsu二值化 - here是一些理论和一个很好的图片,here是文档。这种阈值化通常试图在图像中找到2个最常见的值,并使用它们的平均值作为阈值。

或者考虑使用HSV色彩空间,可能更容易区分黑色和白色区域与其他区域。另一个想法是使用inRange函数(在RGB或HSV颜色空间 - 应该在woth情况下工作) - 你需要找到2个范围(一个来自黑色区域,一个来自白色)并且只搜索那些区域(使用inRange函数) - 看at this post

完成此任务的另一种方法可能是使用某些库进行blob提取like this oneblob extractor,这是OpenCV的一部分。

区分三角形与梯形 - 我在这里看到了两种改善解决方案的基本方法:

  • 在这一行cv::approxPolyDP(hull, hull, 15, true);中使第三个参数(在这种情况下为15)不是常数值,而是轮廓区域或长度的某些部分。绝对应该适应轮廓尺寸,它不仅仅是一个恒定的值。很难说如何在没有一些测试的情况下计算它 - 尝试从1-5%的轮廓区域或长度开始(我会从长度开始,但这只是我的猜测)并且看看这个值是否正常/大/如果需要,可以检查其他值。不幸的是,没有其他办法,但手动找到这个等式不应该花很长时间。
  • 当你有4或5个点时,计算连接连续点的线的方程(点1与点2,点2与点3等,不要忘记计算第一点和最后一点之间的线),而不是检查这些线中是否有两条是平行的(或者至少接近平行 - 它们之间的角度接近0度) - 如果你发现任何平行线而不是这个轮廓是梯形,否则它是一个三角形。