OpenCV&C++技术问题:返回的cv::Mat引发段错误(SIGSEGV)
你的程序触发SIGSEGV的核心原因是悬空指针访问,具体细节和解决办法如下:
问题根源
在GrabPair()函数里,pylonImgLeft和pylonImgRight是局部栈变量。当函数执行完毕返回时,这两个Pylon::CPylonImage对象会被销毁,它们内部管理的图像缓冲区也会被自动释放。
而你创建cv::Mat的代码只是直接指向了这个缓冲区的指针:
outImageL = cv::Mat(pResultLeft->GetHeight(), pResultLeft->GetWidth(), CV_8UC3, (uint8_t*)pylonImgLeft.GetBuffer() );
这里的cv::Mat并没有复制像素数据,只是共享了pylonImgLeft的内存空间。当函数返回后,cv::Mat指向的内存已经被释放,在main函数中调用imshow访问这块无效内存,自然会触发段错误。
至于为什么GrabPair()内部注释掉的imshow能正常工作?因为此时pylonImgLeft/Right还在栈上存在,缓冲区内存处于有效状态,所以可以正常显示。
解决方案
有两种可靠的解决方式,你可以根据需求选择:
1. 让cv::Mat复制像素数据(简单直接)
修改GrabPair()中创建cv::Mat的代码,使用clone()方法让cv::Mat独立分配内存并复制数据,不再依赖Pylon::CPylonImage的缓冲区:
if(pResultLeft->GrabSucceeded() && pResultRight->GrabSucceeded()){ fmtConv.Convert(pylonImgLeft, pResultLeft); // 使用clone()复制数据,让cv::Mat拥有独立内存 outImageL = cv::Mat(pResultLeft->GetHeight(), pResultLeft->GetWidth(), CV_8UC3, (uint8_t*)pylonImgLeft.GetBuffer()).clone(); fmtConv.Convert(pylonImgRight, pResultRight); outImageR = cv::Mat(pResultRight->GetHeight(), pResultRight->GetWidth(), CV_8UC3, (uint8_t*)pylonImgRight.GetBuffer()).clone(); }
这样返回的cv::Mat完全独立,函数返回后内存依然有效,main中的imshow就能正常工作。
2. 将Pylon::CPylonImage改为类成员变量(避免重复内存分配)
如果想避免每次抓取都复制数据的开销,可以把pylonImgLeft和pylonImgRight提升为BslrCap类的成员变量,让它们的生命周期和类对象一致:
// 在BslrCap类的头文件中添加成员变量 class BslrCap { private: Pylon::CPylonImage m_pylonImgLeft, m_pylonImgRight; // 其他成员... };
然后修改GrabPair()函数,去掉局部的pylonImgLeft/Right声明,直接使用类成员:
std::pair<cv::Mat, cv::Mat> GrabPair() { // ... 其他代码 Pylon::CGrabResultPtr pResultLeft, pResultRight; if(cams.IsGrabbing()){ cams[leftCamIndex].RetrieveResult(1000, pResultLeft, Pylon::TimeoutHandling_ThrowException); cams[rightCamIndex].RetrieveResult(1000, pResultRight, Pylon::TimeoutHandling_ThrowException); if(pResultLeft->GrabSucceeded() && pResultRight->GrabSucceeded()){ fmtConv.Convert(m_pylonImgLeft, pResultLeft); outImageL = cv::Mat(pResultLeft->GetHeight(), pResultLeft->GetWidth(), CV_8UC3, (uint8_t*)m_pylonImgLeft.GetBuffer() ); fmtConv.Convert(m_pylonImgRight, pResultRight); outImageR = cv::Mat(pResultRight->GetHeight(), pResultRight->GetWidth(), CV_8UC3, (uint8_t*)m_pylonImgRight.GetBuffer() ); } } return std::make_pair(outImageL, outImageR); }
这样cv::Mat指向的缓冲区会随着BslrCap对象的存在而保持有效,不会出现悬空指针问题。
额外注意事项
- 建议在
GrabPair()函数开头确保outImageL和outImageR初始化为空矩阵,避免在抓取失败时返回未初始化的对象:cv::Mat outImageL, outImageR; // 默认构造即为空矩阵,也可以显式写cv::Mat() - 你的
main函数中已经判断了raw_left和raw_right是否为空,这部分逻辑是正确的,可以保留。
内容的提问来源于stack exchange,提问作者Mikhail Grechukhin

