Merge pull request #12668 from alalek:calib3d_chessboardsb_updates

This commit is contained in:
Alexander Alekhin 2018-10-01 00:00:01 +03:00 committed by GitHub
commit ae4c72a153
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 64 deletions

View File

@ -10,7 +10,7 @@
//#define CV_DETECTORS_CHESSBOARD_DEBUG
#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
#include <opencv2/highgui.hpp>
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<double>(0,0) = scale;
T.at<double>(0,1) = -scale*centroid;
T.at<double>(1,0) = 0;
T.at<double>(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<float>(i);
p(1) = 1.0;
cv::Vec2d p(points.at<float>(i), 1.0);
p = Tx*p;
new_points.at<float>(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<double>(i);
p(1) = 1.0;
cv::Vec2d p(points.at<double>(i), 1.0);
p = Tx*p;
new_points.at<double>(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<double>(i) = b_.at<double>(i)/d.at<double>(i);
cv::Mat x = vt.t()*y;
cv::Mat H = (cv::Mat_<double>(2,2) << x.at<double>(0), x.at<double>(1), x.at<double>(2), 1.0);
cv::Matx22d H_(x.at<double>(0), x.at<double>(1), x.at<double>(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_<double>(src_x), srcY = Mat_<double>(src_y);
Mat A = Mat_<double>::ones(npoints,order + 1);
Mat_<double> srcX(src_x), srcY(src_y);
Mat_<double> A = Mat_<double>::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_<double> m = cv::getRotationMatrix2D(cv::Point2f(float(img.cols*0.5),float(img.rows*0.5)),float(angle/CV_PI*180),1);
m.at<double>(0,2) += 0.5*(size.width-img.cols);
m.at<double>(1,2) += 0.5*(size.height-img.rows);
cv::warpAffine(img,out,m,size);
@ -488,10 +483,12 @@ void FastX::findKeyPoints(const std::vector<cv::Mat> &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<cv::Mat> &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<float> query,dists;
std::vector<int> 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<KeyPoint>& 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

View File

@ -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 ***** */

View File

@ -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_<int> v1, const Size_<int> 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_<int> 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);

View File

@ -113,6 +113,10 @@ void check_failed_auto(const double v1, const double v2, const CheckContext& ctx
{
check_failed_auto_<double>(v1, v2, ctx);
}
void check_failed_auto(const Size_<int> v1, const Size_<int> v2, const CheckContext& ctx)
{
check_failed_auto_< Size_<int> >(v1, v2, ctx);
}
template<typename T> static CV_NORETURN
@ -163,6 +167,10 @@ void check_failed_auto(const double v, const CheckContext& ctx)
{
check_failed_auto_<double>(v, ctx);
}
void check_failed_auto(const Size_<int> v, const CheckContext& ctx)
{
check_failed_auto_< Size_<int> >(v, ctx);
}
}} // namespace