提案されたアプローチとは大幅に異なる方法を思いつくことができませんでした。しかし、私はあなたの決定を導くのに役立つかもしれないいくつかのタイミングを行うことができました. 私のタイミングはすべて 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];
}