【问题标题】:Implementation and speed differences of cv::connectedComponents vs cv::findContourscv::connectedComponents vs cv::findContours 的实现和速度差异
【发布时间】:2019-02-15 01:37:16
【问题描述】:

我正在尝试优化我们的一些计算机视觉算法,并决定将 cv::connectedComponentscv::findContours(和 cv::drawContours)进行基准测试,以获得类似的结果。

基本上,我们需要做的就是在二进制图像中找到 blob,然后选择最大的一个 - 一个相当标准的操作。

我对 OpenCV 的效率有点脱节,在过去的几年里我只将它用于 Python 中的算法原型设计,所以我决定对上述两种方法进行基准测试。

我对我的结果有点困惑,因为this comment 似乎暗示findContours 应该慢得多,这与我观察到的相反(结果在帖子中较低)。我怀疑,实际上我的结果表明,在二进制图像上使用findContours,然后将每个轮廓绘制为不同的索引比运行完整的 connectedComponents 分析要快一些。

他们还表明,仅计算这些轮廓的面积,而不是来自 connectedComponentsWithStats 的完整统计数据集要快得多。

我误解这里发生了什么吗?我希望这两种方法会产生相似的结果。


计时结果:

Starting simple benchmark (100000 iterations) ...
2668ms to run 100000 iterations of findContours
3358ms to run 100000 iterations of connectedComponents
Starting area calculation benchmark (100000 iterations) ...
2691ms to run 100000 iterations of findContours
11285ms to run 100000 iterations of connectedComponentsWithStats
AVERAGE TIMES (ms): 
findContours:           0.0267
connectedComps:         0.0336
findContours (areas):   0.0269
connectedComps (areas): 0.113

下面的基准代码:

#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <chrono>
#include <iomanip>

typedef std::chrono::high_resolution_clock Clock;

cv::Mat src;
cv::Mat src_hsv;
cv::Mat hueChannel;

int threshLow = 230;
int threshHigh = 255;

long numRuns = 100000;
long benchmarkContours(long numRuns, cv::Mat &mask, bool calculateAreas = false) {

    auto start = Clock::now();

    std::vector<std::vector<cv::Point>> contours;
    std::vector<cv::Vec4i> hierarchy;
    std::vector<double> areas;

    for (long run = 0; run < numRuns; ++run) {
        cv::Mat markers = cv::Mat::zeros(mask.size(), CV_8UC1);
        cv::findContours(mask.clone(), contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
        if (calculateAreas) {
            areas = std::vector<double>(contours.size());
        }

        for (unsigned int i = 0; i < contours.size(); i++) {
            if (calculateAreas) {
                areas.push_back(cv::contourArea(contours[i]));
            }
            cv::drawContours(markers, contours, i, cv::Scalar::all(i), -1);
        }
    }

    auto end = Clock::now();

    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}

long benchmarkConnComp(long numRuns, cv::Mat &mask, bool calculateAreas = false) {

    auto start = Clock::now();

    cv::Mat labeledImage;
    cv::Mat stats;
    cv::Mat centroids;
    for (long run = 0; run < numRuns; ++run) {
        if (calculateAreas) {
            cv::connectedComponentsWithStats(mask, labeledImage, stats, centroids);
        } else {
            cv::connectedComponents(mask, labeledImage);
        }
    }

    auto end = Clock::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

}

int main(int, char **argv) {
    src = cv::imread(argv[1]);
    if (src.empty()) {
        std::cerr << "No image supplied ..." << std::endl;
        return -1;
    }

    cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV_FULL);

    std::vector<cv::Mat> hsvChannels = std::vector<cv::Mat>(3);
    cv::split(src, hsvChannels);

    hueChannel = hsvChannels[0];

    cv::Mat mask;
    cv::inRange(hueChannel, cv::Scalar(threshLow), cv::Scalar(threshHigh), mask);

    std::cout << "Starting simple benchmark (" << numRuns << " iterations) ..." << std::endl;
    long findContoursTime = benchmarkContours(numRuns, mask);
    std::cout << findContoursTime << "ms to run " << numRuns << " iterations of findContours" << std::endl;

    long connCompTime = benchmarkConnComp(numRuns, mask);
    std::cout << connCompTime << "ms to run " << numRuns << " iterations of connectedComponents" << std::endl;
    std::cout << "Starting area calculation benchmark (" << numRuns << " iterations) ..." << std::endl;

    long findContoursTimeWithAreas = benchmarkContours(numRuns, mask, true);
    std::cout << findContoursTimeWithAreas << "ms to run " << numRuns << " iterations of findContours" << std::endl;

    long connCompTimeWithAreas = benchmarkConnComp(numRuns, mask, true);
    std::cout << connCompTimeWithAreas << "ms to run " << numRuns << " iterations of connectedComponentsWithStats" << std::endl;

    std::cout << "AVERAGE TIMES: " << std::endl;
    std::cout << "findContours:           " << std::setprecision(3) << (1.0f * findContoursTime) / numRuns << std::endl;
    std::cout << "connectedComps:         " << std::setprecision(3) << (1.0f * connCompTime) / numRuns <<  std::endl;
    std::cout << "findContours (areas):   " << std::setprecision(3) << (1.0f * findContoursTimeWithAreas) / numRuns << std::endl;
    std::cout << "connectedComps (areas): " << std::setprecision(3) << (1.0f * connCompTimeWithAreas) / numRuns <<  std::endl;
}

【问题讨论】:

  • 你在什么样的 CPU 上测试这个?
  • 我在 Macbook Pro 2018 上运行了这个测试,但最终代码最终将通过移动设备上的 C# 接口运行。

标签: c++ opencv connected-components


【解决方案1】:

我还没有真正研究过 OpenCV 中的这两个函数,但我认为 connectedcomponents() 函数更多地取决于图像大小,因为它可能会对图像进行某种多线程光栅化(行-线处理)。而findcontour() 函数可能会以某种方式遍历轮廓,因此性能将取决于 blob 本身的复杂性和大小,而不是图像大小。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 2012-09-17
    相关资源
    最近更新 更多