我正在尝试优化某些计算机视觉算法,并决定将cv::connectedComponents
与cv::findContours
(和cv::drawContours
)进行基准测试,以达到相似的结果。
本质上,我们要做的就是在二进制映像中找到blob,然后选择最大的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;
}
答案 0 :(得分:0)
我还没有真正在OpenCV中研究过这两个函数,但是我认为connectedcomponents()
函数更多地取决于图像大小,因为它可能会对图像进行某种多线程光栅化(逐行处理)。 findcontour()
函数可能会以某种方式沿轮廓移动,因此性能将取决于Blob本身的复杂程度和大小,而不是图像大小。