mirror of
https://github.com/opencv/opencv.git
synced 2024-11-30 14:29:49 +08:00
Merge pull request #12668 from alalek:calib3d_chessboardsb_updates
This commit is contained in:
commit
ae4c72a153
@ -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
|
||||
|
@ -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 ***** */
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user