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

本质上,我们要做的就是在二进制镜像中找到斑点,然后选择最大的斑点-相当标准的操作。

我有点不了解OpenCV的效率,在过去的几年中仅将它用于Python的算法原型(prototype)设计,因此我决定对上述两种方法进行基准测试。

我对我的结果感到有些困惑,因为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;
}

最佳答案

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

10-08 09:16