Что делать, если программа OpenCV / C ++ медленнее, чем ее тупой аналог? - PullRequest
7 голосов
/ 13 июля 2011

Некоторое время назад я реализовал алгоритм анализа Procrustes в Python, и недавно мне сказали перенести его на OpenCV / C ++. После его завершения я выполнил несколько тестов, и для тех же входных данных / экземпляров код C ++ занимает вдвое больше времени, чем код Python (примерно 8 против 4 секунд, соответственно. Я повторяю тесты тысячу раз, просто чтобы убедиться, что я не измеряю их за слишком малый период). Я озадачен этими результатами.

Я использовал gprof, чтобы попытаться понять, что происходит, но я не могу сказать, что много ошибаюсь, кроме того факта, что 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 ++, не волнуйтесь, ребята.

Похоже, что Игнасио Васкес-Абрамс имеет правильную идею. Более краткий / прямой пример:

#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 ++ на самом деле выполняли примерно в 20 раз больше экземпляров, чем код Python, поэтому Код C ++ на самом деле примерно в 10 раз быстрее, так как код был только в два раза медленнее. Тем не менее, кажется, что numpy имеет удар OpenCV в некоторых операциях.

1 Ответ

1 голос
/ 13 июля 2011
for(int i = 0; i < 10; i++) {
        cv::Mat m3 = m1 * m2;
}

Это абсолютно бессмысленно в c ++, m3 уничтожается на каждой итерации цикла - вот почему вы получаете все эти вызовы деструктора.

редактирование:

cv::Mat m3 = m1 * m2;

и

m3 = np.dot(m1, m2)

это не одно и то же. Вы пытались сравнить перекрестный продукт в numy или точечный продукт в opencv?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...