GSoC2011SfM
0.1
Google Summer of Code 2011: Structure from motion
|
00001 #include "CameraPinhole.h" 00002 00003 using std::vector; 00004 using cv::Mat; 00005 using cv::Vec2d; 00006 using cv::Vec3d; 00007 using cv::Vec4d; 00008 using cv::Range; 00009 00010 namespace OpencvSfM{ 00011 00012 CameraPinhole::CameraPinhole( cv::Mat intra_params/*=Mat::eye( 3, 3, CV_64F )*/, 00013 int img_w, int img_h, 00014 unsigned char wantedEstimation/*=FOCAL_PARAM|SKEW_PARAM|PRINCIPAL_POINT_PARAM*/ ) 00015 :Camera( img_w, img_h) 00016 { 00017 CV_DbgAssert( intra_params.rows==3 && intra_params.cols==3 ); 00018 00019 intra_params.convertTo( this->intra_params_,CV_64F ); 00020 this->inv_intra_params_ = intra_params.inv( ); 00021 this->estimation_needed_ = wantedEstimation; 00022 } 00023 00024 CameraPinhole::CameraPinhole( 00025 const std::vector< std::vector< cv::Point3f > >& objectPoints, 00026 const std::vector< std::vector<cv::Point2f > >& imagePoints, 00027 cv::Size imageSize, double aspectRatio/*=1.*/, 00028 int img_w, int img_h, unsigned char wantedEstimation ) 00029 :Camera( img_w, img_h) 00030 { 00031 Mat intra=initCameraMatrix2D( objectPoints,imagePoints,imageSize,aspectRatio ); 00032 //to be sure that newParams has correct type: 00033 intra.convertTo( this->intra_params_,CV_64F ); 00034 this->inv_intra_params_ = intra_params_.inv( ); 00035 this->estimation_needed_ = wantedEstimation; 00036 } 00037 CameraPinhole::~CameraPinhole( ) 00038 { 00039 //TODO 00040 } 00041 00042 uchar CameraPinhole::getNbMissingParams( ) const 00043 { 00044 int nbParams = 0; 00045 if( (estimation_needed_ & FOCAL_PARAM) != 0 ) 00046 nbParams += 2;//2 focals 00047 if( (estimation_needed_ & SKEW_PARAM) != 0 ) 00048 nbParams += 1;//1 param 00049 if( (estimation_needed_ & PRINCIPAL_POINT_PARAM) != 0 ) 00050 nbParams += 2;//2 coord of principal point... 00051 return nbParams; 00052 } 00053 00054 void CameraPinhole::updateIntrinsic( double* values, uchar nbVal, bool add ) 00055 { 00056 if( !add ) 00057 intra_params_ = Mat::zeros( 3, 3, CV_64F); 00058 00059 double* ptrIntraParam=( double* )intra_params_.data; 00060 ptrIntraParam[ 8 ] = 1.0; 00061 if( nbVal >= 1 ) 00062 { 00063 ptrIntraParam[ 0 ] += values[ 0 ]; 00064 if( nbVal >= 3 ) 00065 { 00066 ptrIntraParam[ 2 ] += values[ 1 ]; 00067 ptrIntraParam[ 5 ] += values[ 2 ]; 00068 if( nbVal >= 4 ) 00069 { 00070 ptrIntraParam[ 4 ] = ptrIntraParam[ 0 ] * values[ 3 ]; 00071 if( nbVal >= 5 ) 00072 { 00073 ptrIntraParam[ 1 ] += values[ 4 ]; 00074 } 00075 } 00076 } 00077 } 00078 this->inv_intra_params_ = intra_params_.inv( ); 00079 } 00080 00081 void CameraPinhole::updateIntrinsicMatrix( cv::Mat newParams, 00082 unsigned char intraValues/*=FOCAL_PARAM|SKEW_PARAM|PRINCIPAL_POINT_PARAM*/ ) 00083 { 00084 00085 CV_DbgAssert( !newParams.empty( ) || ( newParams.rows==3 ) || ( newParams.cols==3 ) ); 00086 //to be sure that newParams has correct type: 00087 Mat correctMat; 00088 newParams.convertTo( correctMat,CV_64F ); 00089 00090 //now we can access to element using double array: 00091 double* ptrData=( double* )correctMat.data; 00092 double* ptrIntraParam=( double* )intra_params_.data; 00093 00094 //first focal values: 00095 if( intraValues&FOCAL_PARAM ) 00096 { 00097 ptrIntraParam[ 0 ]=ptrData[ 0 ]; 00098 ptrIntraParam[ 4 ]=ptrData[ 4 ]; 00099 } 00100 //skew param: 00101 if( intraValues&SKEW_PARAM ) 00102 ptrIntraParam[ 1 ]=ptrData[ 1 ]; 00103 00104 //and principal point: 00105 if( intraValues&PRINCIPAL_POINT_PARAM ) 00106 { 00107 ptrIntraParam[ 2 ]=ptrData[ 2 ]; 00108 ptrIntraParam[ 5 ]=ptrData[ 5 ]; 00109 } 00110 } 00111 00112 vector<Vec4d> CameraPinhole::convertFromImageTo3Dray( std::vector< cv::Vec3d > points ) 00113 { 00114 //TODO 00115 return vector<Vec4d>( ); 00116 } 00117 00118 vector<Vec2d> CameraPinhole::pixelToNormImageCoordinates( std::vector< cv::Vec2d > points ) const 00119 { 00120 vector<Vec2d> newCoordinates; 00121 double* ptrIntraParam=( double* )inv_intra_params_.data; 00122 //for each 2D point, use inv_intra to compute 2D point: 00123 vector<Vec2d>::iterator point=points.begin( ); 00124 while( point!=points.end( ) ) 00125 { 00127 //Can be done using matrix multiplication like this: 00129 /* 00130 Vec3d pointNorm( (*point )[ 0 ],( *point )[ 1 ],1 ); 00131 Mat pointNormImageCoordinate=inv_intra_params_ * Mat( pointNorm ); 00132 double * dataPointImageCoordinate = ( double * ) pointNormImageCoordinate.data; 00133 Vec2d point2D( dataPointImageCoordinate[ 0 ]/dataPointImageCoordinate[ 2 ], 00134 dataPointImageCoordinate[ 1 ]/dataPointImageCoordinate[ 2 ] ); 00135 newCoordinates.push_back( point2D ); 00136 */ 00137 00139 //But this is faster ( as intra matrix has only 5 values ) : 00140 newCoordinates.push_back( Vec2d( ptrIntraParam[ 0 ] * ( *point )[ 0 ] + ptrIntraParam[ 1 ] * ( *point )[ 1 ] + ptrIntraParam[ 2 ], 00141 ptrIntraParam[ 4 ] * ( *point )[ 1 ] + ptrIntraParam[ 5 ] )); 00142 00143 point++; 00144 } 00145 return newCoordinates; 00146 } 00147 00148 vector<Vec2d> CameraPinhole::normImageToPixelCoordinates( std::vector< cv::Vec2d > points ) const 00149 { 00150 vector<Vec2d> newCoordinates; 00151 //for each 2D point, use intra params to compute 2D point: 00152 double* ptrIntraParam=( double* )intra_params_.data; 00153 00154 vector<Vec2d>::iterator point=points.begin( ); 00155 while( point!=points.end( ) ) 00156 { 00158 //Same as pixelToNormImageCoordinates, faster than using matrix multiplication: 00159 newCoordinates.push_back( Vec2d( (ptrIntraParam[ 0 ] * ( *point )[ 0 ] + ptrIntraParam[ 1 ] * ( *point )[ 1 ] + ptrIntraParam[ 2 ]) / ptrIntraParam[ 8 ], 00160 (ptrIntraParam[ 4 ] * ( *point )[ 1 ] + ptrIntraParam[ 5 ] ) / ptrIntraParam[ 8 ])); 00161 00162 point++; 00163 } 00164 return newCoordinates; 00165 } 00166 00167 double CameraPinhole::getFocal( ) const 00168 { 00169 Mat x1 = Mat::zeros( 3, 1, CV_64F ); 00170 Mat x2 = Mat::zeros( 3, 1, CV_64F ); 00171 x1.at<double>( 0,0 ) = intra_params_.at<double>( 0,2 ); 00172 x1.at<double>( 1,0 ) = intra_params_.at<double>( 1,2 ); 00173 x1.at<double>( 2,0 ) = 1.0; 00174 x2.at<double>( 0,0 ) = 0; 00175 x2.at<double>( 1,0 ) = 0; 00176 x2.at<double>( 2,0 ) = 1.0; 00177 Mat omega = ( intra_params_ * intra_params_.t( ) ).inv( ); 00178 00179 double upDivision = ( (Mat )( x1.t( ) * omega * x2 )).at<double>( 0,0 ); 00180 double tmpDown1 = ( (Mat )( x1.t( ) * omega * x1 )).at<double>( 0,0 ); 00181 double tmpDown2 = ( (Mat )( x2.t( ) * omega * x2 )).at<double>( 0,0 ); 00182 tmpDown1 = sqrt( tmpDown1 ); 00183 tmpDown2 = sqrt( tmpDown2 ); 00184 00185 double cosAngle = upDivision / ( tmpDown1 * tmpDown2 ); 00186 double angle = acos( cosAngle ); 00187 00188 //instead of computing the focal using distance in pixels, 00189 //we use the distance in camera's coordinates: 00190 double origin_x = inv_intra_params_.at<double>( 0,2 ), 00191 origin_y = inv_intra_params_.at<double>( 1,2 ); 00192 double distance = sqrt ( origin_x * origin_x + 00193 origin_y * origin_y ); 00194 00195 return origin_y / tan( angle ); 00196 } 00197 00198 00199 cv::Ptr<Camera> CameraPinhole::read( const cv::FileNode& node ) 00200 { 00201 std::string myName=node.name( ); 00202 if( myName != "CameraPinhole" ) 00203 { 00204 std::string error = "CameraPinhole FileNode is not correct!\nExpected \"CameraPinhole\", got "; 00205 error += node.name(); 00206 CV_Error( CV_StsError, error.c_str() ); 00207 } 00208 00209 Mat intra_params; 00210 unsigned char wantedEstimation; 00211 //load intra parameters: 00212 node[ "intra_params_" ] >> intra_params; 00213 00214 node[ "estimation_needed_" ] >> wantedEstimation; 00215 00216 return cv::Ptr<Camera>( new CameraPinhole(intra_params, wantedEstimation) ); 00217 } 00218 00219 void CameraPinhole::write( cv::FileStorage& fs ) const 00220 { 00221 fs << "CameraPinhole" << "{"; 00222 fs << "intra_params_" << this->intra_params_; 00223 fs << "estimation_needed_" << this->estimation_needed_; 00224 fs << "}"; 00225 } 00226 00227 }