在OpenCV中高效遍历16位Mat的像素的方法

5
我正在尝试在16位灰度OpenCV Mat上进行非常简单的(类似LUT)操作,这既有效又不会减慢调试器的速度。
虽然文档中有一页非常详细地介绍了这个问题,但它未指出大多数这些方法仅适用于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://stackoverflow.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://stackoverflow.com/a/27099697/518169指向了内置LUT函数的源代码,但它只提到了高级优化技术,如IPP和OpenCL。

我正在寻找一个非常简单的循环,就像第一段代码一样,但是用于无符号短整型。

您推荐哪种方法来解决这个问题?我不需要极端优化,只需要与.data上的单for循环性能相当的东西。

3个回答

5
我已经采纳了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。

“table” 是什么意思? - Adam Polak Moetsi

4

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

  • 8位无符号整数(uchar)
  • 8位有符号整数(schar)
  • 16位无符号整数(ushort)
  • 16位有符号整数(short)
  • 32位有符号整数(int)
  • 32位浮点数(float)
  • 64位浮点数(double)
  • 由若干个元素组成的元组,其中所有元素具有相同的类型(上述之一)。

如果您的cv :: Mat是连续的,则可以使用指针算术运算通过整个数据指针,并且您只需要使用适合于cv :: Mat的适当指针类型。 此外,请注意cv :: Mat并不总是连续的(它可能是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;
    }
}

2

你所提到的教程中已经有最佳解决方案,它在名为“高效方式”的章节中。你只需要用ushort替换每个uchar实例即可,不需要进行其他更改。


如果你看一下你提到的“高效的方法”,它并没有描述如何处理16位数据。这条评论并没有回答原帖作者的问题。 - Adam Polak Moetsi
除了将uchar更改为ushort之外,8位代码和16位代码之间没有任何区别。这就是需要做的全部。 - Michael Burdinov
我相信你所描述的是这个问题的答案。 对于不熟悉C++/OpenCV的人来说,“你可以这样做”可能对他们来说并不明显。 - Adam Polak Moetsi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接