Давайте используем небольшое случайное изображение для демонстрации:
// Generate random input image
cv::Mat image(5, 5, CV_8UC3);
cv::randu(image, 0, 256);
Опция 1
Поскольку входное значение равно CV_8UC3
(то есть каждый элемент является cv::Vec3b
), и мы хотим, чтобыэлементы как cv::Vec3f
, нам сначала нужно использовать convertTo
, чтобы преобразовать Mat
в CV_32FC3
.Мы сохраняем результат во временной матрице, и для удобства (поскольку мы знаем тип элемента) мы можем явно использовать cv::Mat3f
.
// First convert to 32bit floats
cv::Mat3f temp;
image.convertTo(temp, CV_32FC3);
Теперь мы можем просто использовать Mat
итераторы для инициализации вектора.
// Use Mat iterators to construct the vector.
std::vector<cv::Vec3f> v1(temp.begin(), temp.end());
Опция 2
Предыдущая опция заканчивается выделением временного массива.Приложив немного творческих усилий, мы можем избежать этого.
Как оказалось, можно создать заголовок cv:Mat
, обертывающий вектор , совместно использующий хранилище данных.
Мы начнем с создания вектора соответствующего размера:
std::vector<cv::Vec3f> v2(image.total());
Mat
, созданный из такого вектора, будет иметь 1 столбец и столько строк, сколько имеется элементов.Поэтому мы reshape
нашей входной матрицы с идентичной формой, а затем будем использовать convertTo
для прямой записи в вектор.
image.reshape(3, static_cast<int>(image.total())).convertTo(v2, CV_32FC3);
Вся программа:
#include <opencv2/opencv.hpp>
#include <vector>
template<typename T>
void dump(std::string const& label, T const& data)
{
std::cout << label << ":\n";
for (auto const& v : data) {
std::cout << v << " ";
}
std::cout << "\n";
}
int main()
{
// Generate random input image
cv::Mat image(5, 5, CV_8UC3);
cv::randu(image, 0, 256);
// Option 1
// ========
// First convert to 32bit floats
cv::Mat3f temp;
image.convertTo(temp, CV_32FC3);
// Use Mat iterators to construct the vector.
std::vector<cv::Vec3f> v1(temp.begin(), temp.end());
// Option 2
// ========
std::vector<cv::Vec3f> v2(image.total());
image.reshape(3, static_cast<int>(image.total())).convertTo(v2, CV_32FC3);
// Output
// ======
dump("Input", cv::Mat3b(image));
dump("Vector 1", v1);
dump("Vector 2", v2);
return 0;
}
Пример вывода:
Input:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Vector 1:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Vector 2:
[246, 156, 192] [7, 165, 166] [2, 179, 231] [212, 171, 230] [93, 138, 123] [80, 105, 242] [231, 239, 174] [174, 176, 191] [134, 54, 234] [69, 25, 147] [24, 67, 124] [158, 203, 206] [89, 144, 210] [51, 31, 132] [123, 250, 234] [246, 204, 74] [111, 208, 249] [149, 234, 37] [55, 147, 143] [29, 214, 169] [215, 84, 190] [204, 110, 239] [216, 103, 137] [248, 173, 53] [221, 251, 29]
Проблемы с вашим кодом
In src->assign(in->datastart, in->dataend);
Элементы src
равны Vec3f
, однако datastart
и dataend
являются указателями на uchar
.
Это будет иметь несколько последствий.Прежде всего, поскольку in
равно CV_8UC3
, элементов будет в 3 раза больше.Кроме того, каждый из Vec3f
экземпляров будет иметь только первый установленный набор, остальные 2 будут 0.
In src->insert(src->end(), in->ptr<Vec3f>(i), in->ptr<Vec3f>(i)+cols);
Напомним, что у вас естьsrc
уже инициализирован как vector<Vec3f>(rows * cols);
- т.е. вектор уже содержит столько элементов, сколько пикселей в исходном изображении.Тем не менее, в цикле вы продолжаете добавляя дополнительных элементов в конце.Это означает, что результирующий вектор будет иметь в два раза больше элементов, причем первая половина из них будет равна нулю.
Кроме того, in
равно CV_8UC3
, но вы интерпретируете данные как cv::Vec3f
.Это означает, что вы берете байтовые значения 4 последовательных пикселей и интерпретируете это как последовательность из 3 32-битных чисел с плавающей запятой.Результат не может быть ничем иным, как мусором.
Это также означает, что вы в конечном итоге получите доступ к данным за пределами допустимой области, потенциально после конца буфера.
В cv::Mat(rows, cols, CV_8U, src, cv::Mat::AUTO_STEP)
...
Прежде всего, src
содержит Vec3f
элементы, но вы создаете Mat
как CV_8U
(что также является проблемой, так как вам нужноздесь также указывается количество каналов, поэтому оно фактически интерпретируется как CV_8UC1
).Таким образом, вы будете иметь не только неправильное количество каналов, но и мусор из-за несоответствия типов.
Еще большая проблема заключается в том, что вы передаете src
в качестве 4-го параметра.Теперь это указатель на экземпляр std::vector
, а не на реальные данные, которые он содержит.(Компилируется, так как 4-й параметр - void*
).Это означает, что вы на самом деле интерпретируете метаданные vector
вместе со множеством других неизвестных данных.Результат - в лучшем случае мусор (или, как вы узнали, SEGFAULTs или потенциально неприятные ошибки безопасности).
Назад к Mat
Обратите внимание, что этовозможно imshow
с плавающей точкой Mat
, при условии, что значения нормализованы в диапазоне [0,1].
Мы можем воспользоваться конструктором Mat
, который принимает vector
, ипросто измените результирующую матрицу обратно на исходную форму.
cv::Mat result(cv::Mat(v2).reshape(3, image.rows));
Обратите внимание, что в этом случае базовое хранилище данных используется совместно с источником vector
, поэтому вам необходимо убедиться, что оно остается в области действия до тех пор, покаMat
делает.Если вы не хотите делиться данными, просто передайте true
в качестве второго параметра конструктору.
cv::Mat result(cv::Mat(v2, true).reshape(3, image.rows));
Конечно, если вы хотите вернуться к CV_8UC3
, это так же просто, какдобавив convertTo
.В этом случае нет необходимости копировать векторные данные, так как тип данных изменяется, и новый массив хранения будет выделяться автоматически.
cv::Mat result;
cv::Mat(v2).reshape(3, image.rows).convertTo(result, CV_8UC3);