From e4f71994c2a16f0246258feec179ab4264a3c076 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 27 Sep 2018 15:01:51 +0300 Subject: [PATCH] calib3d: findChessboardCornersSB() minor updates - avoid updating of input image during equalizeHist() call - avoid for() with double variable (use 'int' instead) - more CV_Check*() macros - use Mat_, Matx - static for local variables --- modules/calib3d/src/chessboard.cpp | 119 ++++++++++---------- modules/calib3d/test/test_chesscorners.cpp | 16 ++- modules/core/include/opencv2/core/check.hpp | 2 + modules/core/src/check.cpp | 8 ++ 4 files changed, 81 insertions(+), 64 deletions(-) diff --git a/modules/calib3d/src/chessboard.cpp b/modules/calib3d/src/chessboard.cpp index 8f7e861d0b..240ef5262c 100644 --- a/modules/calib3d/src/chessboard.cpp +++ b/modules/calib3d/src/chessboard.cpp @@ -10,7 +10,7 @@ //#define CV_DETECTORS_CHESSBOARD_DEBUG #ifdef CV_DETECTORS_CHESSBOARD_DEBUG #include -cv::Mat debug_image; +static cv::Mat debug_image; #endif using namespace std; @@ -21,19 +21,19 @@ namespace details { ///////////////////////////////////////////////////////////////////////////// // magic numbers used for chessboard corner detection ///////////////////////////////////////////////////////////////////////////// -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*CV_PI); // max angle between line segments supposed to be straight -const float MIN_COS_ANGLE = float(cos(35.0/180*CV_PI)); // min cos angle between board edges -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/CV_PI); -const int MAX_SYMMETRY_ERRORS = 5; // maximal number of failures during point symmetry test (filtering out lines) +static const float CORNERS_SEARCH = 0.5F; // percentage of the edge length to the next corner used to find new corners +static const float MAX_ANGLE = float(48.0/180.0*CV_PI); // max angle between line segments supposed to be straight +static const float MIN_COS_ANGLE = float(cos(35.0/180*CV_PI)); // min cos angle between board edges +static const float MIN_RESPONSE_RATIO = 0.1F; +static const float ELLIPSE_WIDTH = 0.35F; // width of the search ellipse in percentage of its length +static const float RAD2DEG = float(180.0/CV_PI); +static const int MAX_SYMMETRY_ERRORS = 5; // maximal number of failures during point symmetry test (filtering out lines) ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // some helper methods static bool isPointOnLine(cv::Point2f l1,cv::Point2f l2,cv::Point2f pt,float min_angle); -static int testPointSymmetry(cv::Mat mat,cv::Point2f pt,float dist,float max_error); +static int testPointSymmetry(const cv::Mat& mat,cv::Point2f pt,float dist,float max_error); static float calcSubpixel(const float &x_l,const float &x,const float &x_r); static float calcSubPos(const float &x_l,const float &x,const float &x_r); static void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order); @@ -60,26 +60,23 @@ void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray CV_Error(Error::StsBadArg, "all given points are identical"); double scale = 1.0/mean_dist; + // generate transformation - _T.create(2,2,CV_64FC1); - cv::Mat T = _T.getMat(); - T.at(0,0) = scale; - T.at(0,1) = -scale*centroid; - T.at(1,0) = 0; - T.at(1,1) = 1; + cv::Matx22d Tx( + scale, -scale*centroid, + 0, 1 + ); + Mat(Tx, false).copyTo(_T); // calc normalized points; - cv::Matx22d Tx(T); _new_points.create(points.rows,1,points.type()); new_points = _new_points.getMat(); - cv::Vec2d p; switch(points.type()) { case CV_32FC1: for(int i=0;i < points.rows;++i) { - p(0) = points.at(i); - p(1) = 1.0; + cv::Vec2d p(points.at(i), 1.0); p = Tx*p; new_points.at(i) = float(p(0)/p(1)); } @@ -87,8 +84,7 @@ void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray case CV_64FC1: for(int i=0;i < points.rows;++i) { - p(0) = points.at(i); - p(1) = 1.0; + cv::Vec2d p(points.at(i), 1.0); p = Tx*p; new_points.at(i) = p(0)/p(1); } @@ -107,8 +103,7 @@ cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst) src = src.reshape(1,src.cols); if(dst.cols > 1 && dst.rows == 1) dst = dst.reshape(1,dst.cols); - if(src.rows != dst.rows) - CV_Error(Error::StsBadArg, "size mismatch"); + CV_CheckEQ(src.rows, dst.rows, "size mismatch"); CV_CheckChannelsEQ(src.channels(), 1, "data with only one channel are supported"); CV_CheckChannelsEQ(dst.channels(), 1, "data with only one channel are supported"); CV_CheckTypeEQ(src.type(), dst.type(), "src and dst must have the same type"); @@ -163,10 +158,10 @@ cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst) y.at(i) = b_.at(i)/d.at(i); cv::Mat x = vt.t()*y; - cv::Mat H = (cv::Mat_(2,2) << x.at(0), x.at(1), x.at(2), 1.0); + cv::Matx22d H_(x.at(0), x.at(1), x.at(2), 1.0); // denormalize - H = dst_T.inv()*H*src_T; + Mat H = dst_T.inv()*Mat(H_, false)*src_T; // enforce frobeniusnorm of one double scale = 1.0/cv::norm(H); @@ -177,8 +172,8 @@ void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order) int npoints = src_x.checkVector(1); int nypoints = src_y.checkVector(1); CV_Assert(npoints == nypoints && npoints >= order+1); - Mat srcX = Mat_(src_x), srcY = Mat_(src_y); - Mat A = Mat_::ones(npoints,order + 1); + Mat_ srcX(src_x), srcY(src_y); + Mat_ A = Mat_::ones(npoints,order + 1); // build A matrix for (int y = 0; y < npoints; ++y) { @@ -187,7 +182,7 @@ void polyfit(const Mat& src_x, const Mat& src_y, Mat& dst, int order) } cv::Mat w; solve(A,srcY,w,DECOMP_SVD); - w.convertTo(dst,std::max(std::max(src_x.depth(), src_y.depth()), CV_32F)); + w.convertTo(dst, ((src_x.depth() == CV_64F || src_y.depth() == CV_64F) ? CV_64F : CV_32F)); } float calcSignedDistance(const cv::Vec2f &n,const cv::Point2f &a,const cv::Point2f &pt) @@ -207,15 +202,16 @@ bool isPointOnLine(cv::Point2f l1,cv::Point2f l2,cv::Point2f pt,float min_angle) } // returns how many tests fails out of 10 -int testPointSymmetry(cv::Mat mat,cv::Point2f pt,float dist,float max_error) +int testPointSymmetry(const cv::Mat& mat,cv::Point2f pt,float dist,float max_error) { cv::Rect image_rect(int(0.5*dist),int(0.5*dist),int(mat.cols-0.5*dist),int(mat.rows-0.5*dist)); cv::Size size(int(0.5*dist),int(0.5*dist)); int count = 0; cv::Mat patch1,patch2; cv::Point2f center1,center2; - for(double angle=0;angle <= CV_PI;angle+=CV_PI*0.1) + for (int angle_i = 0; angle_i < 10; angle_i++) { + double angle = angle_i * (CV_PI * 0.1); cv::Point2f n(float(cos(angle)),float(-sin(angle))); center1 = pt+dist*n; if(!image_rect.contains(center1)) @@ -284,8 +280,7 @@ void FastX::rotate(float angle,const cv::Mat &img,cv::Size size,cv::Mat &out)con } else { - cv::Mat m = cv::getRotationMatrix2D(cv::Point2f(float(img.cols*0.5),float(img.rows*0.5)),float(angle/CV_PI*180),1); - CV_Assert(m.type() == CV_64FC1); + cv::Mat_ m = cv::getRotationMatrix2D(cv::Point2f(float(img.cols*0.5),float(img.rows*0.5)),float(angle/CV_PI*180),1); m.at(0,2) += 0.5*(size.width-img.cols); m.at(1,2) += 0.5*(size.height-img.rows); cv::warpAffine(img,out,m,size); @@ -488,10 +483,12 @@ void FastX::findKeyPoints(const std::vector &feature_maps, std::vector< { //TODO check that all feature_maps have the same size int num_scales = parameters.max_scale-parameters.min_scale; - if(int(feature_maps.size()) < num_scales) - CV_Error(Error::StsBadArg,"missing feature maps"); - if(_mask.data && (_mask.type() != CV_8UC1 || _mask.size() != feature_maps.front().size())) - CV_Error(Error::StsBadMask,"wrong mask type or size"); + CV_CheckGE(int(feature_maps.size()), num_scales, "missing feature maps"); + if (!_mask.empty()) + { + CV_CheckTypeEQ(_mask.type(), CV_8UC1, "wrong mask type"); + CV_CheckEQ(_mask.size(), feature_maps.front().size(),"wrong mask type or size"); + } keypoints.clear(); cv::Mat mask; @@ -512,10 +509,10 @@ void FastX::findKeyPoints(const std::vector &feature_maps, std::vector< cv::Mat src; for(int scale=parameters.max_scale;scale>=parameters.min_scale;--scale) { - int window_size = int(pow(2.0,scale+super_res)+1); + int window_size = (1 << (scale + super_res)) + 1; float window_size2 = 0.5F*window_size; float window_size4 = 0.25F*window_size; - int window_size2i = int(round(window_size2)); + int window_size2i = cvRound(window_size2); const cv::Mat &feature_map = feature_maps[scale-parameters.min_scale]; int y = ((feature_map.rows)/window_size)-6; @@ -1082,10 +1079,8 @@ const cv::Point2f* Chessboard::Board::PointIter::operator*()const return cell->bottom_right; case BOTTOM_LEFT: return cell->bottom_left; - default: - CV_Assert(false); } - return NULL; + CV_Assert(false); } const cv::Point2f* Chessboard::Board::PointIter::operator->()const @@ -1256,10 +1251,8 @@ void Chessboard::Board::draw(cv::InputArray m,cv::OutputArray out,cv::InputArray bool Chessboard::Board::estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const { cv::Mat K = _K.getMat(); - if(K.type() != CV_64FC1) - throw std::runtime_error("wrong K type"); - if(K.rows != 3|| K.cols != 3) - throw std::runtime_error("wrong K size"); + CV_CheckTypeEQ(K.type(), CV_64FC1, "wrong K type"); + CV_CheckEQ(K.size(), Size(3, 3), "wrong K size"); if(isEmpty()) return false; @@ -1520,8 +1513,9 @@ void Chessboard::Board::flipVertical() float Chessboard::Board::findMaxPoint(cv::flann::Index &index,const cv::Mat &data,const Ellipse &ellipse,float white_angle,float black_angle,cv::Point2f &point) { // flann data type enriched with angles (third column) - if(data.type() != CV_32FC1 || data.cols != 4) - CV_Error(Error::StsBadArg,"type of flann data is not supported. Expect CV_32FC1"); + CV_CheckType(data.type(), CV_32FC1, "type of flann data is not supported"); + CV_CheckEQ(data.cols, 4, "4-cols flann data is expected"); + std::vector query,dists; std::vector indices; query.resize(2); @@ -1540,15 +1534,15 @@ float Chessboard::Board::findMaxPoint(cv::flann::Index &index,const cv::Mat &dat if(response < best_score) continue; const float &a0 = *(val+2); - float a1 = fabs(a0-white_angle); - float a2 = fabs(a0-black_angle); + float a1 = std::fabs(a0-white_angle); + float a2 = std::fabs(a0-black_angle); if(a1 > CV_PI*0.5) - a1= float(fabs(a1-CV_PI)); + a1 = std::fabs(float(a1-CV_PI)); if(a2> CV_PI*0.5) - a2= float(fabs(a2-CV_PI)); + a2 = std::fabs(float(a2-CV_PI)); if(a1 < MAX_ANGLE || a2 < MAX_ANGLE ) { - cv::Point2f pt(*val,*(val+1)); + cv::Point2f pt(val[0], val[1]); if(point.x != point.x) // NaN check point = pt; if(best_score < response && ellipse.contains(pt)) @@ -3161,16 +3155,15 @@ void Chessboard::detectImpl(InputArray image, std::vector& keypoints, detectImpl(image.getMat(),keypoints,mask.getMat()); } -}} // end namespace details and cv +} // end namespace details // public API -bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, - cv::OutputArray corners_, int flags) +bool findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, + cv::OutputArray corners_, int flags) { CV_INSTRUMENT_REGION(); int type = image_.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type); - Mat img = image_.getMat(); CV_CheckType(type, depth == CV_8U && (cn == 1 || cn == 3), "Only 8-bit grayscale or color images are supported"); if(pattern_size.width <= 2 || pattern_size.height <= 2) @@ -3179,8 +3172,12 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, } if (!corners_.needed()) CV_Error(Error::StsNullPtr, "Null pointer to corners"); - if (img.channels() != 1) - cvtColor(img, img, COLOR_BGR2GRAY); + + Mat img; + if (image_.channels() != 1) + cvtColor(image_, img, COLOR_BGR2GRAY); + else + img = image_.getMat(); details::Chessboard::Parameters para; para.chessboard_size = pattern_size; @@ -3193,7 +3190,9 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, // setup search based on flags if(flags & CALIB_CB_NORMALIZE_IMAGE) { - cv::equalizeHist(img,img); + Mat tmp; + cv::equalizeHist(img, tmp); + swap(img, tmp); flags ^= CALIB_CB_NORMALIZE_IMAGE; } if(flags & CALIB_CB_EXHAUSTIVE) @@ -3223,3 +3222,5 @@ bool cv::findChessboardCornersSB(cv::InputArray image_, cv::Size pattern_size, Mat(points).copyTo(corners_); return true; } + +} // namespace cv diff --git a/modules/calib3d/test/test_chesscorners.cpp b/modules/calib3d/test/test_chesscorners.cpp index 2e65cd6c1e..cc947202c9 100644 --- a/modules/calib3d/test/test_chesscorners.cpp +++ b/modules/calib3d/test/test_chesscorners.cpp @@ -409,6 +409,8 @@ bool CV_ChessboardDetectorTest::checkByGenerator() int progress = 0; for(int i = 0; i < test_num; ++i) { + SCOPED_TRACE(cv::format("test_num=%d", test_num)); + progress = update_progress( progress, i, test_num, 0 ); ChessBoardGenerator cbg(sizes[i % sizes_num]); @@ -439,13 +441,17 @@ bool CV_ChessboardDetectorTest::checkByGenerator() } double err = calcErrorMinError(cbg.cornersSize(), corners_found, corners_generated); - if( err > rough_success_error_level ) + EXPECT_LE(err, rough_success_error_level) << "bad accuracy of corner guesses"; +#if 0 + if (err >= rough_success_error_level) { - ts->printf( cvtest::TS::LOG, "bad accuracy of corner guesses" ); - ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); - res = false; - return res; + imshow("cb", cb); + Mat cb_corners = cb.clone(); + cv::drawChessboardCorners(cb_corners, cbg.cornersSize(), Mat(corners_found), found); + imshow("corners", cb_corners); + waitKey(0); } +#endif } /* ***** negative ***** */ diff --git a/modules/core/include/opencv2/core/check.hpp b/modules/core/include/opencv2/core/check.hpp index bf441383e8..604447e8d7 100644 --- a/modules/core/include/opencv2/core/check.hpp +++ b/modules/core/include/opencv2/core/check.hpp @@ -69,6 +69,7 @@ CV_EXPORTS void CV_NORETURN check_failed_auto(const int v1, const int v2, const CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v1, const size_t v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const float v1, const float v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const double v1, const double v2, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v1, const Size_ v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v1, const int v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v1, const int v2, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v1, const int v2, const CheckContext& ctx); @@ -77,6 +78,7 @@ CV_EXPORTS void CV_NORETURN check_failed_auto(const int v, const CheckContext& c CV_EXPORTS void CV_NORETURN check_failed_auto(const size_t v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const float v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_auto(const double v, const CheckContext& ctx); +CV_EXPORTS void CV_NORETURN check_failed_auto(const Size_ v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatDepth(const int v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatType(const int v, const CheckContext& ctx); CV_EXPORTS void CV_NORETURN check_failed_MatChannels(const int v, const CheckContext& ctx); diff --git a/modules/core/src/check.cpp b/modules/core/src/check.cpp index 676f755d1d..26efbb7541 100644 --- a/modules/core/src/check.cpp +++ b/modules/core/src/check.cpp @@ -113,6 +113,10 @@ void check_failed_auto(const double v1, const double v2, const CheckContext& ctx { check_failed_auto_(v1, v2, ctx); } +void check_failed_auto(const Size_ v1, const Size_ v2, const CheckContext& ctx) +{ + check_failed_auto_< Size_ >(v1, v2, ctx); +} template static CV_NORETURN @@ -163,6 +167,10 @@ void check_failed_auto(const double v, const CheckContext& ctx) { check_failed_auto_(v, ctx); } +void check_failed_auto(const Size_ v, const CheckContext& ctx) +{ + check_failed_auto_< Size_ >(v, ctx); +} }} // namespace