2011-07-13 12 views
7

Implementé hace algún tiempo el algoritmo de análisis de Procrustes en Python y me dijeron que lo transfiriera a OpenCV/C++ recientemente. Después de terminarlo realicé algunas pruebas y, para las mismas entradas/instancias, el código C++ está tomando el doble de tiempo que el código Python (aproximadamente 8 vs 4 segundos, respectivamente. Estoy repitiendo las pruebas mil veces solo para asegurarme de que no los estoy midiendo durante un período demasiado pequeño). Estoy desconcertado por estos resultados.Programa OpenCV/C++ más lento que su contraparte numpy, ¿qué debo hacer?

He usado gprof para tratar de entender lo que está pasando, pero no puedo decir que muchas cosas estén mal, además de que cv :: Mat :: ~ Mat() está tomando el 34.67% de la ejecución tiempo y ser llamado más de 100 veces que cualquier otra función. No estoy seguro de qué hacer al respecto tampoco, a menos que se suponga que debo reemplazar cv :: Mats por std :: vectores o matrices en bruto, las cuales me parecerían una mala práctica.

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

Este es el código para alinear 2 conjuntos de puntos. Llama a algunas funciones que no se muestran, pero son simples y puedo explicarlas si es necesario, aunque espero que los nombres sean suficientes para entender lo que hacen.

Soy un programador ocasional de C++, no me moleste chicos.

Parece que Ignacio Vazquez-Abrams tiene la idea correcta. Un ejemplo más conciso/directo:

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

Eso lleva alrededor de 14+ segundos en mi máquina. Ahora 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() 

que se lleva a 4+ segundos, aunque el ejemplo C++ está haciendo solamente 10 veces, mientras que el pitón (Fortran) lo está haciendo 1000.

Pues bien, tiempo de actualización.

Revisé el código de Python que estaba usando y me di cuenta de que solo cargaba un subconjunto de puntos (aproximadamente 5%) ... Lo que significa que mis pruebas de C++ se ejecutan unas 20 veces más veces que el código de Python, el código C++ es en realidad alrededor de 10 veces más rápido, ya que el código era solo dos veces más lento. Sin embargo, parece que Numpy tiene un ritmo de OpenCV en algunas operaciones.

+3

Código o no sucedió. –

+1

Por favor, publique un código de representante. Es imposible decir sin más detalles. –

+0

No está comparando C++ con Python, está comparando C++ con Fortran. Si se trata de números crujientes, entonces Fortran * ganará *. –

Respuesta

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

Esto es totalmente inútil en C++, el M3 se destruye en cada iteración del bucle - es por eso que usted consigue todas esas llamadas destructor.

edición:

cv::Mat m3 = m1 * m2; 

y

m3 = np.dot(m1, m2) 

no son la misma cosa. ¿Has intentado comparar un producto cruzado en numpy o un producto de punto en opencv?

+1

Incluso sospecho que todo el ciclo se optimiza con -O3. – pmr

+0

Me doy cuenta de que no estoy usando eso en mi código. Solo lo agregué más tarde para probar la hipótesis de Ignacio. – friday

+0

Según http://opencv.willowgarage.com/documentation/cpp/basic_structures.html (sección expresiones de matriz), A * B es la multiplicación de matrices en OpenCV. De acuerdo con http://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html, numpy.dot es la multiplicación de matrices para matrices 2D. Podría estar equivocado, pero hasta donde puedo decir, los dos métodos son equivalentes en los ejemplos dados. – friday

Cuestiones relacionadas