OpenCV 3.4.1无拷贝数据赋值及矩阵拼接问题咨询
OpenCV 3.4.1中cv::Mat/cv::UMat无拷贝赋值问题详解
测试代码
std::cout << "test cv::Mat" << std::endl; cv::Mat a(2, 2, CV_32FC1, cv::Scalar(1.)); cv::Mat b(2, 2, CV_32FC1, cv::Scalar(5.)); std::cout << "a:" << std::endl << a << std::endl; std::cout << "b:" << std::endl << b << std::endl; a.col(0) = b.col(0); std::cout << "a:" << std::endl << a << std::endl; std::cout << "b:" << std::endl << b << std::endl; a.col(0) = b.row(0).t(); std::cout << "a:" << std::endl << a << std::endl; std::cout << "b:" << std::endl << b << std::endl; std::cout << "test cv::UMat" << std::endl; cv::UMat c(2, 2, CV_32FC1, cv::Scalar(1.)); cv::UMat d(2, 2, CV_32FC1, cv::Scalar(5.)); std::cout << "c:" << std::endl << c << std::endl; std::cout << "d:" << std::endl << d << std::endl; c.col(0) = d.col(0); std::cout << "c:" << std::endl << c << std::endl; std::cout << "d:" << std::endl << d << std::endl; c.col(0) = d.row(0).t(); std::cout << "c:" << std::endl << c << std::endl; std::cout << "d:" << std::endl << d << std::endl;
运行输出
test cv::Mat a: [1, 1; 1, 1] b: [5, 5; 5, 5] a: [1, 1; 1, 1] b: [5, 5; 5, 5] a: [5, 1; 5, 1] b: [5, 5; 5, 5] test cv::UMat [ INFO:0] Initialize OpenCL runtime... c: [1, 1; 1, 1] d: [5, 5; 5, 5] c: [1, 1; 1, 1] d: [5, 5; 5, 5] c: [1, 1; 1, 1] d: [5, 5; 5, 5]
疑问解答
1. 为什么a.col(0) = b.col(0);赋值不生效?
这是因为cv::Mat::col()返回的是一个临时的cv::Mat实例——它虽然和原矩阵a共享数据,但本质是独立对象。当你执行这个赋值语句时,只是把b.col(0)的浅拷贝赋值给了这个临时对象,而临时对象会在语句执行完立刻销毁,根本没机会修改原矩阵a的内存。
要真正修改a的列数据,得用copyTo()方法,它会直接把数据写入目标矩阵的内存区域:
b.col(0).copyTo(a.col(0));
2. 为什么a.col(0) = b.row(0).t();赋值能够生效?
核心区别在于b.row(0).t()返回的不是普通cv::Mat,而是**cv::MatExpr(矩阵表达式)**。OpenCV专门为MatExpr重载了赋值运算符:当你把表达式赋值给a.col(0)返回的临时Mat时,会先计算表达式结果,再直接写入临时Mat所引用的原矩阵a的内存区域——也就是a第一列的内存,所以最终a的数据被修改了。
3. cv::UMat场景下的赋值表现为何与cv::Mat不同?
cv::UMat是为异构计算(比如OpenCL加速)设计的,内存管理逻辑和cv::Mat完全不同:
- 首先,
UMat的数据可能存在GPU内存中,需要显式同步才能在CPU/GPU间传递; - 其次,OpenCV 3.4.1版本中
UMat对矩阵表达式、临时对象的支持远不如Mat成熟,无论是直接赋值临时UMat还是MatExpr,都无法正确触发数据写入; - 最后,
UMat的临时引用(比如col()返回的对象)赋值时,没有Mat那样的写入原内存的逻辑,导致所有赋值操作都无效。
要修改UMat的列数据,同样要用copyTo():
d.col(0).copyTo(c.col(0));
最终目标:无拷贝拼接多个矩阵是否可行?
先给明确结论:如果想要得到一个单一的cv::Mat/cv::UMat对象表示拼接结果,且完全不拷贝数据,这是不可能的。原因很简单:拼接后的矩阵需要将原矩阵数据组织成连续(或有规律步长)的内存块,但原矩阵内存是独立分散的,无法通过单一矩阵的内存布局覆盖。
不过你可以通过以下方式避免拷贝,达到类似效果:
- 用容器存储矩阵:用
std::vector<cv::Mat>或std::vector<cv::UMat>保存所有待拼接矩阵,处理时逐个访问,完全无拷贝; - 创建ROI引用集合:如果是固定格式拼接(比如固定行数的水平拼接),可为每个原矩阵的列/行创建ROI引用,统一管理这些引用而非合并成一个矩阵;
- 延迟拷贝:只有必须使用单一矩阵对象时才执行拷贝,尽量在之前步骤用分散矩阵处理数据。
如果数据量极大,优先选第一种方式,既避免拷贝开销,又能灵活处理子矩阵。
内容的提问来源于stack exchange,提问作者gondo
相关产品推荐
相关产品推荐

