可以分两行完成:)
垫到数组
uchar * arr = image.isContinuous()? image.data: image.clone().data;
uint length = image.total()*image.channels();
垫到矢量
cv::Mat flat = image.reshape(1, image.total()*image.channels());
std::vector<uchar> vec = image.isContinuous()? flat : flat.clone();
两者都适用于any一般cv::Mat。
通过工作示例进行说明
cv::Mat image;
image = cv::imread(argv[1], cv::IMREAD_UNCHANGED); // Read the file
cv::namedWindow("cvmat", cv::WINDOW_AUTOSIZE );// Create a window for display.
cv::imshow("cvmat", image ); // Show our image inside it.
// flatten the mat.
uint totalElements = image.total()*image.channels(); // Note: image.total() == rows*cols.
cv::Mat flat = image.reshape(1, totalElements); // 1xN mat of 1 channel, O(1) operation
if(!image.isContinuous()) {
flat = flat.clone(); // O(N),
}
// flat.data is your array pointer
auto * ptr = flat.data; // usually, its uchar*
// You have your array, its length is flat.total() [rows=1, cols=totalElements]
// Converting to vector
std::vector<uchar> vec(flat.data, flat.data + flat.total());
// Testing by reconstruction of cvMat
cv::Mat restored = cv::Mat(image.rows, image.cols, image.type(), ptr); // OR vec.data() instead of ptr
cv::namedWindow("reconstructed", cv::WINDOW_AUTOSIZE);
cv::imshow("reconstructed", restored);
cv::waitKey(0);
扩展说明:
Mat 存储为连续的内存块,如果使用其构造函数之一创建或使用clone() 或类似方法复制到另一个Mat 时。要转换为数组或vector,我们需要它的第一个块的地址和数组/向量长度。
指向内部内存块的指针
Mat::data 是一个指向其内存的公共 uchar 指针。
但是这个内存可能不是连续的。如其他答案中所述,我们可以检查mat.data 是否指向连续内存或不使用mat.isContinous()。除非您需要极高的效率,否则您可以在 O(N) 时间内使用 mat.clone() 获得垫子的连续版本。 (N = 来自所有通道的元素数量)。但是,在处理cv::imread() 读取的图像时,我们很少会遇到不连续的垫子。
数组/向量的长度
问:应该是row*cols*channels吧?
答:并非总是如此。可以是rows*cols*x*y*channels。
问:应该等于 mat.total() 吗?
A:对于单通道垫是正确的。但不适用于多通道垫
由于 OpenCV 文档不佳,数组/向量的长度有点棘手。我们有 Mat::size 公共成员,它只存储单个 Mat 没有通道的尺寸。对于 RGB 图像,Mat.size = [rows, cols] 而不是 [rows, cols, channels]。 Mat.total() 返回垫子的单个通道中的总元素,等于 mat.size 中值的乘积。对于 RGB 图像,total() = rows*cols。因此,对于任何通用 Mat,连续内存块的长度将为 mat.total()*mat.channels()。
从数组/向量重构 Mat
除了数组/向量,我们还需要原始 Mat 的 mat.size [array like] 和 mat.type() [int]。然后使用获取数据指针的构造函数之一,我们可以获得原始 Mat。可选的 step 参数不是必需的,因为我们的数据指针指向连续内存。我使用这种方法在 nodejs 和 C++ 之间将 Mat 作为 Uint8Array 传递。这避免了使用 node-addon-api 为 cv::Mat 编写 C++ 绑定。
参考资料: