12

我有一张大约有 50 到 100 个小轮廓的图像。我希望实时找到每个轮廓的平均强度 [1] [2]。我能想到的一些方法是

  1. 使用 FILLED 选项为每个轮廓绘制轮廓;使用每个图像作为原始图像的掩码,从而找到平均值。但我认为这种方法乍一看不会是实时的。

  2. 使用 FILLED 选项研究函数的 OpenCV 实现,drawContour并以相同的方式访问轮廓所包围的像素。但是代码看起来真的很复杂而且不容易理解。

  3. 计算最小面积矩形,使用变换找到矩形内的所有点,并找到非零点的平均值。再次,似乎复杂的方法。

有没有更简单、更有效的方法来做到这一点?

[1] 每个非重叠轮廓包围的所有像素强度的平均值

[2] 在 2.66 Ghz 台式 PC 上每秒大约 25 (960 x 480) 像素图像

4

1 回答 1

15

我无法想出与您建议的方法大不相同的任何方法。但是,我能够做一些可能有助于指导您的决定的时间安排。我所有的计时都是在 iMac 上的 1280*720 图像上运行的,仅限于找到 100 个轮廓。您的机器上的时间当然会有所不同,但相对时间应该是有用的。

对于每个测试用例,声明以下内容:

std::vector<std::vector<cv::Point>> cont;  // Filled by cv::findContours()
cv::Mat labels = cv::Mat::zeros(image.size(), CV_8UC1);     
std::vector<float> cont_avgs(cont.size(), 0.f); // This contains the averages of each contour

方法一:19.0ms

方法 1 在概念上是最简单的,但也是最慢的。通过为每个轮廓分配唯一的颜色来标记每个轮廓。通过迭代图像中的每个像素来求和每个标记组件的值。

for (size_t i = 0; i < cont.size(); ++i)
{
    // Labels starts at 1 because 0 means no contour
    cv::drawContours(labels, cont, i, cv::Scalar(i+1), CV_FILLED);
}

std::vector<float> counts(cont.size(), 0.f);
const int width = image.rows;
for (size_t i = 0; i < image.rows; ++i)
{
    for (size_t j = 0; j < image.cols; ++j)
    {
        uchar label = labels.data[i*width + j];

        if (label == 0)
        {
            continue;   // No contour
        }
        else
        {
            label -= 1; // Make labels zero-indexed
        }

        uchar value = image.data[i*width + j];
        cont_avgs[label] += value;
        ++counts[label];
    }
}
for (size_t i = 0; i < cont_avgs.size(); ++i)
{
    cont_avgs[i] /= counts[i];
}

方法三:15.7ms

未经修改的方法 3 具有最简单的实现,也是最快的。所有轮廓都被填充以用作查找平均值的掩码。计算每个轮廓的边界矩形,然后使用边界框中的掩码计算平均值。

警告:如果任何其他轮廓在感兴趣轮廓的边界矩形内,此方法将给出不正确的结果。

cv::drawContours(labels, cont, -1, cv::Scalar(255), CV_FILLED);

for (size_t i = 0; i < cont.size(); ++i)
{
    cv::Rect roi = cv::boundingRect(cont[i]);
    cv::Scalar mean = cv::mean(image(roi), labels(roi));
    cont_avgs[i] = mean[0];
}

修改方法三:17.8ms

对方法 3 稍作修改会稍微增加执行时间,但无论轮廓位置如何,都能获得正确结果的好处。每个轮廓都单独标记,并且仅使用该轮廓的掩码计算平均值。

for (size_t i = 0; i < cont.size(); ++i)
{
    cv::drawContours(labels, cont, i, cv::Scalar(i), CV_FILLED);
    cv::Rect roi = cv::boundingRect(cont[i]);
    cv::Scalar mean = cv::mean(image(roi), labels(roi) == i);
    cont_avgs[i] = mean[0];
}
于 2013-07-30T20:29:35.833 回答