我想使用这个公式将BGR cv :: Mat转换为灰色Gray=B OR G OR R; pixel-wise operation
.我试过这个:
cv::Mat diff_channels[3]; cv::split(diff, diff_channels); diff = diff_channels[0] | diff_channels[1] | diff_channels[2];
这可以通过更好的方法实现吗?
另外,如果我想实现Gray=MAX(B,G,R); pixel-wise operation
任何建议?
您可以使用cv::ParallelLoopBody
with cv::parallel_for_
来使用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(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); }
测试
用你的和@akarsakov方法测试,我得到了(时间以毫秒为单位):
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
注意事项
@akarsakov方法(在原始数据上巧妙地工作)通常是更好的方法,因为它非常快速且更容易编写.使用它ParallelLoopBody
只有大图像(至少在我的电脑上)有一些优势.
我假设源图像是连续的.这项检查应该在实践中完成.
测试代码
您可以使用以下代码在PC上评估结果:
#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; }
OpenCV不包含任何合适的内置函数来以这种方式处理单独的通道.如果您想获得最佳性能,您可以自己实施此过程.我建议你这样的事情:
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; } } }
请注意,您需要在硬件上测试此功能的性能,因为它通过使用在OpenCV中实现(或稍后实现)的SIMD指令和并行性而失去了好处.但是这个过程使用较少的额外内存和算术运算.我想它在大多数系统(尤其是嵌入式系统)上运行得更快.它还取决于矩阵的大小.
我系统上的计时(Core i7-4790):
| Matrix size | OpenCV (ms) | My (ms) | |:-----------:|------------:|---------| | 1280*720 | 4 | 1 | | 1920*1080 | 8 | 2 | | 4096*3112 | 41 | 17 |