在不知道其像素格式的情况下读取cv :: Mat像素

时间:2015-12-14 14:39:27

标签: opencv

我知道有几种方法可以读取和写入OpenCV cv::Mat图像/矩阵的像素值。 常见的是.at<typename T>(int, int)方法http://opencv.itseez.com/2.4/modules/core/doc/basic_structures.html#mat-at。 但是,这需要知道typename,例如.at<double>。 同样的事情适用于更直接的指针访问OpenCV get pixel channel value from Mat image

如何在不知道其类型的情况下读取像素值?例如,可以接收更通用的CvScalar值作为回报。效率不是问题,因为我想阅读相当小的矩阵。

2 个答案:

答案 0 :(得分:3)

有点儿。您可以构造cv::Mat_并为元素提供显式类型,之后您不必每次都编写元素类型。引用opencv2/core/mat.hpp

  

虽然在大多数情况下Mat足够,但如果使用大量元素,Mat_会更方便   访问操作,如果您在编译时知道矩阵类型。注意   Mat::at(int y,int x)Mat_::operator()(int y,int x)完全相同   并且以相同的速度运行,但后者肯定更短。

Mat_Mat非常相似。再次引用mat.hpp:

  

Mat_<_Tp>是Mat类之上的 thin 模板包装器。它没有任何   额外的数据字段。这类和Mat也没有任何虚拟方法。因此,引用或指向   这两个类可以自由但小心地相互转换。

您可以像这样使用

Mat_<Vec3b> dummy(3,3);
dummy(1, 2)[0] = 10;
dummy(1, 2)[1] = 20;
dummy(1, 2)[2] = 30;
cout << dummy(1, 2) << endl;

为什么我首先说'有点'?因为如果你想在某个地方传递这个Mat_ - 你必须指定它的类型。像这样:

void test(Mat_<Vec3b>& arr) {
    arr(1, 2)[0] = 10;
    arr(1, 2)[1] = 20;
    arr(1, 2)[2] = 30;

    cout << arr(1, 2) << endl;
}
...
Mat_<Vec3b> dummy(3,3);
test(dummy);

从技术上讲,你没有在像素读取期间指定你的类型,但实际上你仍然必须知道它并事先将Mat转换为适当的类型。

我想你可以使用一些低级别的黑客来找到解决方法(比如制作一个读取Mat类型的方法,计算元素大小和步幅,然后使用指针算法和转换来访问原始数据......)。但我不知道使用OpenCV功能的任何“干净”方式。

答案 1 :(得分:2)

如果您已经知道类型,则可以使用Mat_<>类型轻松访问。如果您不知道类型,可以:

  • 将数据转换为double,因此无论如何都不会截断数据
  • 切换通道数以正确访问双矩阵。请注意,您最多可以拥有4个频道,因为Scalar最多包含4个元素。

以下代码仅将源矩阵的选定元素转换为double值(使用N个通道)。

您获得的Scalar包含源矩阵中位置rowcol的值。

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

Scalar valueAt(const Mat& src, int row, int col)
{
    Mat dst;;
    src(Rect(col, row, 1, 1)).convertTo(dst, CV_64F);

    switch (dst.channels())
    {
    case 1: return dst.at<double>(0);
    case 2: return dst.at<Vec2d>(0);
    case 3: return dst.at<Vec3d>(0);
    case 4: return dst.at<Vec4d>(0);
    }

    return Scalar();
}

int main()
{
    Mat m(3, 3, CV_32FC3); // You can use any type here
    randu(m, Scalar(0, 0, 0, 0), Scalar(256, 256, 256, 256));

    Scalar val = valueAt(m, 1, 2);
    cout << val << endl;

    return 0;
}