Типы элементов элементов OpenCV Mat и их размеры

Меня путают типы элементов 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 };

Известно, что стандарт С++ не определяет размер базовых типов в байтах, так как они используют такие предположения? И какой тип я должен ожидать, скажем, CV_32S, это int32_t или int?

Ответ 1

Разработка из Miki answer,
В OpenCV 3 определение переместилось в модули /core/include/opencv 2/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;
};

В большинстве случаев/компиляторов вы должны быть в порядке, используя точные типы данных С++. У вас не возникнет проблем с однобайтовыми типами данных (CV_8Uuint8_t и CV_8Uint8_t) как однозначно определенным в С++. То же самое для float (32bit) и double (64bit). Тем не менее, верно, что для других типов данных необходимо полностью убедиться, что вы используете правильный тип данных (например, при использовании метода at<>), вы должны использовать, например:

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

Ответ 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_32S - это значение для типа int, а не int32_t.

Ответ 3

В то время как С++ не определяет размер элемента, вопрос гипотетический: для систем 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, ожидайте типы, указанные в заголовке, и все будет в порядке.

Ответ 5

Я нашел несколько #define в коде OpenCV, связанном с CV_8UC1, CV_32SC1 и т.д. Чтобы заставить перечисления работать, OpenCV помещает дополнительные коды для преобразования простых чисел вместе в качестве параметра (то есть CV_8UC1, CV_16UC2... все представленные их соответствующими номерами), и разбить глубину и каналы в определении CvMat (я думаю, что Mat может иметь аналогичные коды в своем определении). Затем он использует create() для выделения пробелов для матрицы. Поскольку create() является встроенным, я могу только предположить, что он похож на malloc() или что-то в этом роде.
Поскольку исходные коды сильно меняются с 2,4,9 до 3,0,0, мне нужно опубликовать больше доказательств позже. Пожалуйста, дайте мне немного времени, чтобы узнать больше и отредактировать мой ответ.

Ответ 6

Короче приведенная вами таблица верна. Если вы хотите получить прямой доступ к пикселю, вы приведёте его к указателю справа, например CV_32S - это подписанный 32-разрядный. S всегда означает подписанное целое число (подписанный char, подписанный короткий, подписанный int) F всегда означает число с плавающей запятой (float, double) U всегда означает целое число без знака.

Перечисление используется только при создании или преобразовании Mat. Это способ сказать mat, который является желаемым типом, поскольку я понимаю, что это предшественник C, когда шаблоны не использовались.

Я использую исключительно функциональность C, и для создания изображения было бы ошибкой передать следующее:

cvCreateImage(mySize,char, nChannels);

Вместо этого передаю следующее:

cvCreateImage(mySize, IPL_DEPTH_8U, nChannels);

Здесь IPL_DEPTH_8U является флагом, который используется функцией. Сама функция имеет оператор switch-type, который проверяет флаг. Фактическое значение флага чаще всего бессмысленно, поскольку его чаще всего контролируют условные, а не алгебраические утверждения.