使用OpenCV通过直方图查找HSV阈值

时间:2013-04-30 06:06:54

标签: c++ opencv image-processing

我正在尝试编写一种方法,可以在HSV空间中找到放置在屏幕中心的对象的正确阈值。这些值用于对象跟踪算法。我用手动编码的阈值测试了那段代码,效果很好。该方法背后的想法是它应该计算每个通道的直方图,然后返回每个通道的第5和第95百分位数作为阈值。 (credit:How to find RGB/HSV color parameters for color tracking?)传递的图像是要跟踪的对象的图片(在整个过程开始之前由用户设置。这是代码

std::vector<cv::Scalar> HSV_Threshold_Determiner::Get_Threshold_Values(const cv::Mat& image)
{
    cv::Mat inputImage;
    cv::cvtColor(image, inputImage, CV_BGR2HSV);
    std::vector<cv::Mat> bgrPlanes;

    cv::split(inputImage, bgrPlanes);

    cv::Mat hHist, sHist, vHist;

    int hMax = 180, svMax = 256;

    float hRanges[] = { 0, (float)hMax };
    const float* hRange = { hRanges };
    float svRanges[] = { 0, (float)svMax };
    const float* svRange = { svRanges };
    //float sRanges[] = { 0, 256 };

    cv::calcHist(&bgrPlanes[0], 1, 0, cv::Mat(), hHist, 1, &hMax, &hRange);
    cv::calcHist(&bgrPlanes[1], 1, 0, cv::Mat(), sHist, 1, &svMax, &svRange);
    cv::calcHist(&bgrPlanes[2], 1, 0, cv::Mat(), vHist, 1, &svMax, &svRange);

    int totalEntries = image.cols * image.rows;
    int fiveCutoff = (int)(totalEntries * .05);
    int ninetyFiveCutoff = (int)(totalEntries * .95);

    float hTotal = 0, sTotal = 0, vTotal = 0;
    bool hMinFound = false, hMaxFound = false, sMinFound = false, sMaxFound = false,
        vMinFound = false, vMaxFound = false;

    cv::Scalar hThresholds;
    cv::Scalar sThresholds;
    cv::Scalar vThresholds;

    for(int i = 0; i < vHist.rows; ++i)
    {
        if(i < hHist.rows)
        {
            hTotal += hHist.at<float>(i, 0);

            if(hTotal >= fiveCutoff && !hMinFound)
            {
                hThresholds.val[0] = i;
                hMinFound = true;
            }
            else if(hTotal>= ninetyFiveCutoff && !hMaxFound)
            {
                hThresholds.val[1] = i;
                hMaxFound = true;
            }
        }

        sTotal += sHist.at<float>(i, 0);
        vTotal += vHist.at<float>(i, 0);

        if(sTotal >= fiveCutoff && !sMinFound)
        {
            sThresholds.val[0] = i;
            sMinFound = true;
        }
        else if(sTotal >= ninetyFiveCutoff && !sMaxFound)
        {
            sThresholds.val[1] = i;
            sMaxFound = true;
        }

        if(vTotal >= fiveCutoff && !vMinFound)
        {
            vThresholds.val[0] = i;
            vMinFound = true;
        }
        else if(vTotal >= ninetyFiveCutoff && !vMaxFound)
        {
            vThresholds.val[1] = i;
            vMaxFound = true;
        }


        if(vMaxFound && sMaxFound && hMaxFound)
        {
            break;
        }
    }

    std::vector<cv::Scalar> returnVect;
    returnVect.push_back(hThresholds);
    returnVect.push_back(sThresholds);
    returnVect.push_back(vThresholds);
    return returnVect;
}

我要做的是总结每个桶中的条目数,直到我得到一个大于或等于总数的百分之五和百分之九十五的数字。不幸的是,如果我手工进行阈值处理,我得到的数字永远不会接近我得到的数字。

1 个答案:

答案 0 :(得分:3)

Mat img =  ... // from camera or some other source

// STEP 1: learning phase
Mat hsv, imgThreshed, processed, denoised;
cv::GaussianBlur(img, denoised, cv::Size(5,5), 2, 2); // remove noise
cv::cvtColor(denoised, hsv, CV_BGR2HSV);

// lets say we picked manually a region of 100x100 px with the interested color/object using mouse
cv::Mat roi = hsv (cv::Range(mousex-50, mousey+50), cv::Range(mousex-50, mousey+50));

// must split all channels to get Hue only
std::vector<cv::Mat> hsvPlanes;
cv::split(roi, hsvPlanes);

// compute statistics for Hue value
cv::Scalar mean, stddev;
cv::meanStdDev(hsvPlanes[0], mean, stddev);

// ensure we get 95% of all valid Hue samples (statistics 3*sigma rule)
float minHue = mean[0] - stddev[0]*3;
float maxHue = mean[0] + stddev[0]*3;

// STEP 2: detection phase
cv::inRange(hsvPlanes[0], cv::Scalar(minHue), cv::Scalar(maxHue), imgThreshed);
imshow("thresholded", imgThreshed);

cv_erode(imgThreshed, processed, 5);  // minimizes noise
cv_dilate(processed, processed, 20);  // maximize left regions

imshow("final", processed);

//STEP 3: do some blob/contour detection on processed image & find maximum blob/region, etc ...

一个更简单的解决方案 - 只需计算平均值&amp;性病。感兴趣区域的偏差,即包含色调值。 由于Hue是图像中最稳定的成分,因此其他成分饱和度高。值应该被丢弃,因为它们变化太大。但是,如果需要,您仍然可以为它们计算平均值。

Image results of the algorithm