OpenCV/C++-Programm langsamer als sein numpy-Gegenstück, was soll ich tun?

OpenCV/C++-Programm langsamer als sein numpy-Gegenstück, was soll ich tun?


Ich habe vor einiger Zeit den Procrustes-Analysealgorithmus in Python implementiert und wurde kürzlich aufgefordert, ihn auf OpenCV/C++ zu portieren. Nachdem ich es beendet hatte, führte ich einige Tests durch und für die gleichen Eingaben/Instanzen benötigte der C++-Code doppelt so viel Zeit wie der Python-Code (ungefähr 8 vs. 4 Sekunden). Ich wiederhole die Tests tausend Mal, nur um sicherzustellen, dass ich messe sie nicht über einen zu kleinen Zeitraum). Ich bin verblüfft über diese Ergebnisse.


Ich habe gprof verwendet, um zu versuchen zu verstehen, was vor sich geht, aber ich kann nicht sagen, dass viel falsch ist, abgesehen von der Tatsache, dass cv::Mat::~Mat() 34,67 % der Ausführungszeit benötigt und aufgerufen wird Mehr als 100 Mal häufiger als alle anderen Funktionen. Ich bin mir auch nicht sicher, was ich dagegen tun soll, es sei denn, ich soll cv::Mats durch std::vectors oder rohe Arrays ersetzen, was mir beide wie eine schlechte Praxis erscheinen würde.


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

Dies ist der Code zum Ausrichten von 2 Punktsätzen. Es ruft einige Funktionen auf, die nicht gezeigt werden, aber sie sind einfach und ich kann sie bei Bedarf erklären, obwohl ich hoffe, dass die Namen ausreichen, um zu verstehen, was sie tun.


Ich bin ein Gelegenheits-C++-Programmierer, seid sanft zu mir, Leute.


Es scheint, als hätte Ignacio Vazquez-Abrams die richtige Idee. Ein prägnanteres/direkteres Beispiel:


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

Das dauert ungefähr 14+ Sekunden in meiner Maschine. Jetzt 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()

Das dauert mehr als 4 Sekunden, obwohl das C++-Beispiel es nur 10 Mal macht, während es das Python(Fortran)-Beispiel 1000 Mal macht.


Nun gut, Aktualisierungszeit.


Ich überprüfte den von mir verwendeten Python-Code und stellte fest, dass er nur eine Teilmenge der Punkte lud (ungefähr 5 %) ... Was bedeutet, dass meine C++-Tests tatsächlich ungefähr 20-mal mehr Instanzen ausgeführt haben als der Python-Code, also der C++-Code tatsächlich etwa 10-mal schneller, da der Code nur doppelt so langsam war. Es scheint jedoch immer noch so, als ob numpy OpenCV in einigen Operationen schlägt.


Bis Freitag gefragt

Einige Code-Antworten


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);
}
#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;
}
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()
for(int i = 0;
i <
10;
i++) {
cv::Mat m3 = m1 * m2;
}
cv::Mat m3 = m1 * m2;
m3 = np.dot(m1, m2)