基于Qt框架在做双摄像头的立体视觉重建,程序在VS 2013的Win32 Console运行没有问题,我最近在移植到Qt中就有问题。先上一下相关部分的程序:
相关变量在MainWindow的私有员中定义
1 //初始化标定变量 2 cv::Mat cameraMatLeft, cameraMatRight, distCoeffLeft, \ 3 distCoeffRight, R, T, R1, P1, R2, P2; 4 vector<cv::Mat> rvecs, tvecs; double rmsLeft, rmsRight, rmsStereo; 5 cv::Size boardSize, imageSize; vector<cv::Point3f> corners; 6 vector<vector<cv::Point2f>> imagePointsLeft, imagePointsRight; 7 vector<vector<cv::Point3f>> objectPoints;
然后是出问题的标定函数函数体部分:
1 void MainWindow::calibrate(cv::Mat& cameraMat, cv::Mat& distCoeff, \ 2 vector<vector<cv::Point2f>>& imagePoints, 3 const int nImages, const QStringList fileList, 4 double& rms) 5 { 6 cv::Mat view, viewGray; 7 vector<cv::Point2f> pointBuf; 8 for(int i = 0; i < nImages; i++) 9 { 10 view = cv::imread(fileList.at(i).toStdString(), 1); 11 cv::cvtColor(view, viewGray, cv::COLOR_BGR2GRAY); 12 bool found = cv::findChessboardCorners(view, boardSize, pointBuf, \ 13 CV_CALIB_CB_ADAPTIVE_THRESH | \ 14 CV_CALIB_CB_FAST_CHECK | \ 15 CV_CALIB_CB_NORMALIZE_IMAGE); 16 if(found) 17 { 18 cv::cornerSubPix(viewGray, pointBuf, cv::Size(11, 11), cv::Size(-1, -1), \ 19 cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); 20 imagePoints.push_back(pointBuf); 21 } 22 } 23 24 objectPoints.resize(imagePoints.size(), corners); 25 rms = cv::calibrateCamera(objectPoints, imagePoints, imageSize, cameraMat, \ 26 distCoeff, rvecs, tvecs); 27 }
标定函数的调用过程:
1 corners.resize(1); 2 for(int i = 0; i < w; i++) 3 { 4 for(int j = 0; j < h; j++) 5 { 6 corners.push_back(cv::Point3f(j*squareSize, i*squareSize, 0)); 7 } 8 } 9 10 //标定 11 calibrate(cameraMatLeft, distCoeffLeft, imagePointsLeft, nLeftImages, \ 12 leftFilelist, rmsLeft);
我调试发现是执行到这一步出了问题:
imagePoints.push_back(pointBuf);
然后执行到这一步显示这样的信息
然后程序跳到这里vector文件的这里
__declspec(noreturn) void _Xlen() const { // report a length_error _Xlength_error("vector<T> too long"); }
OS : win 10 64bit
Qt Vision: 5.6 + msvc 2012 32-bit
超过 vector 所能容纳元素的最大数目了。
循环的一开始就这样报错了,那个时候,vector为空啊。并且我这段程序在Win32 console中编译却没有问题。
@nano_zombie.uestc: 你看下 imagePoints 的 size() 和 max_size()
@Launcher: imagePoints.size()=0; imagePoints.max_size()=268435455;
@nano_zombie.uestc: 能多提供一点信息么?
异常从 _Xlen() 函数抛出,你看下调用堆栈,调用 _Xlen() 的函数是哪个?
@Launcher:
调用堆栈如下,我自己分析不出来错误的原因
@nano_zombie.uestc: 你双击 std::vector<cv:Point<float>>::_Buy 行,然后看看里面的语句中
else if (max_size() < _Capacity) 这一条语句的 max_size() 和 _Capacity 的值。
你这个错误是出现在 imagePoints.push_back(pointBuf); 语句中 pointBuf 的拷贝构造中.
@Launcher: _Capacity = 4289879494;
pointBuf定义是这样的 vector<cv::Point2f> pointBuf; Point2f是OpenCV中的类。我并没有手动写任何构造和析构函数。
@nano_zombie.uestc: 这个打不出表情符号真是郁闷。我还得给你讲 C++ 的基础知识。
imagePoints.push_back(pointBuf); 调用的是 void push_back(const value_type& _Val) 函数,根据语义,需要对 pointBuf 进行拷贝构造,而你的 pointBuf 是 vector<cv::Point2f> 类型的,那么它会先执行 vector(const _Myt& _Right),然后把你的 pointBuf 的所有数据拷贝到新创建的 vector<cv::Point2f> 对象中。所以我说的不是你的 Point2f 的拷贝构造,而是 vector<cv::Point2f> 的拷贝构造。
而且我们通常都不这么写,在 C++ 11 之前,我们先在 imagePoints 中增加一个 vector<cv::Point2f> 元素,然后引用该元素:
vector<cv::Point2f>& pointBuf = imagePoints[size() -1];
然后再调用 cv::cornerSubPix(...,pointBuf),这样减少一次对 pointBuf 的拷贝构造。
假设 pointBuf 占用 20M 空间,按照你原来的写法,你需要 40 M 空间完成此操作,而改用我提供给你的方法后,你只需要 20 M。
如果能使用 C++ 11 的话,可以利用右值语义优雅解决,其它代码逻辑都不用动,只是修改 push_back 语句为:
imagePoints.push_back(std::move(pointBuf));
@nano_zombie.uestc: _Capacity 的值是这么计算的:_Capacity = max_size() - _Capacity / 2 < _Capacity
? 0 : _Capacity + _Capacity / 2;
所以,通常 _Capacity 都会比 size() 大,也有可能比 max_size() 大,max_size() 的值是这么计算的:
((size_t)(-1) / sizeof (_Ty))。
由于你对一个元素数目(size())较大的 vector<_Ty> 执行拷贝构造,所以造成 max_size() < _Capacity,因此引发 _Xlen() 异常。
@Launcher: 嗯,由于并没有系统学过C++,我先消化下先,不过修改语句 magePoints.push_back(std::move(pointBuf)); 的确解决了问题。
多谢。
@Launcher: 但是为什么我在Win32 Console中运行这段程序没有问题呢??这段程序我是参考opencv/samples中的示例程序写的。
@nano_zombie.uestc: 有没有问题,你的看 max_size() 和 _Capacity 这两个值是多少。
你看一下imread执行时候view是否为空
不为空,jpg文件读取正常。
@nano_zombie.uestc: 还是加一个imshow()看一下吧,在Win32 Console运行没问题,那vector这部分肯定没问题的,都是标准库的。
@MissingAnObject: 我看了的,view和viewGray正常显示