opencv相机标定和计算外参四元数

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"

#include <cctype>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <iostream>

using namespace cv;
using namespace std;



float squareSize = 1.f, aspectRatio = 1.f;
const char* outputFilename = "out_camera_data.yml";

int  nframes = 1;
int flags = 0;
int m_width = 640;
int m_height = 480;
int cameraId = 0;
int delay = 1000;

Mat cameraMatrix, distCoeffs, my_frameRBG, r, t;
Size boardSize(6, 9), imageSize(640, 480);
clock_t prevTimestamp = 0;
vector<vector<Point2f> > imagePoints;
vector<string> imageList;
vector<Mat> rvecs, tvecs;


enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
Pattern pattern = CHESSBOARD;
int mode = DETECTION;


//计算外参并转化为unity3d能用的
int sign(float x)
{
	return x >= 0 ? 1 : -1;
}
float myMax(float x, float y)
{
	return x >y ? x : y;

}
//计算四元数
void QuaternionFromMatrix(const Mat& R, float quat[])
{
	// Adapted from: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm

	quat[0] = (float)sqrt(myMax(0.0, 1 + R.at<float>(0, 0) + R.at<float>(1, 1) + R.at<float>(2, 2))) / 2;

	quat[1] = (float)sqrt(myMax(0.0, 1 + R.at<float>(0, 0) - R.at<float>(1, 1) - R.at<float>(2, 2))) / 2;
	quat[2] = (float)sqrt(myMax(0.0, 1 - R.at<float>(0, 0) + R.at<float>(1, 1) - R.at<float>(2, 2))) / 2;
	quat[3] = (float)sqrt(myMax(0.0, 1 - R.at<float>(0, 0) - R.at<float>(1, 1) + R.at<float>(2, 2))) / 2;

	quat[1] *= sign(R.at<float>(2, 1) - R.at<float>(1, 2));
	quat[2] *= sign(R.at<float>(0, 2) - R.at<float>(2, 0));
	quat[3] *= sign(R.at<float>(1, 0) - R.at<float>(0, 1));
}

//左右手坐标系转化,计算四元数
void CcalinitEctrinsicMat(float* m_fExtrinsic, Mat rr, Mat tt)
{
	rr.convertTo(rr, CV_32F);
	tt.convertTo(tt, CV_32F);
	Mat R = Mat(3, 3, CV_32F), T = Mat(3, 1, CV_32F);
	T = tt;
	R = rr;
	mrightEX = mrightEX.inv();
	//for (size_t i = 0; i < 3; i++)
	//{
	//	for (size_t j = 0; j < 3; j++)
	//	{
	//		R.at<float>(i, j) = mrightEX.at<float>(i, j);
	//		//T.at<float>(i, 0) = mrightEX.at<float>(i, 3);
	//	}
	//	T.at<float>(i, 0) = mrightEX.at<float>(i, 3);
	//}
	左右手坐标系转化

	Mat R_CPP, R_inv, T_CPP;
	R_CPP = R;
	R_inv = R_CPP.inv();
	T_CPP = T;
	QuaternionFromMatrix(R_inv, m_fExtrinsic);

	// We need to invert rotations on X and Z axis
	m_fExtrinsic[1] = -m_fExtrinsic[1];
	m_fExtrinsic[3] = -m_fExtrinsic[3];
	Mat Tt = -R_inv * T_CPP;
	m_fExtrinsic[4] = (float)Tt.at<float>(0);
	m_fExtrinsic[5] = -(float)Tt.at<float>(1);
	m_fExtrinsic[6] = (float)Tt.at<float>(2);
}




