diff --git a/modules/calib3d/include/opencv2/calib3d.hpp b/modules/calib3d/include/opencv2/calib3d.hpp index 28d6862e59..709c21b1af 100644 --- a/modules/calib3d/include/opencv2/calib3d.hpp +++ b/modules/calib3d/include/opencv2/calib3d.hpp @@ -244,7 +244,9 @@ enum { SOLVEPNP_ITERATIVE = 0, enum { CALIB_CB_ADAPTIVE_THRESH = 1, CALIB_CB_NORMALIZE_IMAGE = 2, CALIB_CB_FILTER_QUADS = 4, - CALIB_CB_FAST_CHECK = 8 + CALIB_CB_FAST_CHECK = 8, + CALIB_CB_EXHAUSTIVE = 16, + CALIB_CB_ACCURACY = 32 }; enum { CALIB_CB_SYMMETRIC_GRID = 1, @@ -847,7 +849,11 @@ CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, Out @param patternSize Number of inner corners per a chessboard row and column ( patternSize = cv::Size(points_per_row,points_per_colum) = cv::Size(columns,rows) ). @param corners Output array of detected corners. -@param flags operation flags for future improvements +@param flags Various operation flags that can be zero or a combination of the following values: +- **CALIB_CB_NORMALIZE_IMAGE** Normalize the image gamma with equalizeHist before detection. +- **CALIB_CB_EXHAUSTIVE ** Run an exhaustive search to improve detection rate. +- **CALIB_CB_ACCURACY ** Up sample input image to improve sub-pixel accuracy due to aliasing effects. +This should be used if an accurate camera calibration is required. The function is analog to findchessboardCorners but uses a localized radon transformation approximated by box filters being more robust to all sort of diff --git a/modules/calib3d/src/chessboard.cpp b/modules/calib3d/src/chessboard.cpp index 08e47d1fe8..a7bfac4d1a 100644 --- a/modules/calib3d/src/chessboard.cpp +++ b/modules/calib3d/src/chessboard.cpp @@ -24,7 +24,7 @@ namespace details { const float CORNERS_SEARCH = 0.5F; // percentage of the edge length to the next corner used to find new corners const float MAX_ANGLE = float(48.0/180.0*M_PI); // max angle between line segments supposed to be straight const float MIN_COS_ANGLE = float(cos(35.0/180*M_PI)); // min cos angle between board edges -const float MIN_RESPONSE_RATIO = 0.3F; +const float MIN_RESPONSE_RATIO = 0.1F; const float ELLIPSE_WIDTH = 0.35F; // width of the search ellipse in percentage of its length const float RAD2DEG = float(180.0/M_PI); const int MAX_SYMMETRY_ERRORS = 5; // maximal number of failures during point symmetry test (filtering out lines) @@ -602,63 +602,67 @@ void FastX::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vecto return; } -void FastX::detectImpl(const cv::Mat& gray_image, +void FastX::detectImpl(const cv::Mat& _gray_image, std::vector &rotated_images, std::vector &feature_maps, const cv::Mat &_mask)const { if(!_mask.empty()) CV_Error(Error::StsBadSize, "Mask is not supported"); - CV_CheckTypeEQ(gray_image.type(), CV_8UC1, "Unsupported image type"); + CV_CheckTypeEQ(_gray_image.type(), CV_8UC1, "Unsupported image type"); // up-sample if needed + cv::Mat gray_image; int super_res = int(parameters.super_resolution); if(super_res) - cv::resize(gray_image,gray_image,cv::Size(),2,2); + cv::resize(_gray_image,gray_image,cv::Size(),2,2); + else + gray_image = _gray_image; //for each scale int num_scales = parameters.max_scale-parameters.min_scale+1; rotated_images.resize(num_scales); feature_maps.resize(num_scales); - for(int scale=parameters.min_scale;scale <= parameters.max_scale;++scale) - { - // calc images - // for each angle step - int scale_id = scale-parameters.min_scale; - cv::Mat rotated,filtered_h,filtered_v; - int diag = int(sqrt(gray_image.rows*gray_image.rows+gray_image.cols*gray_image.cols)); - cv::Size size(diag,diag); - int num = int(0.5001*M_PI/parameters.resolution); - std::vector images; - images.resize(2*num); - int scale_size = int(1+pow(2.0,scale+1+super_res)); - int scale_size2 = int((scale_size/10)*2+1); - for(int i=0;i images; + images.resize(2*num); + int scale_size = int(1+pow(2.0,scale+1+super_res)); + int scale_size2 = int((scale_size/10)*2+1); + for(int i=0;i& keypoints,std::vector &feature_maps,const cv::Mat &mask)const @@ -1865,6 +1869,7 @@ cv::Point2f &Chessboard::Board::getCorner(int _row,int _col) }while(_row); } CV_Error(Error::StsInternal,"cannot find corner"); + // return *top_left->top_left; // never reached } bool Chessboard::Board::isCellBlack(int row,int col)const @@ -3008,11 +3013,6 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector &f #endif CV_CheckTypeEQ(gray.type(),CV_8UC1, "Unsupported image type"); - //TODO is this needed? - // double min,max; - // cv::minMaxLoc(gray,&min,&max); - // gray = (gray-min)*(255.0/(max-min)); - cv::Size chessboard_size2(parameters.chessboard_size.height,parameters.chessboard_size.width); std::vector keypoints_seed; std::vector > angles; @@ -3025,17 +3025,15 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector &f std::vector::const_iterator seed_iter = keypoints_seed.begin(); int count = 0; int inum = chessboard_size2.width*chessboard_size2.height; - for(;seed_iter != keypoints_seed.end();++seed_iter) + for(;seed_iter != keypoints_seed.end() && count < inum;++seed_iter,++count) { - if(fabs(seed_iter->response) > response) + // points are sorted based on response + if(fabs(seed_iter->response) < response) { - ++count; - if(count >= inum) - break; + seed_iter = keypoints_seed.end(); + return Chessboard::Board(); } } - if(seed_iter == keypoints_seed.end()) - return Chessboard::Board(); // just add dummy points or flann will fail during knnSearch if(keypoints_seed.size() < 21) keypoints_seed.resize(21, cv::KeyPoint(-99999.0F,-99999.0F,0.0F,0.0F,0.0F)); @@ -3070,60 +3068,71 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector &f std::vector boards; generateBoards(flann_index, data,*points_iter,white_angle,black_angle,min_response,gray,boards); - std::vector::iterator iter_boards = boards.begin(); - for(;iter_boards != boards.end();++iter_boards) - { - cv::Mat h = iter_boards->estimateHomography(); - int size = iter_boards->validateCorners(data,flann_index,h,min_response); - if(size != 9) - continue; - if(!iter_boards->validateContour()) - continue; - //grow based on kd-tree - iter_boards->grow(data,flann_index); - if(!iter_boards->checkUnique()) - continue; - - // check bounding box - std::vector contour = iter_boards->getContour(); - std::vector::const_iterator iter = contour.begin(); - for(;iter != contour.end();++iter) + parallel_for_(Range(0,(int)boards.size()),[&](const Range& range){ + for(int i=range.start;i estimateHomography(); + int size = iter_boards->validateCorners(data,flann_index,h,min_response); + if(size != 9 || !iter_boards->validateContour()) + { + iter_boards->clear(); + continue; + } + //grow based on kd-tree + iter_boards->grow(data,flann_index); + if(!iter_boards->checkUnique()) + { + iter_boards->clear(); + continue; + } - if(iter_boards->getSize() == parameters.chessboard_size || - iter_boards->getSize() == chessboard_size2) - { - iter_boards->normalizeOrientation(false); - if(iter_boards->getSize() != parameters.chessboard_size) + // check bounding box + std::vector contour = iter_boards->getContour(); + std::vector::const_iterator iter = contour.begin(); + for(;iter != contour.end();++iter) + { + if(!bounding_box.contains(*iter)) + break; + } + if(iter != contour.end()) { - if(iter_boards->isCellBlack(0,0) == iter_boards->isCellBlack(0,int(iter_boards->colCount())-1)) - iter_boards->rotateLeft(); - else - iter_boards->rotateRight(); + iter_boards->clear(); + continue; } + + if(iter_boards->getSize() == parameters.chessboard_size || + iter_boards->getSize() == chessboard_size2) + { + iter_boards->normalizeOrientation(false); + if(iter_boards->getSize() != parameters.chessboard_size) + { + if(iter_boards->isCellBlack(0,0) == iter_boards->isCellBlack(0,int(iter_boards->colCount())-1)) + iter_boards->rotateLeft(); + else + iter_boards->rotateRight(); + } #ifdef CV_DETECTORS_CHESSBOARD_DEBUG - cv::Mat img; - iter_boards->draw(debug_image,img); - cv::imshow("chessboard",img); - cv::waitKey(-1); + cv::Mat img; + iter_boards->draw(debug_image,img); + cv::imshow("chessboard",img); + cv::waitKey(-1); #endif - return *iter_boards; - } - else - { - if(iter_boards->getSize().width*iter_boards->getSize().height > chessboard_size2.width*chessboard_size2.height) + } + else { - if(parameters.larger) - return *iter_boards; - else - return Chessboard::Board(); + if(iter_boards->getSize().width*iter_boards->getSize().height < chessboard_size2.width*chessboard_size2.height) + iter_boards->clear(); + else if(!parameters.larger) + iter_boards->clear(); } } + }); + // check if a good board was found + for(const auto &board : boards) + { + if(!board.isEmpty()) + return board; } } return Chessboard::Board(); @@ -3175,24 +3184,32 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, details::Chessboard::Parameters para; para.chessboard_size = pattern_size; + para.min_scale = 2; + para.max_scale = 4; + para.max_tests = 25; + para.max_points = std::max(100,pattern_size.width*pattern_size.height*2); + para.super_resolution = false; - switch(flags) + // setup search based on flags + if(flags & CALIB_CB_NORMALIZE_IMAGE) + { + cv::equalizeHist(img,img); + flags ^= CALIB_CB_NORMALIZE_IMAGE; + } + if(flags & CALIB_CB_EXHAUSTIVE) { - case 1: // high accuracy profile - para.min_scale = 2; - para.max_scale = 4; para.max_tests = 100; - para.super_resolution = true; para.max_points = std::max(500,pattern_size.width*pattern_size.height*2); - break; - default: // default profile - para.min_scale = 2; - para.max_scale = 3; - para.max_tests = 20; - para.max_points = pattern_size.width*pattern_size.height*2; - para.super_resolution = false; - break; + flags ^= CALIB_CB_EXHAUSTIVE; + } + if(flags & CALIB_CB_ACCURACY) + { + para.super_resolution = true; + flags ^= CALIB_CB_ACCURACY; } + if(flags) + CV_Error(Error::StsOutOfRange, cv::format("Invalid remaing flags %d", (int)flags)); + std::vector corners; details::Chessboard board(para); board.detect(img,corners);