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

方法 1: 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];
}

方法 3: 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];
}

修正方法 3: 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 に答える