static void calcChessboardCorners(Size boardSize, float squareSize,
				vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
{
	corners.resize(0);

	switch (patternType)
	{
	case CHESSBOARD:
	case CIRCLES_GRID:
		for (int i = 0; i < boardSize.height; i++)
			for (int j = 0; j < boardSize.width; j++)
				corners.push_back(Point3f(float(j*squareSize),
				float(i*squareSize), 0));
		break;

	case ASYMMETRIC_CIRCLES_GRID:
		for (int i = 0; i < boardSize.height; i++)
			for (int j = 0; j < boardSize.width; j++)
				corners.push_back(Point3f(float((2 * j + i % 2)*squareSize),
				float(i*squareSize), 0));
		break;

	default:
		CV_Error(CV_StsBadArg, "Unknown pattern type\n");
	}
}


void main()
{

	VideoCapture capture(cameraId);
	float m_fExtrinsic[7];
	memset(m_fExtrinsic, 0, 7 * sizeof(float));

	for (int i = 0;; i++)
{
	Mat view, viewGray;
	bool blink = false;
	capture.set(CV_CAP_PROP_FRAME_WIDTH, m_width);
	capture.set(CV_CAP_PROP_FRAME_HEIGHT, m_height);

	if (capture.isOpened())
	{
		Mat view0;
		capture >> view0;
		view0.copyTo(view);
	}

	//flip(view, view, 0);
	vector<Point2f> pointbuf;
	cvtColor(view, viewGray, COLOR_BGR2GRAY);

	bool found;
	switch (pattern)
	{
	case CHESSBOARD:
		found = findChessboardCorners(view, boardSize, pointbuf,
			CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FAST_CHECK | CV_CALIB_CB_NORMALIZE_IMAGE);
		break;
	case CIRCLES_GRID:
		found = findCirclesGrid(view, boardSize, pointbuf);
		break;
	case ASYMMETRIC_CIRCLES_GRID:
		found = findCirclesGrid(view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID);
		break;
	default:
		break;
	}

	// improve the found corners' coordinate accuracy
	if (pattern == CHESSBOARD && found) cornerSubPix(viewGray, pointbuf, Size(11, 11),
		Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
	mode = CAPTURING;

	if (mode == CAPTURING && found && (!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC))
	{
		imagePoints.push_back(pointbuf);
		prevTimestamp = clock();
		blink = capture.isOpened();
	}

	if (found)
		drawChessboardCorners(view, boardSize, Mat(pointbuf), found);

	imshow("Image View", view);
	waitKey(1);
	//if (view.data)
	//{
	//	cvtColor(view, my_frameRBG, CV_BGR2RGB);
	//	memcpy(texturePtr, my_frameRBG.data, my_frameRBG.cols*my_frameRBG.rows*my_frameRBG.channels()*sizeof(uchar));
	//}

	if (mode == CAPTURING && imagePoints.size() >= (unsigned)nframes)
	{
			double totalAvgErr = 0;
			cameraMatrix = Mat::eye(3, 3, CV_64F);
			if (flags & CV_CALIB_FIX_ASPECT_RATIO)
				cameraMatrix.at<double>(0, 0) = aspectRatio;

			distCoeffs = Mat::zeros(8, 1, CV_64F);
			vector<vector<Point3f> > objectPoints(1);
			calcChessboardCorners(boardSize, squareSize, objectPoints[0], pattern);
			objectPoints.resize(imagePoints.size(), objectPoints[0]);

			double rms = calibrateCamera(objectPoints, imagePoints, imageSize, cameraMatrix,
				distCoeffs, rvecs, tvecs, flags | CV_CALIB_FIX_K4 | CV_CALIB_FIX_K5);		
			mode = CALIBRATED;
		}	
		else
			mode = DETECTION;
		if (!capture.isOpened())
			break;

		if (mode == CALIBRATED)
		{
			Rodrigues(rvecs[0], r);
			cout << rvecs[0] << endl;
			CcalinitEctrinsicMat(m_fExtrinsic, r, tvecs[0]);
			rvecs.clear();
		}
		for each (float var in m_fExtrinsic)
		{
			cout << var << endl;
		}
	}
	return;
}







  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
手眼标定是指将机械臂的末端执行器与相机之间的相对姿态关系确定下来,这样可以用机械臂控制相机来实现精确的视觉引导。在手眼标定中,需要使用到相机的内外参数,机械臂的关节角度和末端执行器的位姿。下面介绍一种用C++实现手眼标定的方法。 1. 准备数据 首先需要获取机械臂的关节角度和末端执行器的位姿,以及相机的内外参数。可以通过机械臂的编程接口获取机械臂的状态信息,也可以手动记录机械臂运动时的关节角度和末端执行器位姿。相机的内外参数可以通过相机标定软件获取,例如OpenCV中的Camera Calibration。 2. 实现标定算法 手眼标定通常使用四元数来表示姿态关系,因为四元数可以避免万向锁问题。手眼标定的过程可以分为两步:计算机械臂的姿态变换矩阵和相机的姿态变换矩阵,然后通过求解手眼标定问题得到机械臂与相机的相对姿态矩阵。 计算机械臂的姿态变换矩阵可以通过机械臂的正运动学求解,计算相机的姿态变换矩阵可以通过相机的位姿信息求解,例如通过相机标定得到的旋转矩阵和平移矩阵。 通过求解手眼标定问题,可以得到机械臂与相机之间的相对姿态矩阵。手眼标定问题通常使用最小二乘法求解,可以使用OpenCV中的solvePnP函数求解。 3. 实现代码 下面是一个用C++和OpenCV实现手眼标定的代码示例: ``` #include <opencv2/opencv.hpp> #include <opencv2/core/eigen.hpp> #include <Eigen/Dense> using namespace cv; void handEyeCalibration(const std::vector<Mat>& robotPoses, const std::vector<Mat>& cameraPoses, Mat& H) { int n = robotPoses.size(); Mat A = Mat::zeros(n * 6, 6, CV_64F); Mat B = Mat::zeros(n * 6, 1, CV_64F); for (int i = 0; i < n - 1; i++) { Mat Ri1, ti1, Ri2, ti2; cv::Matx33d R1(cameraPoses[i](cv::Rect(0, 0, 3, 3))), R2(cameraPoses[i + 1](cv::Rect(0, 0, 3, 3))); cv::Vec3d t1(cameraPoses[i](cv::Rect(3, 0, 1, 3))), t2(cameraPoses[i + 1](cv::Rect(3, 0, 1, 3))); cv::Rodrigues(R1, Ri1); cv::Rodrigues(R2, Ri2); cv::Matx33d Ri = R2 * R1.t(); cv::Vec3d ti = t2 - R2 * t1; cv::Matx33d Ti(robotPoses[i](cv::Rect(0, 0, 3, 3))), Hi(robotPoses[i + 1](cv::Rect(0, 0, 3, 3))); cv::Vec3d pi(robotPoses[i](cv::Rect(3, 0, 1, 3))), hi(robotPoses[i + 1](cv::Rect(3, 0, 1, 3))); cv::Matx33d HiTi = Hi * Ti.t(); cv::Vec3d hi_ti = hi - Hi * ti; cv::Matx33d A1 = Ri.t() - cv::Matx33d::eye(); cv::Matx33d A2 = HiTi - cv::Matx33d::eye(); cv::Matx33d A3 = Hi * Ti.t() - Ri.t() * Hi; cv::Matx33d A4 = Hi * Ti.t() - cv::Matx33d::eye(); cv::Vec3d b1 = hi_ti; cv::Vec3d b2 = Hi * pi - pi * Ri.t(); cv::Vec3d b3 = Hi * ti - ti * Ri.t(); Mat Ai, Bi; cv::vconcat(Mat(A1), Mat(A2), Ai); cv::vconcat(Ai, Mat(A3), Ai); cv::vconcat(Ai, Mat(A4), Ai); cv::vconcat(Mat(b1), Mat(b2), Bi); cv::vconcat(Bi, Mat(b3), Bi); int idx = i * 6; Ai.copyTo(A.rowRange(idx, idx + 6)); Bi.copyTo(B.rowRange(idx, idx + 6)); } Mat X; solve(A, B, X, DECOMP_SVD); H = Mat::eye(4, 4, CV_64F); X.rowRange(0, 3).col(0).copyTo(H.rowRange(0, 3).col(0)); X.rowRange(3, 6).col(0).copyTo(H.rowRange(0, 3).col(1)); X.rowRange(6, 9).col(0).copyTo(H.rowRange(0, 3).col(2)); X.rowRange(9, 12).col(0).copyTo(H.rowRange(0, 3).col(3)); } int main() { std::vector<Mat> robotPoses; // 机械臂位姿 std::vector<Mat> cameraPoses; // 相机位姿 // 加载机械臂位姿和相机位姿 Mat H; handEyeCalibration(robotPoses, cameraPoses, H); std::cout << "Hand-eye calibration result: " << std::endl << H << std::endl; return 0; } ``` 其中,robotPoses和cameraPoses分别是机械臂和相机的位姿序列,H是求解得到的机械臂与相机之间的相对姿态矩阵。通过solve函数求解线性方程组得到H,然后将H转换为4x4的变换矩阵即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值