线结构光三维重建可以得到法兰盘的三维点云图,进一步的,我们可以对螺纹处的点云数据进行分析,本节主要记录线结构光三维重建的主要步骤及一些问题。
1.标定板位置不变,拍摄有激光和无激光两张照片。移动标定板位置,重复拍摄多组。
2.依据标定板无激光图片标定相机内参
3.建立文件夹laser,remaplasr,lasermidline,lasertest
(警示)此处使用了(10,8)标定板,需要更改实验室程序参数
cControl.FindChessPointsDIRAuto(10, 8, 10, imgPath, IS_SMALL_SCREEN, IS_CALIB_SMALL); //Small calib board
此处为(10,8),此前因为(10,8)程序不通,便认为应是(8,10),导致标定结果有错,致使后续重建失败。
不通的原因是实验室同一程序不同个人作过修改
//reSortImageCorners函数中
//对变换后的标定点进行排序
std::vector<Point2d> idx_list;
imgPoints.resize(rowNum*colNum);
std::vector<cv::Point2f>::const_iterator cit = imageCorners.begin();
for (std::vector<cv::Point2f>::const_iterator it = warp_list.begin(); it != warp_list.end(); ++it)
{
cv::Point2f pos = *it;
cv::Point2d idx;
idx.y = int(roundf((pos.y - 2*delta) / delta));
idx.x = int(roundf((pos.x - 0) / delta));
imgPoints[idx.x + idx.y*rowNum] = *cit;
//上应为rowNum,而手中拿到代码此处为colNum*****
++cit;
}
3.依据相机内参、外参和上述拍摄图片得到线结构光光平面方程
String calibFilePath = "D:\\Graduate\\Data\\1119\\cam\\CalibrationResults";//相机标定文件夹
String laserImgPath = "D:\\Graduate\\Data\\1119\\laser\\";//线结构光路径文件夹
String remapLaserImgPath = "D:\\Graduate\\Data\\1119\\remaplaser\\";//校正线结构光路径文件夹
1.使用步进电机移动平移台固定距离(非必须1mm)
2.相机拍摄标定板图片
3.重复1、2
排查不能重建的原因时,曾发现calRT_Mat函数中的问题,将矩阵的定义放在了for循环中,导致后续结果不变化。需要注意的是该问题只会影响结果的准确性,不是重建失败的根本原因。
int pic_num =9;//设置偏移标定图像数量
//平移轴标定
String Camfilepath = "D:\\Graduate\\Data\\1119\\cam\\CalibrationResults\\";//相机参数路径
String imgfilepath = "D:\\Graduate\\Data\\1119\\move\\CalibrationResults\\";//平移/旋转图像角点路径
std::vector<cv::Mat> rotation_matrix, tvecsMat;
calRT_Mat(Camfilepath, imgfilepath, rotation_matrix, tvecsMat);
Vec3f line = calT_Axis(rotation_matrix, tvecsMat);
cout << "位移向量"<< line << endl;
void calRT_Mat(String Camfile,String imgfile,vector<Mat>& rotation_matrix,vector<Mat>& tvecsMat)
{
//相机矩阵和畸变系数应放在下述for循环之前,此前放置在for循环里,导致后续结果不变*****
Mat cameraMatrix = Mat(3, 3, CV_64FC1, Scalar::all(0));
Mat distCoeffs = Mat(5, 1, CV_64FC1, Scalar::all(0));
Mat rot_matrix = Mat(3, 3, CV_64FC1, Scalar::all(0));//旋转矩阵
read_CamMat(Camfile, cameraMatrix, distCoeffs);//由文件得到内参结果和畸变系数
for (int i = 0; i < ::pic_num; i++) {
std::vector<cv::Point2f> imgPos;
std::vector<cv::Point3f> worldPos;
Mat rvec, tvec;
resd_imgandworldPos(imgfile, i, imgPos, worldPos)
cv::solvePnPRansac(worldPos, imgPos, cameraMatrix, distCoeffs, rvec, tvec);
cv::Rodrigues(rvec, rot_matrix);
cout << "idx=" << i << "R: " << rot_matrix << "T: " << tvec << endl;
rotation_matrix.push_back(rot_matrix);
tvecsMat.push_back(tvec);
}
}
cv::Vec3f calT_Axis(std::vector<cv::Mat> rotation_matrix, std::vector<cv::Mat> tvecsMat)
{
std::vector<Mat> worldPoints;
std::vector<cv::Point3f> circlePoses;
for (int i = 0; i < ::boardsize.width; ++i)
{
for (int j = 0; j < ::boardsize.height; ++j)
{
Mat wdpos = Mat(3, 1, CV_64FC1, Scalar::all(0));
wdpos.at<double>(0, 0) = j * ::board;
wdpos.at<double>(1, 0) = i * ::board;
wdpos.at<double>(2, 0) = 0;
cout << wdpos << endl;
worldPoints.push_back(wdpos);//所有的世界坐标
}
}
for (int i = 0; i < 80; i++) {
cout << "a" << worldPoints[i] << endl;
}
vector<Vec6f> lines;
Mat Campos = Mat(3, 1, CV_64FC1, Scalar::all(0));
for (int n = 0; n < worldPoints.size(); n++) {
std::vector<cv::Point3f> pos;
for (int k = 0; k < rotation_matrix.size(); k++) {
Campos = rotation_matrix[k] * worldPoints[n] + tvecsMat[k];//世界坐标转为相机坐标
Point3f p = Point3f(Campos.at<double>(0,0), Campos.at<double>(1, 0), Campos.at<double>(2, 0));
pos.push_back(p);
}
Vec6f line;
cv::fitLine(pos, line, cv::DIST_L2, 0, 1e-2, 1e-2);
cout << n << ":" << line << endl;
lines.push_back(line);
}
Vec3f res=Vec3f(0,0,0);
for (int i = 0; i < lines.size(); i++) {
res[0] += lines[i][0];
res[1] += lines[i][1];
res[2] += lines[i][2];
}
res[0] /= lines.size(); res[1] /= lines.size(); res[2] /= lines.size();
return res;
}
1.使用步进电机移动平移台固定距离(必须1mm),需要注意的是,如下图,在大恒软件中需要设置行程为0.5mm,实际行程才为1mm,该处是正确重建的关键。本人此前曾用QT发送指令移动电机然后控制工业相机拍照得不到好的结果,原因是未考虑这个1mm在指令中对应的脉冲数。到了大恒软件中,设置1mm,依然不准确,分析测试运动20mm实际运动40mm,才明白两倍关系。平移台类型中设置默认GCD-501100m即可。
2.建立文件夹linepic_xihua
3.由于线激光只在宽度较小的ROI中存在,所以采用大恒的软件采集图像时,可设置ROI,参数设置必须在相机采集前设置。图像宽度设置为448,水平偏移设置为1408(根据实际情况确定),曝光时间5000或10000合适(此处根据实际情况确定)。
//矫正畸变
int add_x = 1408, add_y = 0;
int raw_width = 2448, raw_height = 2048;
for (int j = 0; j < linePos.size(); j++) {
//linePos[j].x += 1104;
//此处有更改,原来设置为固定值,导致自己将add_x设置为0时,重建依然失败
//此处改为变量add_x*****
linePos[j].x += add_x;
}
4.尝试了曝光时间为500,1000,2000,5000,10000,20000,40000,发现5000或10000较为合适,但螺纹并不能重建出来,原因系螺纹孔处的线激光在相机当前位置成像不好,后续改换线激光和相机的角度,应能解决这个问题。曝光为1000时得法兰盘局部效果如下图。