【问题标题】:OpenCV Mat element types and their sizesOpenCV Mat 元素类型及其大小
【发布时间】:2013-03-06 10:49:57
【问题描述】:

我对 OpenCV Mat 元素类型感到困惑。这是来自文档:

There is a limited fixed set of primitive data types the library can operate on.
That is, array elements should have one of the following types:

8-bit unsigned integer (uchar) 
8-bit signed integer (schar)
16-bit unsigned integer (ushort)
16-bit signed integer (short)
32-bit signed integer (int)
32-bit floating-point number (float)
64-bit floating-point number (double)
...

For these basic types, the following enumeration is applied:
enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };

众所周知,C++ 标准没有以字节为单位定义基本类型的大小,那么它们如何使用这样的假设呢?我应该期待什么类型,比如说 CV_32S,它是 int32_t 还是 int?

【问题讨论】:

  • openCV 中的类型很容易理解。 CV_numbitsType。所以 CV_16S 是一个 16 位长的有符号整数。
  • 那么为什么他们在文档中使用这样的名称(int、float 等),而(例如)int 可以是 64 位而不是 32 位?
  • 我不太了解底层结构,但是当您使用 CV_32S 类型创建垫子时:Mat myMat(10,10,CV_32S); 您可以使用myMat.at<int>(1,1) = 1; 访问它
  • 顺便说一句,您发布的表格似乎是正确的,我的意思是对于左侧的每种类型,您使用括号中的 C++ 类型来访问垫子,如上面的评论所示。
  • @Ruchir 你应该可以定义一个cv::Mat_<long>。不过,我怀疑您是否能够对其应用任何 OpenCV 功能。

标签: opencv


【解决方案1】:

Miki's answer开发,
在 OpenCV 3 中,定义已移至 modules/core/include/opencv2/core/traits.hpp,您可以在其中找到:

/** @brief A helper class for cv::DataType

The class is specialized for each fundamental numerical data type supported by OpenCV. It provides
DataDepth<T>::value constant.
*/
template<typename _Tp> class DataDepth
{
public:
    enum
    {
        value = DataType<_Tp>::depth,
        fmt   = DataType<_Tp>::fmt
    };
};



template<int _depth> class TypeDepth
{
    enum { depth = CV_USRTYPE1 };
    typedef void value_type;
};

template<> class TypeDepth<CV_8U>
{
    enum { depth = CV_8U };
    typedef uchar value_type;
};

template<> class TypeDepth<CV_8S>
{
    enum { depth = CV_8S };
    typedef schar value_type;
};

template<> class TypeDepth<CV_16U>
{
    enum { depth = CV_16U };
    typedef ushort value_type;
};

template<> class TypeDepth<CV_16S>
{
    enum { depth = CV_16S };
    typedef short value_type;
};

template<> class TypeDepth<CV_32S>
{
    enum { depth = CV_32S };
    typedef int value_type;
};

template<> class TypeDepth<CV_32F>
{
    enum { depth = CV_32F };
    typedef float value_type;
};

template<> class TypeDepth<CV_64F>
{
    enum { depth = CV_64F };
    typedef double value_type;
};

大多数情况/编译器中,使用 C++ 精确数据类型应该没问题。单字节数据类型(CV_8U -> uint8_tCV_8U -> int8_t)作为 unambiguously defined in C++ 不会有问题。 float (32bit) and double (64bit) 也是如此。但是,对于其他数据类型确实要完全确定您使用正确的数据类型(例如,在使用 at&lt;&gt; 方法时),您应该使用例如:

typedef TypeDepth<CV_WHATEVER_YOU_USED_TO_CREATE_YOUR_MAT>::value_type access_type;
myMat.at<access_type>(y,x) = 0;

顺便说一句,我很惊讶他们决定采用这种模棱两可的方法,而不是简单地使用确切的数据类型。

因此,关于您的最后一个问题:

我应该期待什么类型,比如说CV_32S

我相信 OpenCV 3 中最准确的答案是:

TypeDepth<CV_32S>::value_type

【讨论】:

  • 很好的答案,谢谢.. OpenCV 在大多数情况下不遵循 C++ 诽谤。虽然它是一个很棒的计算机视觉库,但我讨厌用 C++ 处理它。它总是会破坏我的代码,让我编写让我感到羞耻的代码。
  • 如何访问 value_type?您的示例在 OpenCV 3.0 中对我不起作用,因为它是成员而不是公开的。
  • @John:是的,只需将“class”替换为“struct”,就可以了。我的 OpenCV 2.4.5 版本没有 hader,我不得不手动将代码添加到我的源代码中。请注意 Mat::elemSize1() 方法,它还显示存储在 Mat 中的一个数字的大小(以字节为单位)。
  • @karsten 您不会修改 OpenCV 标头,是吗?不要那样做。
  • 如果我们从数据中得到CV_WHATEVER_YOU_USED_TO_CREATE_YOUR_MAT呢?如果不是常数值?
