线结构光三维重建法兰盘

线结构光三维重建可以得到法兰盘的三维点云图,进一步的,我们可以对螺纹处的点云数据进行分析,本节主要记录线结构光三维重建的主要步骤及一些问题。

一、光平面方程

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即可。

线结构光三维重建法兰盘_第1张图片

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时得法兰盘局部效果如下图。

线结构光三维重建法兰盘_第2张图片

你可能感兴趣的:(法兰盘三维重建,算法,c++)