在 OpenCV 中循环遍历 16 位 Mat 像素的有效方法

Efficient way to loop through pixels of 16-bit Mat in OpenCV

我正在尝试在 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;
        }
    }

改编自此答案:。对我不起作用,而且速度很慢。


cv::MatIterator_<ushort> it, end;
    for (it = mat_depth.begin<ushort>(), end = mat_depth.end<ushort>(); it != end; ++it)
    {
       *it = ...;   
    }

效果很好,但是它使用了很多 CPU 并使调试器超级慢。


这个答案 points out to the source code of the built-in LUT function,但是它只提到了高级优化技术,例如 IPP 和 OpenCL。

我正在寻找的是一个非常简单的循环,就像第一个代码,但是对于 ushorts。

你推荐什么方法来解决这个问题?我不是在寻找极端的优化,只是与 .data 上的单 for 循环的性能相当。

针对您的问题的最佳解决方案已经写在您提到的教程中,在名为 "The efficient way" 的章节中。您只需将 uchar 的每个实例替换为 ushort。不需要其他更改。

OpenCV 实现基于模板的多态性和运行时分派。在 OpenCV 版本中,模板的使用仅限于一组固定的原始数据类型。即,数组元素应具有以下类型之一:

  • 8 位无符号整数 (uchar)
  • 8 位有符号整数 (schar)
  • 16 位无符号整数(ushort)
  • 16 位有符号整数(短)
  • 32 位有符号整数 (int)
  • 32位浮点数(float)
  • 64 位浮点数(双精度)
  • 多个元素的元组,其中所有元素都具有相同类型(以上之一)。

如果您的 cv::Mat 继续,您可以使用指针算法来遍历整个数据指针,并且您应该只使用适合您的 cv::Mat 的指针类型。 此外,请记住 cv::Mats 并不总是连续的(它可以是 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;
    }
}

我实施了 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;
}

结果如下(release/debug):

  • LUT_16:0.202 毫秒/0.773 毫秒
  • LUT_16_reinterpret_cast:0.184 毫秒 / 0.801 毫秒
  • LUT_16_if: 0.249 毫秒/0.860 毫秒

所以结论是reinterpret_cast在release模式下快了9%,而ptr在debug模式下快了4%

有趣的是,直接调用 if 函数而不是应用 LUT 只会使它慢 0.065 毫秒。

规格:流式传输 640x480x16 位灰度图像,Visual Studio 2013,i7 4750HQ。