У меня есть изображение cv :: Mat of doubles, которое я обрезал между 0,0 и 4095,0. Я хочу иметь возможность преобразовать эту матрицу / создать новую матрицу на основе этой, которая является 12-битной. (наименьший размер int необходим для хранения 0 -> 4095 целочисленных значений). Я могу просто извлечь необработанный буфер, однако я не уверен, что формат данных внутри матрицы.
Вручную я мог бы сделать следующее:
cv::Mat new_matrix(/*type CV_8UC3, size (matrix.rows, matrix.cols/2)*/);
for(int i = 0; i < matrix.rows; ++i){
for(int j = 0; j < matrix.cols; ++j){
std::uint16_t upper_half = static_cast<std::uint16_t>(matrix.at<double>(j*2,i));
std::uint16_t lower_half = static_cast<std::uint16_t>(matrix.at<double>(j*2+1,i));
std::uint8_t first_byte = static_cast<std::uint8_t>(upper_half>>4);
std::uint8_t second_byte = static_cast<std::uint8_t>(upper_half<<4) | static_cast<std::uint8_t>(lower_half << 12 >> 12);
std::uint8_t third_byte = static_cast<std::uint8_t>(lower_half>>4);
new_matrix.at<cv::Vec3B>(j, i) = cv::Vec3b(first_byte, second_byte, third_byte);
}
}
который по существу сжимает два двойных значения в одно для верхней половины, одно для нижнего, выделяя из него три байта (12 + 12 = 24, 24/8 = 3) в 3-байтовую матрицу. Однако я не уверен, что макет памяти будет соответствовать размеру упакованных 12 битов (у меня есть четное число столбцов, поэтому деление столбцов / 2 не проблема), и я не уверен, как убедиться, что это соответствует порядку байтов ,
Я мог бы даже иметь возможность использовать пользовательский тип данных, но мне нужно убедиться, что элементы не дополняются, если я скажу, что я создал 12-битный тип Union Struct или что-то в этом роде.
Обратите внимание, что после преобразования я больше не собираюсь использовать 12-битные значения в OpenCV, мне нужно извлечь необработанные значения, и они будут отправлены в другой отдельный процесс.
cv :: Mat будет хранить данные в 8-битных единицах, минимум. Это означает, что ваши 12-битные значения будут заполнены в любом случае внутри матрицы, о чем свидетельствует возвращаемое значение Mat :: elemSize1 (), которое находится в # байтов. Для того, что вам нужно сделать, лучше всего использовать пользовательскую структуру, содержащую 2 значения (размеры структуры также заполнены байтами), а затем упаковать все в std :: vector<>. Затем вы потратите не более 12 бит заполнения на потоковые данные, когда у вас будет нечетное количество выборок.
Примечание об упаковке: Если вы используете что-то вроде следующего, вам нужно изменить порядок элементов битового размера, в зависимости от машины, если вам нужно перенести байты из одной архитектуры в другую.
#pragma pack(push, 1)
struct PackedSamples {
char lowA;
char highA : 4; // NOTE: the declaration of bit sized fields order is inverse when
char lowB : 4; // going from BIG_ENDIAN to SMALL_ENDIAN and vice-versa
char highB;
};
#pragma pack(pop)
Вот макросы, которые я использую для проверки порядка, я предполагаю, что Windows работает на x86 / x64. AMD __BIG_ENDIAN.
#ifdef WIN32
# ifndef __BYTE_ORDER
# define __LITTLE_ENDIAN 1234
# define __BIG_ENDIAN 4321
# define __BYTE_ORDER __LITTLE_ENDIAN
# endif
#else
# include <endian.h>
#endif
Таким образом, объявление выше станет:
#pragma pack(push, 1)
struct PackedSamples {
char lowA;
#if __BYTE_ORDER == __LITTLE_ENDIAN
char highA : 4;
char lowB : 4;
#else
char lowB : 4;
char highA : 4;
#endif
char highB;
};
#pragma pack(pop)
Других решений пока нет …