You can use cv::ParallelLoopBody
with cv::parallel_for_
to use OpenCV Concurrency API:
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<uchar>(range.start);
uchar* yD = dst.ptr<uchar>(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);
}
Test
Testing with your and @akarsakov method, I got (time in 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
Considerations
@akarsakov method (working smartly on raw data) is in general the better approach, since it's very fast and easier to write. Using the ParallelLoopBody
has some advantage only with large images (at least on my pc).
I assumed source image to be continuous. This check should be done in practice.
Testing code
You can evaluate the results on your pc using this code:
#include <opencv2/opencv.hpp>
#include <iostream>
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<uchar>(range.start);
uchar* yD = dst.ptr<uchar>(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<uchar>(row);
uchar* dst_ptr = dst.ptr<uchar>(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<Size> 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;
}