OpenCV for C++:使用std::ref()传递子矩阵出现0行0列维度问题排查与解决
问题分析与解决方案:线程中OpenCV子矩阵访问异常
问题根源
你碰到的这个问题,核心是循环里的局部变量m2生命周期和线程执行节奏不匹配。
具体来说:
- 每次循环迭代时,你创建的
cv::Mat m2是原矩阵m1的子视图——这里要注意,cv::Mat的子矩阵只是引用原数据,但m2本身是栈上的局部对象。 - 你用
ref(m2)把引用传给线程后,线程可能还没来得及执行任务,当前循环迭代就结束了,m2作为局部变量会被立刻销毁。 - 等线程真正跑
myFunction的时候,它访问的已经是被销毁的m2的无效引用,这时候cv::Mat对象已经失效,自然会显示0行0列。
至于直接传完整m1没问题,是因为m1的生命周期覆盖了所有线程的执行时间,不会提前被销毁。
解决方案
要解决这个问题,关键是保证传递给线程的子矩阵对象在整个线程执行期间都有效,这里有两种常用的靠谱方案:
方案1:拷贝子矩阵,传递值而非引用
如果子矩阵的内存开销在可接受范围内,直接拷贝子矩阵,把值传给线程。这样线程会拿到独立的矩阵副本,完全不受原局部变量生命周期的影响:
for(int i = 0; i < numT; i++) { // 调用clone()创建子矩阵的深拷贝 cv::Mat m2 = m1(cv::Range(get<0>(offsets[i]), get<1>(offsets[i])), cv::Range::all()).clone(); cout << m2.rows << " " << m2.cols << endl; // 直接传值,不需要ref threadsVec.push_back( thread(myFunction, m2)); }
方案2:用智能指针管理子矩阵生命周期(无数据拷贝)
如果子矩阵很大,不想拷贝数据,可以用智能指针来托管子矩阵对象,确保线程执行期间对象不会被销毁:
for(int i = 0; i < numT; i++) { // 用shared_ptr管理子矩阵对象 auto m2 = make_shared<cv::Mat>(m1(cv::Range(get<0>(offsets[i]), get<1>(offsets[i])), cv::Range::all())); cout << m2->rows << " " << m2->cols << endl; // 传递智能指针,线程会持有引用直到任务完成 threadsVec.push_back( thread(myFunction, m2)); }
对应的myFunction需要改成接受shared_ptr<cv::Mat>类型的参数:
void myFunction(shared_ptr<cv::Mat> mat) { cout << mat->rows << " " << mat->cols << endl; // 你的业务逻辑操作... }
这种方式不会拷贝原矩阵的数据,只是传递了管理子矩阵对象的智能指针,既避免了生命周期问题,又节省了内存。
额外提醒
不管用哪种方案,都别忘了在所有线程启动后,调用join()等待所有线程执行完毕,不然主线程可能提前退出导致程序异常:
for(auto& t : threadsVec) { t.join(); }
另外,如果myFunction需要修改原矩阵的数据,方案1因为是拷贝,修改不会影响原矩阵;方案2的子矩阵是原矩阵的视图,修改会同步到原矩阵,你可以根据自己的需求选择合适的方案。
内容的提问来源于stack exchange,提问作者F.M.
相关产品推荐
相关产品推荐

