在 OpenCV 中循环遍历 16 位 Mat 像素的有效方法
Posted
技术标签:
【中文标题】在 OpenCV 中循环遍历 16 位 Mat 像素的有效方法【英文标题】:Efficient way to loop through pixels of 16-bit Mat in OpenCV 【发布时间】:2015-02-10 03:41:54 【问题描述】:我正在尝试在 16 位灰度 OpenCV Mat 上进行非常简单(类似 LUT)的操作,这种操作非常高效且不会减慢调试器的速度。
虽然有一个 very detailed page in the documentation 正好解决了这个问题,但它没有指出大多数这些方法仅适用于 8 位图像(包括完美的优化 LUT 功能)。
我尝试了以下方法:
uchar* p = mat_depth.data;
for (unsigned int i = 0; i < depth_width * depth_height * sizeof(unsigned short); ++i)
*p = ...;
*p++;
真的很快,可惜只支持 uchart(就像 LUT)。
int i = 0;
for (int row = 0; row < depth_height; row++)
for (int col = 0; col < depth_width; col++)
i = mat_depth.at<short>(row, col);
i = ..
mat_depth.at<short>(row, col) = i;
改编自这个答案:https://***.com/a/27225293/518169。对我不起作用,而且速度很慢。
cv::MatIterator_<ushort> it, end;
for (it = mat_depth.begin<ushort>(), end = mat_depth.end<ushort>(); it != end; ++it)
*it = ...;
运行良好,但它使用大量 CPU 并使调试器超级慢。
这个答案https://***.com/a/27099697/518169 指向source code of the built-in LUT function,但是它只提到了高级优化技术,如 IPP 和 OpenCL。
我正在寻找的是一个非常简单的循环,就像第一个代码一样,但是对于 ushorts。
你推荐什么方法来解决这个问题?我不是在寻找极端优化,只是在 .data 上与单循环的性能相提并论。
【问题讨论】:
【参考方案1】:我实施了 Michael 和 Kornel 的建议,并在发布和调试模式下对它们进行了基准测试。
代码:
cv::Mat LUT_16(cv::Mat &mat, ushort table[])
int limit = mat.rows * mat.cols;
ushort* p = mat.ptr<ushort>(0);
for (int i = 0; i < limit; ++i)
p[i] = table[p[i]];
return mat;
cv::Mat LUT_16_reinterpret_cast(cv::Mat &mat, ushort table[])
int limit = mat.rows * mat.cols;
ushort* ptr = reinterpret_cast<ushort*>(mat.data);
for (int i = 0; i < limit; i++, ptr++)
*ptr = table[*ptr];
return mat;
cv::Mat LUT_16_if(cv::Mat &mat)
int limit = mat.rows * mat.cols;
ushort* ptr = reinterpret_cast<ushort*>(mat.data);
for (int i = 0; i < limit; i++, ptr++)
if (*ptr == 0)
*ptr = 65535;
else
*ptr *= 100;
return mat;
ushort* tablegen_zero()
static ushort table[65536];
for (int i = 0; i < 65536; ++i)
if (i == 0)
table[i] = 65535;
else
table[i] = i;
return table;
结果如下(发布/调试):
LUT_16:0.202 毫秒/0.773 毫秒 LUT_16_reinterpret_cast:0.184 毫秒 / 0.801 毫秒 LUT_16_if:0.249 毫秒/0.860 毫秒所以结论是 reinterpret_cast 在发布模式下快 9%,而 ptr 在调试模式下快 4%。
有趣的是,直接调用 if 函数而不是应用 LUT 只会使其慢 0.065 毫秒。
规格:流式传输 640x480x16 位灰度图像,Visual Studio 2013,i7 4750HQ。
【讨论】:
什么是“表”?【参考方案2】:OpenCV 实现基于模板上的多态性和运行时调度。在 OpenCV 版本中,模板的使用仅限于一组固定的原始数据类型。也就是说,数组元素应具有以下类型之一:
8 位无符号整数 (uchar) 8 位有符号整数 (schar) 16 位无符号整数 (ushort) 16 位有符号整数(短) 32 位有符号整数 (int) 32 位浮点数 (float) 64 位浮点数(双精度) 多个元素的元组,其中所有元素都具有相同的类型(上述类型之一)。如果您的cv::Mat
继续存在,您可以使用指针算术来遍历整个数据指针,并且您应该只使用适合您的cv::Mat
的指针类型。
此外,请记住,cv::Mat
s 并不总是连续的(它可以是 ROI、填充或从像素指针创建)并且使用指针迭代它们会崩溃。
一个示例循环:
cv::Mat cvmat16sc1 = cv::Mat::eye(10, 10, CV_16SC1);
if (cvmat16sc1.data)
if (!cvmat16sc1.isContinuous())
cvmat16sc1 = cvmat16sc1.clone();
short* ptr = reinterpret_cast<short*>(cvmat16sc1.data);
for (int i = 0; i < cvmat16sc1.cols * cvmat16sc1.rows; i++, ptr++)
if (*ptr == 1)
std::cout << i << ": " << *ptr << std::endl;
【讨论】:
【参考方案3】:问题的最佳解决方案已经写在您提到的教程中,在名为“有效方法”的一章中。您所需要的只是用 ushort 替换 uchar 的每个实例。无需进行其他更改。
【讨论】:
如果您查看您提到的“有效方式”,它并没有描述如何处理 16 位数据。此评论不回答原发帖者。 8 位代码和 16 位代码没有区别,除了 ushort 的 uchar 变化。这就是所有需要做的事情 我相信您所描述的是问题的答案。这对您来说似乎很明显,但是知道对不熟悉 C++/OpenCV 的人来说“您可以做到这一点”是不可忽略的以上是关于在 OpenCV 中循环遍历 16 位 Mat 像素的有效方法的主要内容,如果未能解决你的问题,请参考以下文章
LIBTIFF 16位灰度tif图像转成OpenCV中的Mat格式并显示
图像位深度 8位 16位 24位 32位区别对比 RGB 真彩色 基本概念:(大小,深度,通道)位深度数据类型转换原理 Mat数据读取(opencv里的imread)