You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 01:34:07