Optimiser l’obtention du OU des canaux

Je veux convertir un cv :: Mat de BGR en gris en utilisant cette formule Gray=B OR G OR R; pixel-wise operation Gray=B OR G OR R; pixel-wise operation . J’ai essayé ceci:

 cv::Mat diff_channels[3]; cv::split(diff, diff_channels); diff = diff_channels[0] | diff_channels[1] | diff_channels[2]; 

Cela peut-il être réalisé avec une meilleure approche?

Aussi, si je veux atteindre Gray=MAX(B,G,R); pixel-wise operation Gray=MAX(B,G,R); pixel-wise operation toute suggestion?

OpenCV ne contient aucune fonction intégrée appropriée pour traiter des canaux séparés de cette manière. Si vous souhaitez obtenir des performances maximales, vous pouvez mettre en œuvre cette procédure vous-même. Je vous suggère quelque chose comme ça:

 void calcOrChannels(const cv::Mat& src, cv::Mat& dst) { CV_Assert(src.type() == CV_8UC3); int rows = src.rows, cols = src.cols; dst.create(src.size(), CV_8UC1); if (src.isContinuous() && dst.isContinuous()) { cols = rows * cols; rows = 1; } for (int row = 0; row < rows; row++) { const uchar* src_ptr = src.ptr(row); uchar* dst_ptr = dst.ptr(row); for (int col = 0; col < cols; col++) { dst_ptr[col] = src_ptr[0] | src_ptr[1] | src_ptr[2]; // std::max(src_ptr[0], std::max(src_ptr[1], src_ptr[2])) src_ptr += 3; } } } 

Veuillez noter que vous devez tester les performances de cette fonction sur votre matériel, car celle-ci perd des avantages en utilisant l'instruction SIMD et le parallélisme implémentés (ou éventuellement implémentés ultérieurement) dans OpenCV. Mais cette procédure utilise moins de mémoire supplémentaire et d’opérations arithmétiques. Je suppose que cela fonctionnerait plus rapidement sur la plupart des systèmes (notamment embarqués). Cela dépend aussi de la taille de vos masortingces.

Timings sur mon système (Core i7-4790):

 | Masortingx size | OpenCV (ms) | My (ms) | |:-----------:|------------:|---------| | 1280*720 | 4 | 1 | | 1920*1080 | 8 | 2 | | 4096*3112 | 41 | 17 | 

Vous pouvez utiliser cv::ParallelLoopBody avec cv::parallel_for_ pour utiliser l’API OpenCV Concurrency:

 class ParallelBGRtoGrayOR : public ParallelLoopBody { const Mat3b src; mutable Mat1b dst; public: ParallelBGRtoGrayOR(const Mat3b& _src, Mat1b& _dst) : ParallelLoopBody(), src(_src), dst(_dst) {} virtual void operator()(const Range& range) const { int rows = range.end - range.start; int cols = src.cols; int len = rows * cols; const uchar* yS = src.ptr(range.start); uchar* yD = dst.ptr(range.start); for (int i = 0; i < len; ++i, yD++, yS += 3) { *yD = yS[0] | yS[1] | yS[2]; //*yD = std::max(yS[0], std::max(yS[1], yS[2])); } } }; void cvtBgrToGray_OR_Miki(const Mat3b& src, Mat1b& dst) { dst.create(src.rows, src.cols); parallel_for_(Range(0, src.rows), ParallelBGRtoGrayOR(src, dst), -1); } 

Tester

En testant avec votre méthode et @akarsakov, j'ai eu (temps en ms):

 Size: akarsakov Humam Helfawi Miki OpenCV (not same operation) [10 x 10] 0.00109963 0.0711094 2.60722 0.0934685 [100 x 100] 0.0106298 0.0373874 0.0461844 0.0395867 [1000 x 1000] 1.1799 3.30622 0.747382 1.61646 [1280 x 720] 1.07324 2.91585 0.520858 0.9893 [1920 x 1080] 2.31252 6.87818 1.11502 1.94011 [4096 x 3112] 14.3454 42.0125 6.79644 12.0754 [10000 x 10000] 115.575 321.145 61.1544 93.8846 

Considérations

La méthode @akarsakov (travailler intelligemment sur des données brutes) est généralement la meilleure approche, car elle est très rapide et plus facile à écrire. L'utilisation de ParallelLoopBody présente certains avantages uniquement avec les grandes images (du moins sur mon ordinateur).

J'ai supposé que l'image source était continue. Cette vérification devrait être faite dans la pratique.

Code de test

Vous pouvez évaluer les résultats sur votre ordinateur en utilisant ce code:

 #include  #include  using namespace std; using namespace cv; class ParallelBGRtoGrayOR : public ParallelLoopBody { const Mat3b src; mutable Mat1b dst; public: ParallelBGRtoGrayOR(const Mat3b& _src, Mat1b& _dst) : ParallelLoopBody(), src(_src), dst(_dst) {} virtual void operator()(const Range& range) const { int rows = range.end - range.start; int cols = src.cols; int len = rows * cols; const uchar* yS = src.ptr(range.start); uchar* yD = dst.ptr(range.start); for (int i = 0; i < len; ++i, yD++, yS += 3) { *yD = yS[0] | yS[1] | yS[2]; //*yD = std::max(yS[0], std::max(yS[1], yS[2])); } } }; void cvtBgrToGray_OR_Miki(const Mat3b& src, Mat1b& dst) { dst.create(src.rows, src.cols); parallel_for_(Range(0, src.rows), ParallelBGRtoGrayOR(src, dst), -1); } // credits to @akarsakov void cvtBgrToGray_OR_akarsakov(const Mat3b& src, Mat1b& dst) { int rows = src.rows, cols = src.cols; dst.create(src.size()); if (src.isContinuous() && dst.isContinuous()) { cols = rows * cols; rows = 1; } for (int row = 0; row < rows; row++) { const uchar* src_ptr = src.ptr(row); uchar* dst_ptr = dst.ptr(row); for (int col = 0; col < cols; col++) { dst_ptr[col] = src_ptr[0] | src_ptr[1] | src_ptr[2]; //dst_ptr[col] = std::max(src_ptr[0], std::max(src_ptr[1], src_ptr[2])); src_ptr += 3; } } } // credits to @Humam_Helfawi void cvtBgrToGray_OR_Humam_Helfawi(const Mat3b& src, Mat1b& dst) { cv::Mat channels[3]; cv::split(src, channels); dst = channels[0] | channels[1] | channels[2]; } int main() { vector sizes{ Size(10, 10), Size(100, 100), Size(1000, 1000), Size(1280, 720), Size(1920, 1080), Size(4096, 3112), Size(10000, 10000) }; cout << "Size: \t\takarsakov \tHumam Helfawi \tMiki \tOpenCV (not same operation)" << endl; for (int is = 0; is < sizes.size(); ++is) { Size sz = sizes[is]; cout << sz << "\t"; Mat3b img(sz); randu(img, Scalar(0, 0, 0), Scalar(255, 255, 255)); Mat1b gray_akarsakov; Mat1b gray_Miki; Mat1b gray_Humam; Mat1b grayOpenCV; double tic = double(getTickCount()); cvtBgrToGray_OR_akarsakov(img, gray_akarsakov); double toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency(); cout << toc << " \t"; tic = double(getTickCount()); cvtBgrToGray_OR_Humam_Helfawi(img, gray_Humam); toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency(); cout << toc << " \t"; tic = double(getTickCount()); cvtBgrToGray_OR_Miki(img, gray_Miki); toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency(); cout << toc << " \t"; tic = double(getTickCount()); cvtColor(img, grayOpenCV, COLOR_BGR2GRAY); toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency(); cout << toc << endl; } getchar(); return 0; } 

je voulais juste partager mes résultats

 Size: akarsakov Humam Helfawi Miki OpenCV (not same operation) [10 x 10] 0.00733416 1.03216 1.15244 0.044005 [100 x 100] 0.078231 0.0816536 0.185799 0.043516 [1000 x 1000] 7.81039 5.89764 40.7481 3.49253 [1280 x 720] 7.61432 5.31824 8.74916 1.70397 [1920 x 1080] 16.0256 12.8186 9.32367 3.6045 [4096 x 3112] 97.7365 72.6287 49.3452 22.9545 [10000 x 10000] 763.509 575.718 402.729 197.01 

EDIT: J’ai un nouvel ordinateur portable et testé à nouveau le code sur celui-ci et sur le plus ancien. il semble que la différence de résultats dépende des différentes configurations construites d’OpenCV

1.Intel (R) _Core (TM) _i5-3230M_CPU _ @ _ 2,60 GHz

 Size: akarsakov Humam Helfawi Miki OpenCV (not same operation) [10 x 10] 0.00276318 0.0627636 0.445661 0.0351318 [100 x 100] 0.0303949 0.0734216 0.0457898 0.0663162 [1000 x 1000] 3.01186 5.30727 2.11699 3.05805 [1280 x 720] 2.59975 4.91806 1.82014 2.69528 [1920 x 1080] 5.97478 11.5406 3.56213 5.52556 [4096 x 3112] 37.3076 64.1728 22.4575 35.0398 [10000 x 10000] 284.249 510.332 175.626 268.652 

2.Intel (R) _Core (TM) 2_Duo_CPU _____ T6500__@_2.10GHz

 Size: akarsakov Humam Helfawi Miki OpenCV (not same operation) [10 x 10] 0.00586751 0.107571 24.1966 1.50844 [100 x 100] 0.0704101 0.154511 0.308044 0.119306 [1000 x 1000] 7.00825 11.072 3.44912 5.25778 [1280 x 720] 6.63322 9.88529 3.91999 5.0177 [1920 x 1080] 14.6199 21.8047 7.19357 10.9551 [4096 x 3112] 85.8226 133.165 42.4392 64.2184 [10000 x 10000] 675.604 1050.19 334.334 507.87