OpenCV/C++程序比它的numpy对手慢,我该怎么办?

fri*_*day 7 c++ python opencv numpy image-processing

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

我已经使用gprof试图理解发生了什么,但除了cv :: Mat :: ~Mat()占用34.67%的执行时间并被调用之外,我无法说出错误.比任何其他功能多100倍.不知道我应该怎么做,除非我应该替换std :: vectors或raw数组的cv :: Mats,这对我来说似乎都是不好的做法.

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);
}
Run Code Online (Sandbox Code Playgroud)

这是对齐2组点的代码.它调用了一些未显示的函数,但它们很简单,如果有必要我可以解释它们,但我希望这些名称足以理解它们的作用.

我是一个休闲的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;
}
Run Code Online (Sandbox Code Playgroud)

我的机器需要大约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()
Run Code Online (Sandbox Code Playgroud)

这需要4秒以上,尽管C++示例只执行了10次,而Python(Fortran)则执行了1000次.

好吧,更新时间.

我查看了我正在使用的Python代码并意识到它只加载了一个点的子集(大约5%)...这意味着我的C++测试实际上运行的实例比Python代码多20倍,所以C++代码是实际上快了大约10倍,因为代码只有两倍慢.看起来好像numpy在某些操作中有OpenCV击败.

Mar*_*ett 1

for(int i = 0; i < 10; i++) {
        cv::Mat m3 = m1 * m2;
}
Run Code Online (Sandbox Code Playgroud)

这在 C++ 中是完全没有意义的,m3 在循环的每次迭代中都会被销毁 - 这就是为什么你会得到所有这些析构函数调用。

编辑:

cv::Mat m3 = m1 * m2;
Run Code Online (Sandbox Code Playgroud)

m3 = np.dot(m1, m2)
Run Code Online (Sandbox Code Playgroud)

不是同一件事。您是否尝试过比较 numpy 中的叉积或 opencv 中的点积?