2011-07-13 11 views
7

私はPythonでProcrustes Analysisアルゴリズムを実装していましたが、最近OpenCV/C++に移植するように言われました。私はいくつかのテストを実行した後、同じ入力/インスタンスに対して、C++コードはPythonコードの2倍の時間を費やしています(約8対4秒です。あまりにも小さい期間に測定していない)。私はこれらの結果にうんざりしています。OpenCV/C++プログラムのnumpyの対応よりも遅いですが、どうすればいいですか?

私はgpr​​ofを使って何が起こっているのか理解しようとしましたが、cv :: Mat ::〜Mat()が実行の34.67%を占めているという事実の他に、他の機能よりも100倍以上頻繁に呼び出されます。 cv :: Matsをstd :: vectorsやraw配列の代わりにしなければ、私はそれについて何をすべきか分かりません。両方とも私にとって悪い習慣のように思えます。

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); 
} 

これは、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; 
} 

私のマシンでは約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() 

C++の例では、それだけを10回行っているもののパイソン(Fortranの)1時間を更新し、まあ大丈夫それに1000

をやっているのに対し、4+秒かかります。

私が使用していたPythonコードを見直したところ、ポイントの一部(約5%)しかロードされていないことに気付きました。つまり、私のC++テストは実際にPythonコードの約20倍のインスタンスを実行していました。 C++コードは実際には約10倍高速ですが、コードは2倍の遅さしかなかったためです。しかし、まだいくつかの操作でnumpyがOpenCVのビートを持っているようです。

+3

コードまたはそれは起こらなかった。 –

+1

代表的なコードを記入してください。それ以上の詳細なしで言うことは不可能です。 –

+0

C++とPythonを比較していないので、C++とFortranを比較しています。それが数字の場合は、Fortran *が勝ちます。 –

答えて

1
for(int i = 0; i < 10; i++) { 
     cv::Mat m3 = m1 * m2; 
} 

これは、M3は、ループの各反復で破壊され、C++で全く無意味です - あなたはすべてのそれらのデストラクタ呼び出しを取得する理由です。

編集:

cv::Mat m3 = m1 * m2; 

m3 = np.dot(m1, m2) 

同じものではありません。あなたはnumpyでのクロス積とopencvでのドット積を比較しようとしましたか?

+1

私はループ全体が-O3で最適化されていると思うでしょう。 – pmr

+0

私は自分のコードでそれを使用していないことを認識しています。私はIgnacioの仮説を検証するために後でそれを追加しました。 – friday

+0

http://opencv.willowgarage.com/documentation/cpp/basic_structures.html(セクションのマトリックス式)に従って、A * BはOpenCVの行列乗算です。 http://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.htmlによると、numpy.dotは2D配列の行列乗算です。私は間違っているかもしれませんが、私が知る限りでは、2つの方法は同じ例です。 – friday

関連する問題