OpenCV/C++程序比numpy程序慢,我该怎么办

OpenCV/C++ program slower than its numpy counterpart, what should I do?

本文关键字:程序 我该怎么办 numpy C++ OpenCV      更新时间:2023-10-16

我不久前在Python中实现了Procrustes Analysis算法,最近被告知要将其移植到OpenCV/C++。完成后,我运行了一些测试,对于相同的输入/实例,C++代码所花费的时间是Python代码的两倍(分别约为8秒和4秒。我重复了一千次测试,只是为了确保我不会在太短的时间内测量它们(。我对这些结果感到困惑。

我使用gprof试图了解发生了什么,但除了cv::Mat::~Mat((占用了34.67%的执行时间,并且被调用的频率比任何其他函数都高出100多次之外,我看不出有什么错误。我也不知道该怎么办,除非我应该用cv::Mats代替std::vectors或原始数组,这两种方法对我来说都是一种糟糕的做法

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++程序员,伙计们,对我宽容一点。

看来伊格纳西奥·巴斯克斯·艾布拉姆斯的想法是正确的。一个更简洁/直接的例子:

#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倍,因为代码的速度只有Python代码的两倍。不过,numpy在某些操作中似乎仍然击败了OpenCV。

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中的点积?