【解决方案2】:

core.hpp 你可以找到以下内容:

/*!
  A helper class for cv::DataType

  The class is specialized for each fundamental numerical data type supported by OpenCV.
  It provides DataDepth<T>::value constant.
*/
template<typename _Tp> class DataDepth {};

template<> class DataDepth<bool> { public: enum { value = CV_8U, fmt=(int)'u' }; };
template<> class DataDepth<uchar> { public: enum { value = CV_8U, fmt=(int)'u' }; };
template<> class DataDepth<schar> { public: enum { value = CV_8S, fmt=(int)'c' }; };
template<> class DataDepth<char> { public: enum { value = CV_8S, fmt=(int)'c' }; };
template<> class DataDepth<ushort> { public: enum { value = CV_16U, fmt=(int)'w' }; };
template<> class DataDepth<short> { public: enum { value = CV_16S, fmt=(int)'s' }; };
template<> class DataDepth<int> { public: enum { value = CV_32S, fmt=(int)'i' }; };
// this is temporary solution to support 32-bit unsigned integers
template<> class DataDepth<unsigned> { public: enum { value = CV_32S, fmt=(int)'i' }; };
template<> class DataDepth<float> { public: enum { value = CV_32F, fmt=(int)'f' }; };
template<> class DataDepth<double> { public: enum { value = CV_64F, fmt=(int)'d' }; };
template<typename _Tp> class DataDepth<_Tp*> { public: enum { value = CV_USRTYPE1, fmt=(int)'r' }; };

您可以看到CV_32Sint 类型的值,而不是int32_t

【讨论】:

    【解决方案3】:

    虽然 C++ 没有定义元素的大小,但问题是假设性的:对于运行 OpenCV 的系统,大小是已知的。给定

    cv::Mat m(32,32,CV_32SC1, cv:Scalar(0));
    std::cout << "size of the element in bytes: " << m.depth() << std::endl;
    std::cout << "or " << m.step.p[ m.dims-1 ]/m.channels() << std::endl;
    

    那你怎么确定是int

    尝试调用

    int pxVal = m.at<int>(0,0);
    

    CV_DbgAssert( elemSize()==sizeof(int) );
    

    通过cv::Mat::flags 定义左手——在本例中为CV_32SC1 的预定义深度等于

    CV_DbgAssert( m.depth() == sizeof(int) )
    

    CV_DbgAssert( 4 == sizeof(int) )
    

    所以如果你成功了,你就只剩下字节序了。并且在生成 cvconfig.h 时(通过 CMake)进行了检查。

    TL;DR,期待标题中给出的类型,你会没事的。

    【讨论】:

    • m.depth() 不是以字节为单位,而是提供类型的 EV 枚举值,例如CV_8U。
    • 使用 elemSize1() 代替。请注意,elemSize() == elemSize1() * channels()
    【解决方案4】:

    您可以在 opencv 的来源中找到有关您的问题的所有定义。

    参见https://github.com/Itseez/opencv/blob/master/modules/core/include/opencv2/core/cvdef.h 文件。

    【讨论】:

      【解决方案5】:

      我在 OpenCV 的代码中发现了几个与 CV_8UC1、CV_32SC1 等相关的#define。为了使枚举工作,OpenCV 将附加代码作为参数将普通数字转换在一起(即 CV_8UC1、CV_16UC2...都是由它们各自的数字表示),并在 CvMat 的定义中将深度和通道分开(我猜 Mat 在其定义中可能有类似的代码)。然后,它使用 create() 为矩阵分配空间。由于 create() 是内联的,我只能猜测它类似于 malloc() 什么的。
      由于源代码从 2.4.9 到 3.0.0 发生了很大变化,我需要稍后发布更多证据。请给我一点时间来了解更多信息并编辑我的答案。

      【讨论】:

        【解决方案6】:

        简而言之,您提供的表格是正确的。 如果要直接访问像素,请将其类型转换为右侧的说明符,例如 CV_32S 是有符号的 32 位。 S 始终表示有符号整数(signed char、signed short、signed int) F 始终表示浮点数(float、double) U 始终表示无符号整数。

        仅在创建或转换 Mat 时使用枚举。这是一种告诉垫子是所需类型的方法,据我了解,它是不使用模板时的 C 前身。

        我只使用C功能,为了创建图像,传递以下内容会出错:

        cvCreateImage(mySize,char, nChannels);
        

        相反,我传递了以下内容:

        cvCreateImage(mySize, IPL_DEPTH_8U, nChannels);
        

        这里,IPL_DEPTH_8U 是函数使用的标志。该函数本身有一个检查标志的开关类型语句。标志的实际值通常是无意义的,因为它通常由条件语句而非代数语句控制。

        【讨论】:

        • 这无关紧要。问题在opencv2上,答案与opencv1有关
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-05
        • 2016-09-25
        • 1970-01-01
        • 2017-03-06
        • 1970-01-01
        相关资源
        最近更新 更多