Commit 8811dabb authored by Alexander Duda's avatar Alexander Duda Committed by Alexander Alekhin

Merge pull request #12615 from D-Alex:master

findChessboardCornersSB: speed improvements (#12615)

* chessboard: fix do not modify const image

* chessboard: speed up scale space using parallel_for

* chessboard: small improvements

* chessboard: speed up board growing using parallel_for

* chessboard: add flags for tuning detection

* chessboard: fix compiler warnings

* chessborad: change flag name to CALIB_CB_EXHAUSTIVE

This also fixes a typo

* chessboard: fix const ref + remove to_string
parent efca746d
...@@ -244,7 +244,9 @@ enum { SOLVEPNP_ITERATIVE = 0, ...@@ -244,7 +244,9 @@ enum { SOLVEPNP_ITERATIVE = 0,
enum { CALIB_CB_ADAPTIVE_THRESH = 1, enum { CALIB_CB_ADAPTIVE_THRESH = 1,
CALIB_CB_NORMALIZE_IMAGE = 2, CALIB_CB_NORMALIZE_IMAGE = 2,
CALIB_CB_FILTER_QUADS = 4, 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, enum { CALIB_CB_SYMMETRIC_GRID = 1,
...@@ -847,7 +849,11 @@ CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, Out ...@@ -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 @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) ). ( patternSize = cv::Size(points_per_row,points_per_colum) = cv::Size(columns,rows) ).
@param corners Output array of detected corners. @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 The function is analog to findchessboardCorners but uses a localized radon
transformation approximated by box filters being more robust to all sort of transformation approximated by box filters being more robust to all sort of
......
...@@ -24,7 +24,7 @@ namespace details { ...@@ -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 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 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_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 ELLIPSE_WIDTH = 0.35F; // width of the search ellipse in percentage of its length
const float RAD2DEG = float(180.0/M_PI); 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) const int MAX_SYMMETRY_ERRORS = 5; // maximal number of failures during point symmetry test (filtering out lines)
...@@ -602,25 +602,29 @@ void FastX::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vecto ...@@ -602,25 +602,29 @@ void FastX::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vecto
return; return;
} }
void FastX::detectImpl(const cv::Mat& gray_image, void FastX::detectImpl(const cv::Mat& _gray_image,
std::vector<cv::Mat> &rotated_images, std::vector<cv::Mat> &rotated_images,
std::vector<cv::Mat> &feature_maps, std::vector<cv::Mat> &feature_maps,
const cv::Mat &_mask)const const cv::Mat &_mask)const
{ {
if(!_mask.empty()) if(!_mask.empty())
CV_Error(Error::StsBadSize, "Mask is not supported"); 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 // up-sample if needed
cv::Mat gray_image;
int super_res = int(parameters.super_resolution); int super_res = int(parameters.super_resolution);
if(super_res) 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 //for each scale
int num_scales = parameters.max_scale-parameters.min_scale+1; int num_scales = parameters.max_scale-parameters.min_scale+1;
rotated_images.resize(num_scales); rotated_images.resize(num_scales);
feature_maps.resize(num_scales); feature_maps.resize(num_scales);
for(int scale=parameters.min_scale;scale <= parameters.max_scale;++scale) parallel_for_(Range(parameters.min_scale,parameters.max_scale+1),[&](const Range& range){
for(int scale=range.start;scale < range.end;++scale)
{ {
// calc images // calc images
// for each angle step // for each angle step
...@@ -648,7 +652,6 @@ void FastX::detectImpl(const cv::Mat& gray_image, ...@@ -648,7 +652,6 @@ void FastX::detectImpl(const cv::Mat& gray_image,
// calc feature map // calc feature map
calcFeatureMap(rotated_images[scale_id],feature_maps[scale_id]); calcFeatureMap(rotated_images[scale_id],feature_maps[scale_id]);
// filter feature map to improve impulse responses // filter feature map to improve impulse responses
if(parameters.filter) if(parameters.filter)
{ {
...@@ -659,6 +662,7 @@ void FastX::detectImpl(const cv::Mat& gray_image, ...@@ -659,6 +662,7 @@ void FastX::detectImpl(const cv::Mat& gray_image,
feature_maps[scale_id] = high-0.8*low; feature_maps[scale_id] = high-0.8*low;
} }
} }
});
} }
void FastX::detectImpl(const cv::Mat& image,std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat &mask)const void FastX::detectImpl(const cv::Mat& image,std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat &mask)const
...@@ -1865,6 +1869,7 @@ cv::Point2f &Chessboard::Board::getCorner(int _row,int _col) ...@@ -1865,6 +1869,7 @@ cv::Point2f &Chessboard::Board::getCorner(int _row,int _col)
}while(_row); }while(_row);
} }
CV_Error(Error::StsInternal,"cannot find corner"); CV_Error(Error::StsInternal,"cannot find corner");
// return *top_left->top_left; // never reached
} }
bool Chessboard::Board::isCellBlack(int row,int col)const bool Chessboard::Board::isCellBlack(int row,int col)const
...@@ -3008,11 +3013,6 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f ...@@ -3008,11 +3013,6 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f
#endif #endif
CV_CheckTypeEQ(gray.type(),CV_8UC1, "Unsupported image type"); 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); cv::Size chessboard_size2(parameters.chessboard_size.height,parameters.chessboard_size.width);
std::vector<KeyPoint> keypoints_seed; std::vector<KeyPoint> keypoints_seed;
std::vector<std::vector<float> > angles; std::vector<std::vector<float> > angles;
...@@ -3025,17 +3025,15 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f ...@@ -3025,17 +3025,15 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f
std::vector<KeyPoint>::const_iterator seed_iter = keypoints_seed.begin(); std::vector<KeyPoint>::const_iterator seed_iter = keypoints_seed.begin();
int count = 0; int count = 0;
int inum = chessboard_size2.width*chessboard_size2.height; 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; seed_iter = keypoints_seed.end();
if(count >= inum) return Chessboard::Board();
break;
} }
} }
if(seed_iter == keypoints_seed.end())
return Chessboard::Board();
// just add dummy points or flann will fail during knnSearch // just add dummy points or flann will fail during knnSearch
if(keypoints_seed.size() < 21) if(keypoints_seed.size() < 21)
keypoints_seed.resize(21, cv::KeyPoint(-99999.0F,-99999.0F,0.0F,0.0F,0.0F)); keypoints_seed.resize(21, cv::KeyPoint(-99999.0F,-99999.0F,0.0F,0.0F,0.0F));
...@@ -3070,19 +3068,24 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f ...@@ -3070,19 +3068,24 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f
std::vector<Board> boards; std::vector<Board> boards;
generateBoards(flann_index, data,*points_iter,white_angle,black_angle,min_response,gray,boards); generateBoards(flann_index, data,*points_iter,white_angle,black_angle,min_response,gray,boards);
std::vector<Chessboard::Board>::iterator iter_boards = boards.begin(); parallel_for_(Range(0,(int)boards.size()),[&](const Range& range){
for(;iter_boards != boards.end();++iter_boards) for(int i=range.start;i <range.end;++i)
{ {
auto iter_boards = boards.begin()+i;
cv::Mat h = iter_boards->estimateHomography(); cv::Mat h = iter_boards->estimateHomography();
int size = iter_boards->validateCorners(data,flann_index,h,min_response); int size = iter_boards->validateCorners(data,flann_index,h,min_response);
if(size != 9) if(size != 9 || !iter_boards->validateContour())
continue; {
if(!iter_boards->validateContour()) iter_boards->clear();
continue; continue;
}
//grow based on kd-tree //grow based on kd-tree
iter_boards->grow(data,flann_index); iter_boards->grow(data,flann_index);
if(!iter_boards->checkUnique()) if(!iter_boards->checkUnique())
{
iter_boards->clear();
continue; continue;
}
// check bounding box // check bounding box
std::vector<cv::Point2f> contour = iter_boards->getContour(); std::vector<cv::Point2f> contour = iter_boards->getContour();
...@@ -3093,7 +3096,10 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f ...@@ -3093,7 +3096,10 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f
break; break;
} }
if(iter != contour.end()) if(iter != contour.end())
{
iter_boards->clear();
continue; continue;
}
if(iter_boards->getSize() == parameters.chessboard_size || if(iter_boards->getSize() == parameters.chessboard_size ||
iter_boards->getSize() == chessboard_size2) iter_boards->getSize() == chessboard_size2)
...@@ -3112,18 +3118,21 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f ...@@ -3112,18 +3118,21 @@ Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &f
cv::imshow("chessboard",img); cv::imshow("chessboard",img);
cv::waitKey(-1); cv::waitKey(-1);
#endif #endif
return *iter_boards;
} }
else else
{ {
if(iter_boards->getSize().width*iter_boards->getSize().height > chessboard_size2.width*chessboard_size2.height) if(iter_boards->getSize().width*iter_boards->getSize().height < chessboard_size2.width*chessboard_size2.height)
{ iter_boards->clear();
if(parameters.larger) else if(!parameters.larger)
return *iter_boards; iter_boards->clear();
else
return Chessboard::Board();
} }
} }
});
// check if a good board was found
for(const auto &board : boards)
{
if(!board.isEmpty())
return board;
} }
} }
return Chessboard::Board(); return Chessboard::Board();
...@@ -3175,24 +3184,32 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, ...@@ -3175,24 +3184,32 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size,
details::Chessboard::Parameters para; details::Chessboard::Parameters para;
para.chessboard_size = pattern_size; para.chessboard_size = pattern_size;
switch(flags)
{
case 1: // high accuracy profile
para.min_scale = 2; para.min_scale = 2;
para.max_scale = 4; 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;
// 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)
{
para.max_tests = 100; para.max_tests = 100;
para.super_resolution = true;
para.max_points = std::max(500,pattern_size.width*pattern_size.height*2); para.max_points = std::max(500,pattern_size.width*pattern_size.height*2);
break; flags ^= CALIB_CB_EXHAUSTIVE;
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;
} }
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<cv::KeyPoint> corners; std::vector<cv::KeyPoint> corners;
details::Chessboard board(para); details::Chessboard board(para);
board.detect(img,corners); board.detect(img,corners);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment