Drawing of epipolar lines (known internal and external parameters of the camera, epipolar geometry)

Polar Geometry and Stereo Vision
Goals:
Constraints of Extreme Geometry There are a lot of information on the Internet, but most of them are theoretical knowledge and some derivations. However, in specific applications, ** often uses limit constraints to judge whether the camera's pose is accurate, and there are relatively few introductions to this. **This blog will introduce how to realize the limit constraints of two cameras in the case of constant internal and external parameters of the camera.

The binocular vision correspondence can also be used for motion estimation between two adjacent frames. See the figure below:
insert image description here
the matching point must be on the epipolar line
Baseline: the line connecting the optical centers of the left and right cameras (yellow line segment O 1 O 2 O_1O_2O1O2);
polar plane: space point, the plane determined by the optical centers of the two cameras (gray plane O 1 PO 2 O_1PO_2O1P.O _2);
pole: the intersection point of the baseline and the image plane of the two cameras;
epipolar line: the intersection line between the polar plane and the image plane (blue line segment)

Based on the geometric relationship of the limit, it is necessary to know their parameter relationship as follows.
Transformation of two cameras to R , TR,TR,T. If the internal and
external parameters of the two cameras are known. Their transformation matrices can be calculated. Assume that the camera on the left is the world coordinate system. They can be expressed as:
insert image description here

The above is assuming the relationship between the two cameras. The world coordinate system is the coordinate system of the left camera. They coincide. So by R , TR,TR,The parameters of the second camera calculated by T (it is the camera position pose, which refers to R , TR, Tin the first coordinate systemR,T ),
the specific calculation can refer to the following.
p 2 p_2p2Coordinate point (originally at O ​​2 O_2O2coordinate system) into O 1 O_1OCoordinate system p 1 p_1pof ( p 1 p_1p1means it is at O ​​1 O_1O1The coordinates in the coordinate system) are:
p 1 = R p 2 + T = > p 2 = RT p 1 − RTT ( 1 ) p_1=Rp_2+T=>p_2=R^Tp_1-R^TT\space \space \space \space (1)p=Rp+T=>p=RTpRT T(1)    
The above can be obtained, fromO 1 O_1OConvert the coordinate system to O 2 O_2OThe coordinate system is:
[ RT − RTT ] [R^T \space \space -R^TT][RT  RTT]

So the two mutual transformation matrices are:
[ R , T ] [R,T][R,T ] :means from O 2 O_2OCoordinate system to O 1 O_1OTransformation of the coordinate system .
[ RT − RTT ] [R^T \space \space -R^TT][RT  RT T]:means fromO 1 O_1OCoordinate system to O 2 O_2OTransformation of the coordinate system .

Finally, add the internal reference matrix K of the camera, K ′ K,K'K,K' The overall matrix transformation can be obtained as follows:

M = K [ I    0 ] ,    M ′ = K ′ [ R T    − R T T ] M=K[I \space \space 0] ,\space \space M'=K'[R^T \space \space -R^TT] M=K [ I0 ] , _    M=K[RT  RTT]

For the convenience of calculation, it is assumed that the camera's internal parameter matrix K , K ′ K,K'K,K is the identity matrixIII。因此
M = [ I    0 ] ,    M ′ = [ R T    − R T T ] M=[I \space \space 0] ,\space \space M'=[R^T \space \space -R^TT] M=[ I 0 ] ,    M=[RT  RTT]

From Figure 2 we can know that p ′ p'p in the coordinate systemO 1 O_1O1The coordinates are: R p ′ + T Rp'+TRp+T , sinceR p ′ + T Rp'+TRp+T this point is inO 1 O_1O1The epipolar line O 1 P O_1P of the coordinate systemO1on P , because the limit planeO 1 O 2 P O_1O_2PO1O2P是平面:
T × ( R p ′ + T ) = T × ( R p ′ )      ( 2 ) T\times(Rp'+T)=T\times(Rp')\space \space\space \space (2) T×(Rp+T)=T×(Rp )(2)    
VectorT × ( R p ′ ) T\times(Rp')T×(Rp )means perpendicular to the planeO 1 O 2 P O_1O_2PO1O2P
is obtained by cross product:
p T ⋅ [ T × ( R p ′ ) ] = 0 ( 3 ) p^T \cdot[T\times(Rp')]=0 \space \space\space \space (3)pT[T×(Rp)]=0 ( 3 )    
where the cross productT TT can be expressed in matrix form, generally expressed asskew ( T ) skew(T)s k e w ( T ) or[ T × ] [T_{\times}][T×]
[ T × ] = [ 0 − T [ 2 ] T [ 1 ] T [ 2 ] 0 − T [ 0 ] − T [ 1 ] T [ 0 ] 0 ]      ( 4 ) [T_{\times}]=\begin{bmatrix} 0 & -T[2] & T[1] \\ T[2] & 0 & -T[0] \\ -T[1] & T[0] & 0 \end{bmatrix} \space \space\space \space (4) [T×]=0T[2]T[1]T[2]0T[0]T[1]T[0]0    ( 4 )
Formula(3) (3)( 3 ) Written in matrix form:
p T ⋅ [ T × ( R p ′ ) ] = 0 = > p T [ T × ] R p ′ = 0 ( 5 ) p^T \cdot[T\times(Rp ')]=0=>p^T[T_{\times}]Rp'=0 \space \space\space \space (5)pT[T×(Rp)]=0=>pT[T×]Rp=0 ( 5 )    
from formula( 5 ) (5)( 5 ) get[ T × ] R [T_{\times}]R[T×] R is Essential Matrix, this matrix is ​​written as:[ T × ] R = E [T_{\times}]R=E[T×]R=E , so:
p TE p ′ = 0 ( 6 ) p^TEp'=0 \space \space\space \space (6)pTEp=The expression of 0 ( 6 )    
straight line isl ⋅ p = 0 l\cdot p=0lp=0 (under the homogeneous coordinate representation)
can putE p ′ Ep'Ep regarded as the equation of a straight line,ppp is regarded as a point on the line, that is,E p ′ Ep'Ep isO 1 O_1O1It is the epipolar line in the origin coordinate system (Figure 1 O 1 e O_1eO1e line segment), and vice versa.

All the above are K , K ′ K, K'K,K is set to the identity matrix. when is no longer the identity matrix. The obtained matrix is ​​"
M = K [ I 0 ] , M ′ = K ′ [ RT − RTT ] ( 7 ) M=K[I \space \space 0] ,\space \space M'=K'[R^T \space \space -R^TT]\space \space\space \space (7)M=K [ I0 ] , _    M=K[RT  RT T](7)    
becauseK , K ′ K,K'K,K is a camera internal parameter, which represents the conversion from the camera coordinate system to the image coordinate system. Therefore:p I = K − 1 p p_I=K^{-1}ppI=K1 p; p I ′ = K ′ − 1 p ′ p_I'=K'^{-1}p'pI=K1p
Because of the formula:
p TE p ′ = 0 ( 6 ) p^TEp'=0 \space \space\space \space (6)pTEp=0 ( 6 )    
Thenp I = K − 1 p p_I=K^{-1}ppI=K1 p; p I ′ = K ′ − 1 p ′ p_I'=K'^{-1}p'pI=K1p' into( 6 ) (6)( 6 ) get:

p T K − T [ T × ] R K ′ − 1 p ′ = 0      ( 8 ) p^TK^{-T}[T_{\times}]RK'^{-1}p'=0 \space \space\space \space (8) pTKT[T×]RK1p=0    (8)
F = K − T [ T × ] R K ′ − 1 F=K^{-T}[T_{\times}]RK'^{-1} F=KT[T×]RK1 , this is the Fundamental Matrix. It is also the Fundametal matrix calculated in opencv

The above is to set the first camera to be consistent with the world coordinate system. But in the case of two cameras all the time. May need to calculate R , TR,TR,T matrix. They are:means from O 2 O_2OCoordinate system to O 1 O_1OTransformation of the coordinate system .
Known two cameras (both external parameters of the camera) are:
M 1 = [ R 1 T 1 0 1 ] M 2 = [ R 2 T 2 0 1 ] M_1=\begin{bmatrix} R_1 & T_1 \\ 0 & 1 \end{bmatrix} \\ \\ \\ M_2=\begin{bmatrix} R_2 & T_2 \\ 0 & 1 \\end{bmatrix}M=[R10T11]M2=[R20T21]
needs to be calculated fromO 2 O_2OCoordinate system to O 1 O_1OTransformation matrices R , TR,T of the coordinate systemR,T
derivation:p 1 p_1
in their respective camera coordinate systemsp1, p 2 p_2 p2
p 1 p_1 p1The vertices in the world coordinate system are
pw = M 1 p 1 ( 9 ) p_w=M_1p_1 \space \space\space \space (9)pw=M1p1    (9)
p 2 p_2 p2The vertices in the world coordinate system are
pw = M 2 p 2 ( 10 ) p_w=M_2p_2 \space \space\space \space (10)pw=M2p2    (10)
( 9 ) ( 10 ) (9)(10) ( 9 ) ( 1 0 ) Formula:
p 1 = M 1 − 1 M 2 p 2 p_1=M^{-1}_1M_2p_2p=M11M2p2
where R , TR,TR,T isM 1 − 1 M 2 M^{-1}_1M_2M11M2Rotation matrix and translation vector.
code show as below:

void RunTwoImagesCorrspondEpilinesWithPath(int src_idx, int tgt_idx, const std::string& dir)
{
    
    
	std::cerr << "the cams num: " << cams_modes_.size() << std::endl;
	if (src_idx < 0 || tgt_idx < 0 ||
		src_idx >= cams_modes_.size() || tgt_idx >= cams_modes_.size())
	{
    
    
		std::cerr << "RunTwoImagesCorrespondEpilines invalid idx..." << std::endl;
		return;
	}
	if (!cams_modes_[src_idx].valid_ || !cams_modes_[tgt_idx].valid_)
	{
    
    
		std::cerr << "RunTwoImagesCorrespondEpilines invalid cams existed..." << std::endl;
		return;
	}
	CameraModel src_cam_model = cams_modes_[src_idx];
	CameraModel tgt_cam_model = cams_modes_[tgt_idx];
	std::string src_name = images_files_path_[src_idx];
	std::string tgt_name = images_files_path_[tgt_idx];
	std::cerr << "src_name: " << src_name << std::endl;
	std::cerr << "tgt_name: " << tgt_name << std::endl;
	std::string src_file_name = src_name.substr(src_name.find_last_of("/") + 1, src_name.find_last_of(".") - src_name.find_last_of("/") - 1);
	std::string tgt_file_name = tgt_name.substr(tgt_name.find_last_of("/") + 1, tgt_name.find_last_of(".") - tgt_name.find_last_of("/") - 1);
	cv::Mat src_rgb = cv::imread(src_name);
	cv::Mat tgt_rgb = cv::imread(tgt_name);
	//cv::imshow("tgt_rgb", tgt_rgb);
	//cv::waitKey(0);
	// used in OpenCV3
	cv::Ptr<cv::FeatureDetector> detector = cv::ORB::create();
	cv::Ptr<cv::DescriptorExtractor> descriptor = cv::ORB::create();
	std::vector< cv::KeyPoint > src_kp, tgt_kp;
	detector->detect(src_rgb, src_kp);
	detector->detect(tgt_rgb, tgt_kp);
	// ¼ÆËãÃèÊö×Ó
	cv::Mat src_desp, tgt_desp;
	descriptor->compute(src_rgb, src_kp, src_desp);
	descriptor->compute(tgt_rgb, tgt_kp, tgt_desp);
	// Æ¥ÅäÃèÊö×Ó
	std::vector< cv::DMatch > matches;
	cv::BFMatcher matcher;
	matcher.match(src_desp, tgt_desp, matches);
	cout << "Find total " << matches.size() << " matches." << endl;
	if (matches.size() < match_pnts_num_)
	{
    
    
		std::cerr << "match num is too much fewer..." << std::endl;
		return;
	}
	// ɸѡƥÅä¶Ô
	std:vector< cv::DMatch > goodMatches;
	double minDis = 9999;
	for (size_t i = 0; i < matches.size(); i++)
	{
    
    
		if (matches[i].distance < minDis)
			minDis = matches[i].distance;
	}
	for (size_t i = 0; i < matches.size(); i++)
	{
    
    
		if (matches[i].distance < 10 * minDis)
			goodMatches.push_back(matches[i]);
	}
	std::vector< cv::Point2f > src_pts, tgt_pts;
	for (size_t i = 0; i<goodMatches.size(); i++)
	{
    
    
		src_pts.push_back(src_kp[goodMatches[i].queryIdx].pt);
		tgt_pts.push_back(tgt_kp[goodMatches[i].trainIdx].pt);
	}
	//通过两个相机的位姿计算它们之间的e_matrix
	Eigen::Matrix4f src_pose = src_cam_model.cam_pose_;
	Eigen::Matrix4f tgt_pose = tgt_cam_model.cam_pose_;
	Eigen::Matrix4f src_extr = src_pose.inverse();
	Eigen::Matrix4f tgt_extr = tgt_pose.inverse();
	Eigen::Matrix4f relative_extr = tgt_extr*src_pose;
	Eigen::Matrix3f r_extr = relative_extr.topLeftCorner(3, 3);
	Eigen::Vector3f t_extr = relative_extr.topRightCorner(3, 1);
	//
	Eigen::Matrix3f em = ComputeCrossMatrixFromLVector(t_extr)*r_extr;
	//已知e_matrix,计算fundamental_matrix
	Eigen::Matrix3f src_km = Eigen::Matrix3f::Identity();
	Eigen::Matrix3f tgt_km = Eigen::Matrix3f::Identity();	
	src_km(0, 0) = src_cam_model.fx_;
	src_km(1, 1) = src_cam_model.fy_;
	src_km(0, 2) = src_cam_model.cx_;
	src_km(1, 2) = src_cam_model.cy_;
	tgt_km(0, 0) = src_cam_model.fx_;
	tgt_km(1, 1) = src_cam_model.fy_;
	tgt_km(0, 2) = src_cam_model.cx_;
	tgt_km(1, 2) = src_cam_model.cy_;
	Eigen::Matrix3f fm = (tgt_km.inverse()).transpose()*em*src_km.inverse();
	std::cerr << "fm: \n" << fm << std::endl;
	Eigen::Matrix3f fmt = fm;
	//²âÊÔ
	cv::Mat fundm = (cv::Mat_<float>(3, 3)
		<< fmt(0, 0), fmt(0, 1), fmt(0, 2),
		fmt(1, 0), fmt(1, 1), fmt(1, 2),
		fmt(2, 0), fmt(2, 1), fmt(2, 2));
	std::cerr << fundm << std::endl;
	std::vector<cv::Vec<float, 3>> epilines1, epilines2;
	computeCorrespondEpilines(src_pts, 1, fundm, epilines1);
	computeCorrespondEpilines(tgt_pts, 2, fundm, epilines2);
	cv::RNG &rng = cv::theRNG();
	for (int i = 0; i < match_pnts_num_; ++i) {
    
    
		//Ëæ»ú²úÉúÑÕÉ«
		std::cerr << "epilines1: " << i << ": " << epilines1[i] << std::endl;
		std::cerr << "epilines2: " << i << ": " << epilines2[i] << std::endl;
		cv::Scalar color = cv::Scalar(rng(255), rng(255), rng(255));
		circle(src_rgb, src_pts[i], 5, color, 3);
		//»æÖÆÍ⼫ÏßµÄʱºò£¬Ñ¡ÔñÁ½¸öµã£¬Ò»¸öÊÇx=0´¦µÄµã£¬Ò»¸öÊÇxΪͼƬ¿í¶È´¦
		line(src_rgb, cv::Point(0, -epilines2[i][2] / epilines2[i][1]), cv::Point(src_rgb.cols, -(epilines2[i][2] + epilines2[i][0] * src_rgb.cols) / epilines2[i][1]), color);
		circle(tgt_rgb, tgt_pts[i], 5, color, 3);
		line(tgt_rgb, cv::Point(0, -epilines1[i][2] / epilines1[i][1]), cv::Point(tgt_rgb.cols, -(epilines1[i][2] + epilines1[i][0] * tgt_rgb.cols) / epilines1[i][1]), color);
	}
	std::string abs_src_file_name = dir + "/" + src_file_name + ".png";
	std::string abs_tgt_file_name = dir + "/" + tgt_file_name + ".png";
	std::cerr << "abs_src_file_name: " << abs_src_file_name << std::endl;
	std::cerr << "abs_tgt_file_name: " << abs_tgt_file_name << std::endl;
	//std::system("pause");
	//cv::imshow("epiline1", tgt_rgb);
	//cv::waitKey(0);
	//std::cerr << "tgt_rgb rows, cols: " << tgt_rgb.rows << ", " << tgt_rgb.cols << std::endl;
	//std::system("pause");
	cv::imwrite(abs_tgt_file_name, tgt_rgb);
	//std::system("pause");
	//cv::imshow("epiline2", src_rgb);
	cv::imwrite(abs_src_file_name, src_rgb);
	cv::waitKey(0);
}

The test results are as follows:
insert image description here

Guess you like

Origin blog.csdn.net/weixin_43851636/article/details/126783484