Различия в реализации и скорости cv :: connectedComponents и cv :: findContours - PullRequest
0 голосов
/ 10 сентября 2018

Я пытаюсь оптимизировать некоторые из наших алгоритмов компьютерного зрения и решил сравнить cv::connectedComponents с cv::findContourscv::drawContours), чтобы достичь аналогичных результатов.

По сути, все, что нам нужно сделать, это найти капли в двоичном изображении, а затем выбрать самый большой - довольно стандартная операция.

Я немного потерял связь с эффективностью OpenCV, поскольку использовал его только для прототипирования алгоритмов в Python в последние пару лет, поэтому я решил провести тестирование двух методов выше.

Я немного смущен моими результатами, так как этот комментарий , кажется, предполагает, что findContours должен быть намного медленнее, что противоположно тому, что я наблюдаю (результаты ниже в посте) , Я подозревал, и действительно мои результаты показали, что использование findContours на двоичном изображении с последующим рисованием каждого контура в виде отдельного индекса было незначительно быстрее, чем выполнение полного анализа связанных компонентов.

Они также показали, что вычисление только площадей этих контуров, а не полного набора характеристик из 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;
}

1 Ответ

0 голосов
/ 09 апреля 2019

Я на самом деле не изучал эти две функции в OpenCV, но я бы предположил, что функция connectedcomponents() больше зависит от размера изображения, поскольку, вероятно, будет выполнять некоторую многопотоковую растеризацию изображения (line- оперативная обработка). Принимая во внимание, что функция findcontour(), вероятно, каким-то образом обойдет контур, поэтому производительность будет зависеть от сложности и размера самого объекта, а не от размера изображения.

...