访问 OpenCV Mat 元素时确定模板类型

Posted

技术标签:

【中文标题】访问 OpenCV Mat 元素时确定模板类型【英文标题】:Determining template type when accessing OpenCV Mat elements 【发布时间】:2011-01-20 06:09:28 【问题描述】:

我正在使用以下代码为图像添加一些噪点(直接来自 OpenCV 参考,第 449 页——cv::Mat::begin 的解释):

void
simulate_noise(Mat const &in, double stddev, Mat &out)

    cv::Size s = in.size();
    vector<double> noise = generate_noise(s.width*s.height, stddev);

    typedef cv::Vec<unsigned char, 3> V4;
    cv::MatConstIterator_<V4> in_itr = in.begin<V4>();
    cv::MatConstIterator_<V4> in_end = in.end<V4>();
    cv::MatIterator_<V4> out_itr = out.begin<V4>();
    cv::MatIterator_<V4> out_end = out.end<V4>();

    for (; in_itr != in_end && out_itr != out_end; ++in_itr, ++out_itr)
    
        int noise_index = my_rand(noise.size());
        for (int j = 0; j < 3; ++j)
            (*out_itr)[j] = (*in_itr)[j] + noise[noise_index];
    

没有什么太复杂的:

inout 分配了相同维度和类型的cv::Mat 对象 迭代输入图像in 在每个位置,从noise中选择一个随机值(my_rand(int n)[0..n-1]中返回一个随机数 将来自in 的像素与随机噪声值相加 将求和结果放入out

我不喜欢这段代码,因为下面的语句似乎是不可避免的:

typedef cv::Vec<unsigned char, 3> V4;

它硬编码了两件事:

    图像有 3 个通道 通道深度为 8bpp

如果我得到这个typedef 错误(例如错误的通道深度或错误的通道数),那么我的程序会出现段错误。我最初使用typedef cv::Vec&lt;unsigned char, 4&gt; V4 来处理具有任意数量通道的图像(OpenCV 支持的最大通道数为 4),但这导致了段错误。

有什么办法可以避免对上述两件事进行硬编码?理想情况下,我想要一些通用的东西:

typedef cv::Vec<in.type(), in.size()> V4;

【问题讨论】:

【参考方案1】:

我知道这来晚了。但是,真正解决您的问题的方法是使用 OpenCV 功能来做您想做的事情。

    像你已经做的那样创建噪声向量(或使用 OpenCV 提供的函数提示!) 混洗噪声向量,因此您不需要为每个像素单独设置noise_index;或预先创建随机噪声向量 围绕你的洗牌/随机向量构建一个矩阵头:cv::Mat_&lt;double&gt;(noise); 使用矩阵运算进行计算:out = in + noise;cv::add(in, noise, out); 利润!

此方法的另一个优点是 OpenCV 可能会使用多线程、SSE 或其他任何方法来加速这种大规模元素操作,而您没有这样做。您的代码更简单、更干净,OpenCV 为您完成了所有令人讨厌的类型处理。

【讨论】:

谢谢,这确实听起来更好。【参考方案2】:

问题是您需要在运行时确定通道的类型和数量,但模板需要在编译时获得信息。您可以通过使用cv::splitcv::merge 或将迭代更改为

来避免对通道数进行硬编码
for(int row = 0; row < in.rows; ++row) 
    unsigned char* inp  = in.ptr<unsigned char>(row);
    unsigned char* outp = out.ptr<unsigned char>(row);
    for (int col = 0; col < in.cols; ++col) 
        for (int c = 0; c < in.channels(); ++c) 
            *outp++ = *inp++ + noise();
        
    

如果您想摆脱类型的依赖性,我建议将上述内容放在模板函数中并从您的函数中调用它,具体取决于矩阵的类型。

【讨论】:

感谢您的回复。我最终可能会像你提到的那样使用行指针,但我有点说看到迭代器会消失,因为这是使用它们的理想场所。此外,即使我对函数进行模板化,我仍然必须在编译时指定其类型名,这实际上只是将问题转移到代码中的另一个点。也许我有点愤世嫉俗,但对我来说,在编译时指定这些信息似乎违背了首先拥有模板和多态性的意义。 它不会“仅仅”解决问题——如果你没有模板函数,你必须复制整个类型的代码。此外,您的潜在问题是您试图在不知道图像包含什么的情况下对图像进行操作,这注定会失败(例如,整数值图像与实值图像需要不同的噪声。在您的方法中,如果 generate_noise 返回均值为 0 的高斯噪声,则该噪声将具有使图像变暗的偏差(因为整数将 向下 舍入)。 不,必须在编译时指定此信息并不会破坏拥有模板的意义,它们就是为这个确切的东西而设计的 - 而多态性完全是另一个问题。 你说得有道理。你明白为什么 OpenCV 模板化 cv::Mat 的迭代器和访问器方法,而不是模板化整个 cv::Mat 类本身吗?例如,当使用 STL 时,我只需要在声明时指定包含的类型名 (std::vector&lt;double&gt; vec)。使用 OpenCV,我不会在声明时声明包含的类型名,但每次使用访问器时都需要这样做。与STL相比,它不方便。是不是和老C版本库的兼容性有关系? 有一个模板继承自 cv::Mat - 它是 cv::Mat_ 并且有预制的 typedef,例如cv::Mat3b 用于 3 通道图像,每个通道一个字节。【参考方案3】:

它们是硬编码的,因为这样性能会更好。

在 OpenCV1.x 中有 cvGet2D() ,可以在这里使用,因为 Mat 可以转换为 IplImage。 但是它很慢,因为每次访问一个像素时,该函数都会找出类型、大小等。在循环中效率特别低。

【讨论】:

感谢您的建议,但我更愿意继续使用 C++ 接口,因为它更简洁(除了这个问题,我对此非常满意)。另外,我不明白为什么cv::Mat 不是模板类——这将避免必须模板化迭代器/访问器函数。这也是出于效率原因吗?

以上是关于访问 OpenCV Mat 元素时确定模板类型的主要内容,如果未能解决你的问题,请参考以下文章

数字图像处理OpenCV3 学习笔记

opencv怎么给mat赋值

OpenCV-无法访问 Mat 的元素(描述符)

如何用opencv提取一张图片的像素矩阵

未捕获的错误:模板解析错误:'mat-label' 不是已知元素:

通过未知类型的 Mat 进行 OpenCV 索引