OpenCV/C++程序比numpy版本慢,该怎么办?
我之前在Python中实现了一个叫做Procrustes分析的算法,最近有人让我把它移植到OpenCV/C++上。完成后我进行了测试,发现对于相同的输入,C++代码的运行时间是Python代码的两倍(大约8秒对4秒)。我重复测试了一千次,确保不是因为测试时间太短导致的。我对这个结果感到很困惑。
我使用了gprof工具来试图理解发生了什么,但我发现除了cv::Mat::~Mat()占用了34.67%的执行时间,并且被调用的次数比其他任何函数都多100多次之外,似乎没有什么明显的问题。我也不太确定该怎么处理这个,除非我把cv::Mat换成std::vector或者原始数组,但这两种做法我觉得都不太好。
void align(const cv::Mat& points, const cv::Mat& pointsRef, cv::Mat& res, cv::Mat& ops) {
cv::Mat pts(points.rows, points.cols, CV_64FC1);
cv::Mat ptsRef(points.rows, points.cols, CV_64FC1);
points.copyTo(pts);
pointsRef.copyTo(ptsRef);
cv::Mat avgs = meanOfColumns(pts);
for(int i = 0; i < avgs.cols; i++) {
pts.col(i) -= avgs.col(i);
}
cv::Mat avgsR = meanOfColumns(ptsRef);
for(int i = 0; i < avgsR.cols; i++) {
ptsRef.col(i) -= avgsR.col(i);
}
cv::Mat x2(pts.rows, 1, CV_64FC1);
cv::Mat y2(pts.rows, 1, CV_64FC1);
cv::Mat x2R(pts.rows, 1, CV_64FC1);
cv::Mat y2R(pts.rows, 1, CV_64FC1);
cv::pow(pts.col(0), 2, x2);
cv::pow(pts.col(1), 2, y2);
cv::pow(ptsRef.col(0), 2, x2R);
cv::pow(ptsRef.col(1), 2, y2R);
cv::Mat sqrootP(pts.rows, 1, CV_64FC1);
cv::Mat sqrootPR(pts.rows, 1, CV_64FC1);
cv::sqrt(x2R + y2R, sqrootPR);
cv::sqrt(x2 + y2, sqrootP);
double offsetS = (cv::mean(sqrootPR) / cv::mean(sqrootP))[0];
pts *= offsetS;
cv::Mat rot(pts.rows, 1, CV_64FC1);
cv::Mat rotR(pts.rows, 1, CV_64FC1);
rot = arctan2(pts.col(1), pts.col(0));
rotR = arctan2(ptsRef.col(1), ptsRef.col(0));
double offsetR = -cv::mean((rot - rotR))[0];
cv::Mat angRot(pts.rows, 1, CV_64FC1);
angRot = rot + offsetR;
cv::Mat dist(pts.rows, 1, CV_64FC1);
cv::pow(pts.col(0), 2, x2);
cv::pow(pts.col(1), 2, y2);
cv::sqrt(x2 + y2, dist);
copyColumn(dist.mul(cosine(angRot)), res, 0, 0);
copyColumn(dist.mul(sine(angRot)), res, 0, 1);
ops.at<double>(0, 0) = -avgs.at<double>(0, 0);
ops.at<double>(0, 1) = -avgs.at<double>(0, 1);
ops.at<double>(0, 2) = offsetS * cv::cos(offsetR / RADIANS_TO_DEGREES);
ops.at<double>(0, 3) = offsetS * cv::sin(offsetR / RADIANS_TO_DEGREES);
}
这是用来对齐两组点的代码。它调用了一些没有展示的函数,但那些函数很简单,如果需要我可以解释,不过我希望函数名能让你明白它们的作用。
我只是个业余的C++程序员,大家轻点对我。
看起来Ignacio Vazquez-Abrams的想法是对的。这里有一个更简洁直接的例子:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <cv.hpp>
#include <iostream>
using namespace boost::posix_time;
int main() {
cv::Mat m1(1000, 1000, CV_64FC1);
cv::Mat m2(1000, 1000, CV_64FC1);
ptime firstValue( microsec_clock::local_time() );
for(int i = 0; i < 10; i++) {
cv::Mat m3 = m1 * m2;
}
ptime secondValue( microsec_clock::local_time() );
time_duration diff = secondValue - firstValue;
std::cout << diff.seconds() << "." << diff.fractional_seconds() << " microsec" << std::endl;
}
在我的机器上,这个大约需要14秒以上。现在来看Python:
import datetime
import numpy as np
if __name__ == '__main__':
print datetime.datetime.now()
m1 = np.zeros((1000, 1000), dtype=float)
m2 = np.zeros((1000, 1000), dtype=float)
for i in range(1000):
m3 = np.dot(m1, m2)
print datetime.datetime.now()
这个大约需要4秒以上,不过C++的例子只运行了10次,而Python(Fortran)的例子运行了1000次。
好吧,更新一下情况。
我回顾了一下我用的Python代码,发现它只加载了一部分点(大约5%)……这意味着我的C++测试实际上运行的实例比Python代码多了大约20倍,所以C++代码实际上快了大约10倍,因为它只慢了两倍。不过看起来在某些操作上,numpy还是比OpenCV更快。
1 个回答
for(int i = 0; i < 10; i++) {
cv::Mat m3 = m1 * m2;
}
在C++中,这完全没有意义,因为在每次循环中,m3都会被销毁——这就是你看到那么多析构函数被调用的原因。
补充:
cv::Mat m3 = m1 * m2;
而且
m3 = np.dot(m1, m2)
并不是同一回事。你有没有尝试过在numpy中比较叉乘,或者在opencv中比较点乘?