Merge remote-tracking branch 'upstream/3.4' into merge-3.4
authorAlexander Alekhin <alexander.a.alekhin@gmail.com>
Sun, 17 Nov 2019 22:51:16 +0000 (22:51 +0000)
committerAlexander Alekhin <alexander.alekhin@intel.com>
Mon, 18 Nov 2019 10:56:24 +0000 (13:56 +0300)
16 files changed:
1  2 
cmake/OpenCVCompilerOptions.cmake
cmake/OpenCVUtils.cmake
modules/calib3d/src/chessboard.cpp
modules/calib3d/src/chessboard.hpp
modules/calib3d/src/solvepnp.cpp
modules/calib3d/test/test_chesscorners.cpp
modules/core/include/opencv2/core/persistence.hpp
modules/core/src/persistence.cpp
modules/dnn/src/onnx/onnx_importer.cpp
modules/features2d/misc/java/test/FlannBasedDescriptorMatcherTest.java
modules/features2d/misc/java/test/ORBDescriptorExtractorTest.java
modules/highgui/src/window_gtk.cpp
modules/imgproc/test/test_filter.cpp
modules/js/test/tests.html
modules/ts/include/opencv2/ts.hpp
modules/ts/src/ts.cpp

Simple merge
Simple merge
index 00d3ff4,0000000..38051b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,3228 -1,0 +1,3219 @@@
- Ellipse::Ellipse(const Ellipse &other)
- {
-     center = other.center;
-     axes= other.axes;
-     angle= other.angle;
-     cosf = other.cosf;
-     sinf = other.sinf;
- }
 +// This file is part of OpenCV project.
 +// It is subject to the license terms in the LICENSE file found in the top-level directory
 +// of this distribution and at http://opencv.org/license.html.
 +
 +#include "precomp.hpp"
 +#include "opencv2/flann.hpp"
 +#include "chessboard.hpp"
 +#include "math.h"
 +
 +//#define CV_DETECTORS_CHESSBOARD_DEBUG
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +#include <opencv2/highgui.hpp>
 +static cv::Mat debug_image;
 +#endif
 +
 +using namespace std;
 +namespace cv {
 +namespace details {
 +
 +/////////////////////////////////////////////////////////////////////////////
 +/////////////////////////////////////////////////////////////////////////////
 +// magic numbers used for chessboard corner detection
 +/////////////////////////////////////////////////////////////////////////////
 +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(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);
 +static float calcSignedDistance(const cv::Vec2f &n,const cv::Point2f &a,const cv::Point2f &pt);
 +static void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray _new_points);
 +static cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst);
 +
 +void normalizePoints1D(cv::InputArray _points,cv::OutputArray _T,cv::OutputArray _new_points)
 +{
 +    cv::Mat points = _points.getMat();
 +    if(points.cols > 1 && points.rows == 1)
 +        points = points.reshape(1,points.cols);
 +    CV_CheckChannelsEQ(points.channels(), 1, "points must have only one channel");
 +
 +    // calc centroid
 +    double centroid= cv::mean(points)[0];
 +
 +    // shift origin to centroid
 +    cv::Mat new_points = points-centroid;
 +
 +    // calc mean distance
 +    double mean_dist = cv::mean(cv::abs(new_points))[0];
 +    if(mean_dist<= DBL_EPSILON)
 +        CV_Error(Error::StsBadArg, "all given points are identical");
 +    double scale = 1.0/mean_dist;
 +
 +
 +    // generate transformation
 +    cv::Matx22d Tx(
 +        scale, -scale*centroid,
 +        0,     1
 +    );
 +    Mat(Tx, false).copyTo(_T);
 +
 +    // calc normalized points;
 +    _new_points.create(points.rows,1,points.type());
 +    new_points = _new_points.getMat();
 +    switch(points.type())
 +    {
 +    case CV_32FC1:
 +        for(int i=0;i < points.rows;++i)
 +        {
 +            cv::Vec2d p(points.at<float>(i), 1.0);
 +            p = Tx*p;
 +            new_points.at<float>(i) = float(p(0)/p(1));
 +        }
 +        break;
 +    case CV_64FC1:
 +        for(int i=0;i < points.rows;++i)
 +        {
 +            cv::Vec2d p(points.at<double>(i), 1.0);
 +            p = Tx*p;
 +            new_points.at<double>(i) = p(0)/p(1);
 +        }
 +        break;
 +    default:
 +        CV_Error(Error::StsUnsupportedFormat, "unsupported point type");
 +    }
 +}
 +
 +cv::Mat findHomography1D(cv::InputArray _src,cv::InputArray _dst)
 +{
 +    // check inputs
 +    cv::Mat src = _src.getMat();
 +    cv::Mat dst = _dst.getMat();
 +    if(src.cols > 1 && src.rows == 1)
 +        src = src.reshape(1,src.cols);
 +    if(dst.cols > 1 && dst.rows == 1)
 +        dst = dst.reshape(1,dst.cols);
 +    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");
 +    CV_Check(src.rows, src.rows >= 3,"at least three point pairs are needed");
 +
 +    // normalize points
 +    cv::Mat src_T,dst_T, src_n,dst_n;
 +    normalizePoints1D(src,src_T,src_n);
 +    normalizePoints1D(dst,dst_T,dst_n);
 +
 +    int count = src_n.rows;
 +    cv::Mat A = cv::Mat::zeros(count,3,CV_64FC1);
 +    cv::Mat b = cv::Mat::zeros(count,1,CV_64FC1);
 +
 +    // fill A;b and perform singular value decomposition
 +    // it is assumed that w is one for both coordinates
 +    // h22 is kept to 1
 +    switch(src_n.type())
 +    {
 +    case CV_32FC1:
 +        for(int i=0;i<count;++i)
 +        {
 +            double s = src_n.at<float>(i);
 +            double d = dst_n.at<float>(i);
 +            A.at<double>(i,0) = s;
 +            A.at<double>(i,1) = 1.0;
 +            A.at<double>(i,2) = -s*d;
 +            b.at<double>(i) = d;
 +        }
 +        break;
 +    case CV_64FC1:
 +        for(int i=0;i<count;++i)
 +        {
 +            double s = src_n.at<double>(i);
 +            double d = dst_n.at<double>(i);
 +            A.at<double>(i,0) = s;
 +            A.at<double>(i,1) = 1.0;
 +            A.at<double>(i,2) = -s*d;
 +            b.at<double>(i) = d;
 +        }
 +        break;
 +    default:
 +        CV_Error(Error::StsUnsupportedFormat,"unsupported type");
 +    }
 +
 +    cv::Mat u,d,vt;
 +    cv::SVD::compute(A,d,u,vt);
 +    cv::Mat b_ = u.t()*b;
 +
 +    cv::Mat y(b_.rows,1,CV_64FC1);
 +    for(int i=0;i<b_.rows;++i)
 +        y.at<double>(i) = b_.at<double>(i)/d.at<double>(i);
 +
 +    cv::Mat x = vt.t()*y;
 +    cv::Matx22d H_(x.at<double>(0), x.at<double>(1), x.at<double>(2), 1.0);
 +
 +    // denormalize
 +    Mat H = dst_T.inv()*Mat(H_, false)*src_T;
 +
 +    // enforce frobeniusnorm of one
 +    double scale = cv::norm(H);
 +    CV_Assert(fabs(scale) > DBL_EPSILON);
 +    scale = 1.0 / scale;
 +    return H*scale;
 +}
 +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_<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)
 +    {
 +        for (int x = 1; x < A.cols; ++x)
 +            A.at<double>(y,x) = srcX.at<double>(y)*A.at<double>(y,x-1);
 +    }
 +    cv::Mat w;
 +    solve(A,srcY,w,DECOMP_SVD);
 +    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)
 +{
 +    cv::Vec3f v1(n[0],n[1],0);
 +    cv::Vec3f v2(pt.x-a.x,pt.y-a.y,0);
 +    return v1.cross(v2)[2];
 +}
 +
 +bool isPointOnLine(cv::Point2f l1,cv::Point2f l2,cv::Point2f pt,float min_angle)
 +{
 +    cv::Vec2f vec1(l1-pt);
 +    cv::Vec2f vec2(pt-l2);
 +    if(vec1.dot(vec2) < min_angle*cv::norm(vec1)*cv::norm(vec2))
 +        return false;
 +    return true;
 +}
 +
 +// returns how many tests fails out of 10
 +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 (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))
 +            return false;
 +        center2 = pt-dist*n;
 +        if(!image_rect.contains(center2))
 +            return false;
 +        cv::getRectSubPix(mat,size,center1,patch1);
 +        cv::getRectSubPix(mat,size,center2,patch2);
 +        if(fabs(cv::mean(patch1)[0]-cv::mean(patch2)[0]) > max_error)
 +            ++count;
 +    }
 +    return count;
 +}
 +
 +float calcSubpixel(const float &x_l,const float &x,const float &x_r)
 +{
 +    // prevent zero values
 +    if(x_l <= 0)
 +        return 0;
 +    if(x <= 0)
 +        return 0;
 +    if(x_r <= 0)
 +        return 0;
 +    const float l0 = float(std::log(x_l+1e-6));
 +    const float l1 = float(std::log(x+1e-6));
 +    const float l2 = float(std::log(x_r+1e-6));
 +    float delta = l2-l1-l1+l0;
 +    if(!delta) // this happens if all values are identical
 +        return 0;
 +    delta = (l0-l2)/(delta+delta);
 +    return delta;
 +}
 +
 +float calcSubPos(const float &x_l,const float &x,const float &x_r)
 +{
 +    float val = 2.0F *(x_l-2.0F*x+x_r);
 +    if(val == 0.0F)
 +        return 0.0F;
 +    val = (x_l-x_r)/val;
 +    if(val > 1.0F)
 +        return 1.0F;
 +    if(val < -1.0F)
 +        return -1.0F;
 +    return val;
 +}
 +
 +FastX::FastX(const Parameters &para)
 +{
 +    reconfigure(para);
 +}
 +
 +void FastX::reconfigure(const Parameters &para)
 +{
 +    CV_Check(para.min_scale, para.min_scale >= 0 && para.min_scale <= para.max_scale, "invalid scale");
 +    parameters = para;
 +}
 +
 +// rotates the image around its center
 +void FastX::rotate(float angle,const cv::Mat &img,cv::Size size,cv::Mat &out)const
 +{
 +    if(angle == 0)
 +    {
 +        out = img;
 +        return;
 +    }
 +    else
 +    {
 +        cv::Matx23d m = cv::getRotationMatrix2D(cv::Point2f(float(img.cols*0.5),float(img.rows*0.5)),float(angle/CV_PI*180),1);
 +        m(0,2) += 0.5*(size.width-img.cols);
 +        m(1,2) += 0.5*(size.height-img.rows);
 +        cv::warpAffine(img,out,m,size);
 +    }
 +}
 +
 +void FastX::calcFeatureMap(const Mat &images,Mat& out)const
 +{
 +    if(images.empty())
 +        CV_Error(Error::StsBadArg,"no rotation images");
 +    int type = images.type(), depth = CV_MAT_DEPTH(type);
 +    CV_CheckType(type,depth == CV_8U,
 +            "Only 8-bit grayscale or color images are supported");
 +    if(!images.isContinuous())
 +        CV_Error(Error::StsBadArg,"image must be continuous");
 +
 +    float signal,noise,rating;
 +    int count1;
 +    unsigned char val1,val2,val3;
 +    const unsigned char* wrap_around;
 +    const unsigned char* pend;
 +    const unsigned char* pimages = images.data;
 +    const int channels = images.channels();
 +    if(channels < 4)
 +        CV_Error(Error::StsBadArg,"images must have at least four channels");
 +
 +    // for each pixel
 +    out = cv::Mat::zeros(images.rows,images.cols,CV_32FC1);
 +    const float *pout_end = reinterpret_cast<const float*>(out.dataend);
 +    for(float *pout=out.ptr<float>(0,0);pout != pout_end;++pout)
 +    {
 +        //reset values
 +        rating = 0.0; count1 = 0;
 +        noise = 255; signal = 0;
 +
 +        //calc rating
 +        pend = pimages+channels;
 +        val1 = *(pend-1);                       // wrap around (last value)
 +        wrap_around = pimages++;                // store for wrap around (first value)
 +        val2 = *wrap_around;                    // first value
 +        for(;pimages != pend;++pimages)
 +        {
 +            val3 = *pimages;
 +            if(val1 <= val2)
 +            {
 +                if(val3 < val2) // maxima
 +                {
 +                    if(signal < val2)
 +                        signal = val2;
 +                    ++count1;
 +                }
 +            }
 +            else if(val1 > val2 && val3 >= val2) // minima
 +            {
 +                if(noise > val2)
 +                    noise = val2;
 +                ++count1;
 +            }
 +            val1 = val2;
 +            val2 = val3;
 +        }
 +        // wrap around
 +        if(val1 <= val2) // maxima
 +        {
 +            if(*wrap_around < val2)
 +            {
 +                if(signal < val2)
 +                    signal = val2;
 +                ++count1;
 +            }
 +        }
 +        else if(val1 > val2 && *wrap_around >= val2) // minima
 +        {
 +            if(noise > val2)
 +                noise = val2;
 +            ++count1;
 +        }
 +
 +        // store rating
 +        if(count1 == parameters.branches)
 +        {
 +            rating = signal-noise;
 +            *pout = rating*rating; //store rating in the feature map
 +        }
 +    }
 +}
 +
 +std::vector<std::vector<float> > FastX::calcAngles(const std::vector<cv::Mat> &rotated_images,std::vector<cv::KeyPoint> &keypoints)const
 +{
 +    // validate rotated_images
 +    if(rotated_images.empty())
 +        CV_Error(Error::StsBadArg,"no rotated images");
 +    std::vector<cv::Mat>::const_iterator iter = rotated_images.begin();
 +    for(;iter != rotated_images.end();++iter)
 +    {
 +        if(iter->empty())
 +            CV_Error(Error::StsBadArg,"empty rotated images");
 +        if(iter->channels() < 4)
 +            CV_Error(Error::StsBadArg,"rotated images must have at least four channels");
 +    }
 +
 +    // assuming all elements of the same channel
 +    const int channels = rotated_images.front().channels();
 +    int channels_1 = channels-1;
 +    float resolution = float(CV_PI/channels);
 +
 +    float angle;
 +    float val1,val2,val3,wrap_around;
 +    const unsigned char *pimages1,*pimages2,*pimages3,*pimages4;
 +    std::vector<std::vector<float> > angles;
 +    angles.resize(keypoints.size());
 +    float scale = float(parameters.super_resolution)+1.0F;
 +
 +    // for each keypoint
 +    std::vector<cv::KeyPoint>::iterator pt_iter = keypoints.begin();
 +    for(int id=0;pt_iter != keypoints.end();++pt_iter,++id)
 +    {
 +        int scale_id = pt_iter->octave - parameters.min_scale;
 +        if(scale_id>= int(rotated_images.size()) ||scale_id < 0)
 +            CV_Error(Error::StsBadArg,"no rotated image for requested keypoint octave");
 +        const cv::Mat &s_rotated_images = rotated_images[scale_id];
 +
 +        float x2 = pt_iter->pt.x*scale;
 +        float y2 = pt_iter->pt.y*scale;
 +        int row = int(y2);
 +        int col = int(x2);
 +        x2 -= col;
 +        y2 -= row;
 +        float x1 = 1.0F-x2; float y1 = 1.0F-y2;
 +        float a = x1*y1; float b = x2*y1; float c = x1*y2; float d = x2*y2;
 +        pimages1 = s_rotated_images.ptr<unsigned char>(row,col);
 +        pimages2 = s_rotated_images.ptr<unsigned char>(row,col+1);
 +        pimages3 = s_rotated_images.ptr<unsigned char>(row+1,col);
 +        pimages4 = s_rotated_images.ptr<unsigned char>(row+1,col+1);
 +        std::vector<float> &angles_i = angles[id];
 +
 +        //calc rating
 +        val1 = a**(pimages1+channels_1)+b**(pimages2+channels_1)+
 +            c**(pimages3+channels_1)+d**(pimages4+channels_1);        // wrap around (last value)
 +        wrap_around = a**(pimages1++)+b**(pimages2++)+c**(pimages3++)+d**(pimages4++); // first value
 +        val2 = wrap_around;                                                             // first value
 +        for(int i=0;i<channels-1;++pimages1,++pimages2,++pimages3,++pimages4,++i)
 +        {
 +            val3 = a**(pimages1)+b**(pimages2)+c**(pimages3)+d**(pimages4);
 +            if(val1 <= val2)
 +            {
 +                if(val3 < val2)
 +                {
 +                    angle = float((calcSubPos(val1,val2,val3)+i)*resolution);
 +                    if(angle < 0)
 +                        angle += float(CV_PI);
 +                    else if(angle > CV_PI)
 +                        angle -= float(CV_PI);
 +                    angles_i.push_back(angle);
 +                    pt_iter->angle = 360.0F-angle*RAD2DEG;
 +                }
 +            }
 +            else if(val1 > val2 && val3 >= val2)
 +            {
 +                angle = float((calcSubPos(val1,val2,val3)+i)*resolution);
 +                if(angle < 0)
 +                    angle += float(CV_PI);
 +                else if(angle > CV_PI)
 +                    angle -= float(CV_PI);
 +                angles_i.push_back(-angle);
 +                pt_iter->angle = 360.0F-angle*RAD2DEG;
 +            }
 +            val1 = val2;
 +            val2 = val3;
 +        }
 +        // wrap around
 +        if(val1 <= val2)
 +        {
 +            if(wrap_around< val2)
 +            {
 +                angle = float((calcSubPos(val1,val2,wrap_around)+channels-1)*resolution);
 +                if(angle < 0)
 +                    angle += float(CV_PI);
 +                else if(angle > CV_PI)
 +                    angle -= float(CV_PI);
 +                angles_i.push_back(angle);
 +                pt_iter->angle = 360.0F-angle*RAD2DEG;
 +            }
 +        }
 +        else if(val1 > val2 && wrap_around >= val2)
 +        {
 +            angle = float((calcSubPos(val1,val2,wrap_around)+channels-1)*resolution);
 +            if(angle < 0)
 +                angle += float(CV_PI);
 +            else if(angle > CV_PI)
 +                angle -= float(CV_PI);
 +            angles_i.push_back(-angle);
 +            pt_iter->angle = 360.0F-angle*RAD2DEG;
 +        }
 +    }
 +    return angles;
 +}
 +
 +void FastX::findKeyPoints(const std::vector<cv::Mat> &feature_maps, std::vector<KeyPoint>& keypoints,const Mat& _mask) const
 +{
 +    //TODO check that all feature_maps have the same size
 +    int num_scales = parameters.max_scale-parameters.min_scale;
 +    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;
 +    if(!_mask.empty())
 +        mask = _mask;
 +    else
 +        mask = cv::Mat::ones(feature_maps.front().size(),CV_8UC1);
 +
 +    int super_res = int(parameters.super_resolution);
 +    int super_scale = super_res+1;
 +    float super_comp = 0.25F*super_res;
 +
 +    // for each scale
 +    float strength = parameters.strength;
 +    std::vector<int> windows;
 +    cv::Point pt,pt2;
 +    double min,max;
 +    cv::Mat src;
 +    for(int scale=parameters.max_scale;scale>=parameters.min_scale;--scale)
 +    {
 +        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 = cvRound(window_size2);
 +
 +        const cv::Mat &feature_map = feature_maps[scale-parameters.min_scale];
 +        int y = ((feature_map.rows)/window_size)-6;
 +        int x = ((feature_map.cols)/window_size)-6;
 +        for(int row=5;row<y;++row)
 +        {
 +            for(int col=5;col<x;++col)
 +            {
 +                Rect rect(col*window_size,row*window_size,window_size,window_size);
 +                src = feature_map(rect);
 +                cv::minMaxLoc(src,&min,&max,NULL,&pt);
 +                if(min == max || max < strength)
 +                    continue;
 +
 +                cv::Point pos(pt.x+rect.x,pt.y+rect.y);
 +                if(mask.at<unsigned char>(pos.y,pos.x) == 0)
 +                    continue;
 +
 +                Rect rect2(int(pos.x-window_size2),int(pos.y-window_size2),window_size,window_size);
 +                src = feature_map(rect2);
 +                cv::minMaxLoc(src,NULL,NULL,NULL,&pt2);
 +                if(pos.x == pt2.x+rect2.x && pos.y == pt2.y+rect2.y)
 +                {
 +                    // the point is the best one on the current scale
 +                    // check all larger scales if there is a stronger one
 +                    double max2;
 +                    int scale2= scale-1;
 +                    //parameters.min_scale;
 +                    for(;scale2>=parameters.min_scale;--scale2)
 +                    {
 +                        cv::minMaxLoc(feature_maps[scale2-parameters.min_scale](rect),NULL,&max2,NULL,NULL);
 +                        if(max2 > max)
 +                            break;
 +                    }
 +                    if(scale2<parameters.min_scale && pos.x+1 < feature_map.cols && pos.y+1 < feature_map.rows)
 +                    {
 +                        float sub_x = float(calcSubpixel(feature_map.at<float>(pos.y,pos.x-1),
 +                                feature_map.at<float>(pos.y,pos.x),
 +                                feature_map.at<float>(pos.y,pos.x+1)));
 +                        float sub_y = float(calcSubpixel(feature_map.at<float>(pos.y-1,pos.x),
 +                                feature_map.at<float>(pos.y,pos.x),
 +                                feature_map.at<float>(pos.y+1,pos.x)));
 +                        cv::KeyPoint kpt(sub_x+pos.x,sub_y+pos.y,float(window_size),0.F,float(max),scale);
 +                        int x2 = std::max(0,int(kpt.pt.x-window_size4));
 +                        int y2 = std::max(0,int(kpt.pt.y-window_size4));
 +                        int w = std::min(int(mask.cols-x2),window_size2i);
 +                        int h = std::min(int(mask.rows-y2),window_size2i);
 +                        mask(cv::Rect(x2,y2,w,h)) = 0.0;
 +                        if(super_scale != 1)
 +                        {
 +                            kpt.pt.x /= super_scale;
 +                            kpt.pt.y /= super_scale;
 +                            kpt.pt.x -= super_comp;
 +                            kpt.pt.y -= super_comp;
 +                            kpt.size /= super_scale;
 +                        }
 +                        keypoints.push_back(kpt);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +void FastX::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vector<cv::KeyPoint>& keypoints,
 +        cv::OutputArray _descriptors,bool useProvidedKeyPoints)
 +{
 +    useProvidedKeyPoints = false;
 +    detectImpl(image.getMat(),keypoints,mask.getMat());
 +    if(!_descriptors.needed())
 +        return;
 +
 +    // generate descriptors based on their position
 +    _descriptors.create(int(keypoints.size()),2,CV_32FC1);
 +    cv::Mat descriptors = _descriptors.getMat();
 +    std::vector<cv::KeyPoint>::const_iterator iter = keypoints.begin();
 +    for(int row=0;iter != keypoints.end();++iter,++row)
 +    {
 +        descriptors.at<float>(row,0) = iter->pt.x;
 +        descriptors.at<float>(row,1) = iter->pt.y;
 +    }
 +    if(!useProvidedKeyPoints)        // suppress compiler warning
 +        return;
 +    return;
 +}
 +
 +void FastX::detectImpl(const cv::Mat& _gray_image,
 +        std::vector<cv::Mat> &rotated_images,
 +        std::vector<cv::Mat> &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");
 +
 +    // 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);
 +    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);
 +    parallel_for_(Range(parameters.min_scale,parameters.max_scale+1),[&](const Range& range){
 +        for(int scale=range.start;scale < range.end;++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*CV_PI/parameters.resolution);
 +            std::vector<cv::Mat> 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<num;++i)
 +            {
 +                float angle = parameters.resolution*i;
 +                rotate(-angle,gray_image,size,rotated);
 +                cv::blur(rotated,filtered_h,cv::Size(scale_size,scale_size2));
 +                cv::blur(rotated,filtered_v,cv::Size(scale_size2,scale_size));
 +
 +                // rotate filtered images back
 +                rotate(angle,filtered_h,gray_image.size(),images[i]);
 +                rotate(angle,filtered_v,gray_image.size(),images[i+num]);
 +            }
 +            cv::merge(images,rotated_images[scale_id]);
 +
 +            // calc feature map
 +            calcFeatureMap(rotated_images[scale_id],feature_maps[scale_id]);
 +            // filter feature map to improve impulse responses
 +            if(parameters.filter)
 +            {
 +                cv::Mat high,low;
 +                cv::blur(feature_maps[scale_id],low,cv::Size(scale_size,scale_size));
 +                int scale2 = int((scale_size/6))*2+1;
 +                cv::blur(feature_maps[scale_id],high,cv::Size(scale2,scale2));
 +                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
 +{
 +    std::vector<cv::Mat> rotated_images;
 +    detectImpl(image,rotated_images,feature_maps,mask);
 +    findKeyPoints(feature_maps,keypoints,mask);
 +}
 +
 +void FastX::detectImpl(InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask)const
 +{
 +    std::vector<cv::Mat> feature_maps;
 +    detectImpl(image.getMat(),keypoints,feature_maps,mask.getMat());
 +}
 +
 +void FastX::detectImpl(const Mat& src, std::vector<KeyPoint>& keypoints, const Mat& mask)const
 +{
 +    std::vector<cv::Mat> feature_maps;
 +    detectImpl(src,keypoints,feature_maps,mask);
 +}
 +
 +
 +Ellipse::Ellipse():
 +    angle(0),
 +    cosf(0),
 +    sinf(0)
 +{
 +}
 +
 +Ellipse::Ellipse(const cv::Point2f &_center, const cv::Size2f &_axes, float _angle):
 +    center(_center),
 +    axes(_axes),
 +    angle(_angle),
 +    cosf(cos(-_angle)),
 +    sinf(sin(-_angle))
 +{
 +}
 +
 +const cv::Size2f &Ellipse::getAxes()const
 +{
 +    return axes;
 +}
 +
 +cv::Point2f Ellipse::getCenter()const
 +{
 +    return center;
 +}
 +
 +void Ellipse::draw(cv::InputOutputArray img,const cv::Scalar &color)const
 +{
 +    cv::ellipse(img,center,axes,360-angle/CV_PI*180,0,360,color);
 +}
 +
 +bool Ellipse::contains(const cv::Point2f &pt)const
 +{
 +    cv::Point2f ptc = pt-center;
 +    float x = cosf*ptc.x+sinf*ptc.y;
 +    float y = -sinf*ptc.x+cosf*ptc.y;
 +    if(x*x/(axes.width*axes.width)+y*y/(axes.height*axes.height) <= 1.0)
 +        return true;
 +    return false;
 +}
 +
 +
 +// returns false if the angle from the line pt1-pt2 to the line pt3-pt4 is negative
 +static bool checkOrientation(const cv::Point2f &pt1,const cv::Point2f &pt2,
 +        const cv::Point2f &pt3,const cv::Point2f &pt4)
 +{
 +    cv::Point3f p1(pt2.x-pt1.x,pt2.y-pt1.y,0);
 +    cv::Point3f p2(pt4.x-pt3.x,pt4.y-pt3.y,0);
 +    return p1.cross(p2).z > 0;
 +}
 +
 +static bool sortKeyPoint(const cv::KeyPoint &pt1,const cv::KeyPoint &pt2)
 +{
 +    // used as comparison function for partial sort
 +    // the keypoints with the best score should be first
 +    return pt1.response > pt2.response;
 +}
 +
 +cv::Mat Chessboard::getObjectPoints(const cv::Size &pattern_size,float cell_size)
 +{
 +    cv::Mat result(pattern_size.width*pattern_size.height,1,CV_32FC3);
 +    for(int row=0;row < pattern_size.height;++row)
 +    {
 +        for(int col=0;col< pattern_size.width;++col)
 +        {
 +            cv::Point3f &pt = *result.ptr<cv::Point3f>(row*pattern_size.width+col);
 +            pt.x = cell_size*col;
 +            pt.y = cell_size*row;
 +            pt.z = 0;
 +        }
 +    }
 +    return result;
 +}
 +
 +bool Chessboard::Board::Cell::empty()const
 +{
 +    // check if one of its corners has NaN
 +    if(top_left->x != top_left->x || top_left->y != top_left->y)
 +        return true;
 +    if(top_right->x != top_right->x || top_right->y != top_right->y)
 +        return true;
 +    if(bottom_right->x != bottom_right->x || bottom_right->y != bottom_right->y)
 +        return true;
 +    if(bottom_left->x != bottom_left->x || bottom_left->y != bottom_left->y)
 +        return true;
 +    return false;
 +}
 +
 +int Chessboard::Board::Cell::getRow()const
 +{
 +    int row = 0;
 +    Cell const* temp = this;
 +    for(;temp->top;temp=temp->top,++row);
 +    return row;
 +}
 +
 +int Chessboard::Board::Cell::getCol()const
 +{
 +    int col = 0;
 +    Cell const* temp = this;
 +    for(;temp->left;temp=temp->left,++col);
 +    return col;
 +}
 +
 +Chessboard::Board::Cell::Cell() :
 +    top_left(NULL), top_right(NULL), bottom_right(NULL), bottom_left(NULL),
 +    left(NULL), top(NULL), right(NULL), bottom(NULL),black(false)
 +{}
 +
 +Chessboard::Board::PointIter::PointIter(Cell *_cell,CornerIndex _corner_index):
 +    corner_index(_corner_index),
 +    cell(_cell)
 +{
 +}
 +
 +Chessboard::Board::PointIter::PointIter(const PointIter &other)
 +{
 +    this->operator=(other);
 +}
 +
 +void Chessboard::Board::PointIter::operator=(const PointIter &other)
 +{
 +    corner_index = other.corner_index;
 +    cell = other.cell;
 +}
 +
 +Chessboard::Board::Cell* Chessboard::Board::PointIter::getCell()
 +{
 +    return cell;
 +}
 +
 +bool Chessboard::Board::PointIter::valid()const
 +{
 +    return cell != NULL;
 +}
 +
 +bool Chessboard::Board::PointIter::isNaN()const
 +{
 +    const cv::Point2f *pt = operator*();
 +    if(pt->x != pt->x || pt->y != pt->y)        // NaN check
 +        return true;
 +    return false;
 +}
 +
 +bool Chessboard::Board::PointIter::checkCorner()const
 +{
 +    if(!cell->empty())
 +        return true;
 +    // test all other cells
 +    switch(corner_index)
 +    {
 +    case BOTTOM_LEFT:
 +        if(cell->left)
 +        {
 +            if(!cell->left->empty())
 +                return true;
 +            if(cell->left->bottom && !cell->left->bottom->empty())
 +                return true;
 +        }
 +        if(cell->bottom)
 +        {
 +            if(!cell->bottom->empty())
 +                return true;
 +            if(cell->bottom->left && !cell->bottom->left->empty())
 +                return true;
 +        }
 +        break;
 +    case TOP_LEFT:
 +        if(cell->left)
 +        {
 +            if(!cell->left->empty())
 +                return true;
 +            if(cell->left->top && !cell->left->top->empty())
 +                return true;
 +        }
 +        if(cell->top)
 +        {
 +            if(!cell->top->empty())
 +                return true;
 +            if(cell->top->left && !cell->top->left->empty())
 +                return true;
 +        }
 +        break;
 +    case TOP_RIGHT:
 +        if(cell->right)
 +        {
 +            if(!cell->right->empty())
 +                return true;
 +            if(cell->right->top && !cell->right->top->empty())
 +                return true;
 +        }
 +        if(cell->top)
 +        {
 +            if(!cell->top->empty())
 +                return true;
 +            if(cell->top->right && !cell->top->right->empty())
 +                return true;
 +        }
 +        break;
 +    case BOTTOM_RIGHT:
 +        if(cell->right)
 +        {
 +            if(!cell->right->empty())
 +                return true;
 +            if(cell->right->bottom && !cell->right->bottom->empty())
 +                return true;
 +        }
 +        if(cell->bottom)
 +        {
 +            if(!cell->bottom->empty())
 +                return true;
 +            if(cell->bottom->right && !cell->bottom->right->empty())
 +                return true;
 +        }
 +        break;
 +    default:
 +        CV_Assert(false);
 +    }
 +    return false;
 +}
 +
 +
 +bool Chessboard::Board::PointIter::left(bool check_empty)
 +{
 +    switch(corner_index)
 +    {
 +    case BOTTOM_LEFT:
 +        if(cell->left && (!check_empty || !cell->left->empty()))
 +            cell = cell->left;
 +        else if(check_empty && cell->bottom && cell->bottom->left && !cell->bottom->left->empty())
 +        {
 +            cell = cell->bottom->left;
 +            corner_index = TOP_LEFT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case TOP_LEFT:
 +        if(cell->left && (!check_empty || !cell->left->empty()))
 +            cell = cell->left;
 +        else if(check_empty && cell->top && cell->top->left && !cell->top->left->empty())
 +        {
 +            cell = cell->top->left;
 +            corner_index = BOTTOM_LEFT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case TOP_RIGHT:
 +        corner_index = TOP_LEFT;
 +        break;
 +    case BOTTOM_RIGHT:
 +        corner_index = BOTTOM_LEFT;
 +        break;
 +    default:
 +        CV_Assert(false);
 +    }
 +    return true;
 +}
 +
 +bool Chessboard::Board::PointIter::top(bool check_empty)
 +
 +{
 +    switch(corner_index)
 +    {
 +    case TOP_RIGHT:
 +        if(cell->top && (!check_empty || !cell->top->empty()))
 +            cell = cell->top;
 +        else if(check_empty && cell->right && cell->right->top&& !cell->right->top->empty())
 +        {
 +            cell = cell->right->top;
 +            corner_index = TOP_LEFT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case TOP_LEFT:
 +        if(cell->top && (!check_empty || !cell->top->empty()))
 +            cell = cell->top;
 +        else if(check_empty && cell->left && cell->left->top&& !cell->left->top->empty())
 +        {
 +            cell = cell->left->top;
 +            corner_index = TOP_RIGHT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case BOTTOM_LEFT:
 +        corner_index = TOP_LEFT;
 +        break;
 +    case BOTTOM_RIGHT:
 +        corner_index = TOP_RIGHT;
 +        break;
 +    default:
 +        CV_Assert(false);
 +    }
 +    return true;
 +}
 +
 +bool Chessboard::Board::PointIter::right(bool check_empty)
 +{
 +    switch(corner_index)
 +    {
 +    case TOP_RIGHT:
 +        if(cell->right && (!check_empty || !cell->right->empty()))
 +            cell = cell->right;
 +        else if(check_empty && cell->top && cell->top->right && !cell->top->right->empty())
 +        {
 +            cell = cell->top->right;
 +            corner_index = BOTTOM_RIGHT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case BOTTOM_RIGHT:
 +        if(cell->right && (!check_empty || !cell->right->empty()))
 +            cell = cell->right;
 +        else if(check_empty && cell->bottom && cell->bottom->right && !cell->bottom->right->empty())
 +        {
 +            cell = cell->bottom->right;
 +            corner_index = TOP_RIGHT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case TOP_LEFT:
 +        corner_index = TOP_RIGHT;
 +        break;
 +    case BOTTOM_LEFT:
 +        corner_index = BOTTOM_RIGHT;
 +        break;
 +    default:
 +        CV_Assert(false);
 +    }
 +    return true;
 +}
 +
 +bool Chessboard::Board::PointIter::bottom(bool check_empty)
 +{
 +    switch(corner_index)
 +    {
 +    case BOTTOM_LEFT:
 +        if(cell->bottom && (!check_empty || !cell->bottom->empty()))
 +            cell = cell->bottom;
 +        else if(check_empty && cell->left && cell->left->bottom && !cell->left->bottom->empty())
 +        {
 +            cell = cell->left->bottom;
 +            corner_index = BOTTOM_RIGHT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case BOTTOM_RIGHT:
 +        if(cell->bottom && (!check_empty || !cell->bottom->empty()))
 +            cell = cell->bottom;
 +        else if(check_empty && cell->right && cell->right->bottom && !cell->right->bottom->empty())
 +        {
 +            cell = cell->right->bottom;
 +            corner_index = BOTTOM_LEFT;
 +        }
 +        else
 +            return false;
 +        break;
 +    case TOP_LEFT:
 +        corner_index = BOTTOM_LEFT;
 +        break;
 +    case TOP_RIGHT:
 +        corner_index = BOTTOM_RIGHT;
 +        break;
 +    default:
 +        CV_Assert(false);
 +    }
 +    return true;
 +}
 +
 +
 +const cv::Point2f* Chessboard::Board::PointIter::operator*()const
 +{
 +    switch(corner_index)
 +    {
 +    case TOP_LEFT:
 +        return cell->top_left;
 +    case TOP_RIGHT:
 +        return cell->top_right;
 +    case BOTTOM_RIGHT:
 +        return cell->bottom_right;
 +    case BOTTOM_LEFT:
 +        return cell->bottom_left;
 +    }
 +    CV_Assert(false);
 +}
 +
 +const cv::Point2f* Chessboard::Board::PointIter::operator->()const
 +{
 +    return operator*();
 +}
 +
 +cv::Point2f* Chessboard::Board::PointIter::operator*()
 +{
 +    const cv::Point2f *pt = const_cast<const PointIter*>(this)->operator*();
 +    return const_cast<cv::Point2f*>(pt);
 +}
 +
 +cv::Point2f* Chessboard::Board::PointIter::operator->()
 +{
 +    return operator*();
 +}
 +
 +Chessboard::Board::Board(float _white_angle,float _black_angle):
 +    top_left(NULL),
 +    rows(0),
 +    cols(0),
 +    white_angle(_white_angle),
 +    black_angle(_black_angle)
 +{
 +}
 +
 +
 +Chessboard::Board::Board(const Chessboard::Board &other):
 +    top_left(NULL),
 +    rows(0),
 +    cols(0)
 +{
 +    *this = other;
 +}
 +
 +Chessboard::Board::Board(const cv::Size &size, const std::vector<cv::Point2f> &points,float _white_angle,float _black_angle):
 +    top_left(NULL),
 +    rows(0),
 +    cols(0),
 +    white_angle(_white_angle),
 +    black_angle(_black_angle)
 +{
 +    if(size.width*size.height != int(points.size()))
 +        CV_Error(Error::StsBadArg,"size mismatch");
 +    if(size.width < 3 || size.height < 3)
 +        CV_Error(Error::StsBadArg,"at least 3 rows and cols are needed to initialize the board");
 +
 +    // init board with 3x3
 +    // TODO write function speeding up the copying
 +    cv::Mat data = cv::Mat(points).reshape(2,size.height);
 +    cv::Mat temp;
 +    data(cv::Rect(0,0,3,3)).copyTo(temp);
 +    std::vector<cv::Point2f> ipoints = temp.reshape(2,1);
 +    if(!init(ipoints))
 +        return;
 +
 +    // add all cols
 +    for(int col=3 ; col< data.cols;++col)
 +    {
 +        data(cv::Rect(col,0,1,3)).copyTo(temp);
 +        ipoints = temp.reshape(2,1);
 +        addColumnRight(ipoints);
 +    }
 +
 +    // add all rows
 +    for(int row=3; row < data.rows;++row)
 +    {
 +        data(cv::Rect(0,row,cols,1)).copyTo(temp);
 +        ipoints = temp.reshape(2,1);
 +        addRowBottom(ipoints);
 +    }
 +}
 +
 +Chessboard::Board::~Board()
 +{
 +    clear();
 +}
 +
 +std::vector<cv::Point2f> Chessboard::Board::getCellCenters()const
 +{
 +    int icols = int(colCount());
 +    int irows = int(rowCount());
 +    if(icols < 3 || irows < 3)
 +        throw std::runtime_error("getCellCenters: Chessboard must be at least consist of 3 rows and cols to calculate the cell centers");
 +
 +    std::vector<cv::Point2f> points;
 +    cv::Matx33d H(estimateHomography(DUMMY_FIELD_SIZE));
 +    cv::Vec3d pt1,pt2;
 +    pt1[2] = 1;
 +    for(int row = 0;row < irows;++row)
 +    {
 +        pt1[1] = (0.5+row)*DUMMY_FIELD_SIZE;
 +        for(int col= 0;col< icols;++col)
 +        {
 +            pt1[0] = (0.5+col)*DUMMY_FIELD_SIZE;
 +            pt2 = H*pt1;
 +            points.push_back(cv::Point2f(float(pt2[0]/pt2[2]),float(pt2[1]/pt2[2])));
 +        }
 +    }
 +    return points;
 +}
 +
 +void Chessboard::Board::draw(cv::InputArray m,cv::OutputArray out,cv::InputArray _H)const
 +{
 +    cv::Mat H = _H.getMat();
 +    if(H.empty())
 +        H = estimateHomography();
 +    cv::Mat image = m.getMat().clone();
 +    if(image.type() == CV_32FC1)
 +    {
 +        double maxVal,minVal;
 +        cv::minMaxLoc(image, &minVal, &maxVal);
 +        double scale = 255.0/(maxVal-minVal);
 +        image.convertTo(image,CV_8UC1,scale,-scale*minVal);
 +        cv::applyColorMap(image,image,cv::COLORMAP_JET);
 +    }
 +
 +    // draw all points and search areas
 +    std::vector<cv::Point2f> points = getCorners();
 +    std::vector<cv::Point2f>::const_iterator iter1 = points.begin();
 +    int icols = int(colCount());
 +    int irows = int(rowCount());
 +    int count=0;
 +    for(int row=0;row<irows;++row)
 +    {
 +        for(int col=0;col<icols;++col,++iter1)
 +        {
 +            if(iter1->x != iter1->x)    // NaN check
 +            {
 +                // draw search ellipse
 +                Ellipse ellipse = estimateSearchArea(H,row,col,0.4F);
 +                ellipse.draw(image,cv::Scalar::all(200));
 +            }
 +            else
 +            {
 +                cv::circle(image,*iter1,4,cv::Scalar(count*20,count*20,count*20,255),-1);
 +                ++count;
 +            }
 +        }
 +    }
 +
 +    // draw field colors
 +    for(int row=0;row<irows-1;++row)
 +    {
 +        for(int col=0;col<icols-1;++col)
 +        {
 +            const Cell *cell = getCell(row,col);
 +            cv::Point2f center = *cell->top_left+*cell->top_right+*cell->bottom_left+*cell->bottom_right;
 +            center.x /=4;
 +            center.y /=4;
 +            int size = 4;
 +            if(row==0&&col==0)
 +                size=8;
 +            if(row==0&&col==1)
 +                size=7;
 +            if(cell->black)
 +                cv::circle(image,center,size,cv::Scalar::all(255),-1);
 +            else
 +                cv::circle(image,center,size,cv::Scalar(0,0,10,255),-1);
 +        }
 +    }
 +
 +    out.create(image.rows,image.cols,image.type());
 +    image.copyTo(out.getMat());
 +}
 +
 +bool Chessboard::Board::estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const
 +{
 +    cv::Mat K = _K.getMat();
 +    CV_CheckTypeEQ(K.type(), CV_64FC1, "wrong K type");
 +    CV_CheckEQ(K.size(), Size(3, 3), "wrong K size");
 +    if(isEmpty())
 +        return false;
 +
 +    int icols = int(colCount());
 +    int irows = int(rowCount());
 +    float field_width = real_size.width/(icols+1);
 +    float field_height= real_size.height/(irows+1);
 +    // the center of the board is placed at (0,0,1)
 +    int offset_x = int(-(icols-1)*field_width*0.5F);
 +    int offset_y = int(-(irows-1)*field_width*0.5F);
 +
 +    std::vector<cv::Point2f> image_points;
 +    std::vector<cv::Point3f> object_points;
 +    std::vector<cv::Point2f> corners_temp = getCorners(true);
 +    std::vector<cv::Point2f>::const_iterator iter = corners_temp.begin();
 +    for(int row = 0;row < irows;++row)
 +    {
 +        for(int col= 0;col<icols;++col,++iter)
 +        {
 +            if(iter == corners_temp.end())
 +                CV_Error(Error::StsInternal,"internal error");
 +            if(iter->x != iter->x)      // NaN check
 +                continue;
 +            image_points.push_back(*iter);
 +            object_points.push_back(cv::Point3f(field_width*col-offset_x,field_height*row-offset_y,1.0));
 +        }
 +    }
 +    return cv::solvePnP(object_points,image_points,K,cv::Mat(),rvec,tvec);//,cv::SOLVEPNP_P3P);
 +}
 +
 +float Chessboard::Board::getBlackAngle()const
 +{
 +    return black_angle;
 +}
 +
 +float Chessboard::Board::getWhiteAngle()const
 +{
 +    return white_angle;
 +}
 +
 +void Chessboard::Board::swap(Chessboard::Board &other)
 +{
 +    corners.swap(other.corners);
 +    cells.swap(other.cells);
 +    std::swap(rows,other.rows);
 +    std::swap(cols,other.cols);
 +    std::swap(top_left,other.top_left);
 +    std::swap(white_angle,other.white_angle);
 +    std::swap(black_angle,other.black_angle);
 +}
 +
 +Chessboard::Board& Chessboard::Board::operator=(const Chessboard::Board &other)
 +{
 +    if(this == &other)
 +        return *this;
 +    clear();
 +    rows = other.rows;
 +    cols = other.cols;
 +    white_angle = other.white_angle;
 +    black_angle = other.black_angle;
 +    cells.reserve(other.cells.size());
 +    corners.reserve(other.corners.size());
 +
 +    //copy all points and generate mapping
 +    std::map<cv::Point2f*,cv::Point2f*> point_point_mapping;
 +    point_point_mapping[NULL] = NULL;
 +    std::vector<cv::Point2f*>::const_iterator iter = other.corners.begin();
 +    for(;iter != other.corners.end();++iter)
 +    {
 +        cv::Point2f *pt = new cv::Point2f(**iter);
 +        point_point_mapping[*iter] = pt;
 +        corners.push_back(pt);
 +    }
 +
 +    //copy all cells using mapping
 +    std::map<Cell*,Cell*> cell_cell_mapping;
 +    std::vector<Cell*>::const_iterator iter2 = other.cells.begin();
 +    for(;iter2 != other.cells.end();++iter2)
 +    {
 +        Cell *cell = new Cell;
 +        cell->top_left = point_point_mapping[(*iter2)->top_left];
 +        cell->top_right= point_point_mapping[(*iter2)->top_right];
 +        cell->bottom_right= point_point_mapping[(*iter2)->bottom_right];
 +        cell->bottom_left = point_point_mapping[(*iter2)->bottom_left];
 +        cell->black = (*iter2)->black;
 +        cell_cell_mapping[*iter2] = cell;
 +        cells.push_back(cell);
 +    }
 +
 +    //set cell connections using mapping
 +    cell_cell_mapping[NULL] = NULL;
 +    iter2 = other.cells.begin();
 +    std::vector<Cell*>::iterator iter3 = cells.begin();
 +    for(;iter2 != other.cells.end();++iter2,++iter3)
 +    {
 +        (*iter3)->left = cell_cell_mapping[(*iter2)->left];
 +        (*iter3)->top = cell_cell_mapping[(*iter2)->top];
 +        (*iter3)->right = cell_cell_mapping[(*iter2)->right];
 +        (*iter3)->bottom= cell_cell_mapping[(*iter2)->bottom];
 +    }
 +    top_left = cell_cell_mapping[other.top_left];
 +    return *this;
 +}
 +
 +void Chessboard::Board::normalizeOrientation(bool bblack)
 +{
 +    // fix ordering
 +    cv::Point2f y = getCorner(0,1)-getCorner(2,1);
 +    cv::Point2f x = getCorner(1,2)-getCorner(1,0);
 +    cv::Point3f y3d(y.x,y.y,0);
 +    cv::Point3f x3d(x.x,x.y,0);
 +    if(x3d.cross(y3d).z > 0)
 +        flipHorizontal();
 +
 +    //normalize orientation so that first element is black or white
 +    const Cell* cell = getCell(0,0);
 +    if(cell->black != bblack && colCount()%2 != 0)
 +        rotateLeft();
 +    else if(cell->black != bblack && rowCount()%2 != 0)
 +    {
 +        rotateLeft();
 +        rotateLeft();
 +    }
 +
 +    //find closest point to top left image corner
 +    //in case of symmetric checkerboard
 +    if(colCount() == rowCount())
 +    {
 +        PointIter iter_top_right(top_left,TOP_RIGHT);
 +        while(iter_top_right.right());
 +        PointIter iter_bottom_right(iter_top_right);
 +        while(iter_bottom_right.bottom());
 +        PointIter iter_bottom_left(top_left,BOTTOM_LEFT);
 +        while(iter_bottom_left.bottom());
 +        // check if one of the cell is empty and do not normalize if so
 +        if(top_left->empty() || iter_top_right.getCell()->empty() ||
 +                iter_bottom_left.getCell()->empty() || iter_bottom_right.getCell()->empty())
 +            return;
 +
 +        float d1 = pow(top_left->top_left->x,2)+pow(top_left->top_left->y,2);
 +        float d2 = pow((*iter_top_right)->x,2)+pow((*iter_top_right)->y,2);
 +        float d3 = pow((*iter_bottom_left)->x,2)+pow((*iter_bottom_left)->y,2);
 +        float d4 = pow((*iter_bottom_right)->x,2)+pow((*iter_bottom_right)->y,2);
 +        if(d2 <= d1 && d2 <= d3 && d2 <= d4) // top left is top right
 +            rotateLeft();
 +        else if(d3 <= d1 && d3 <= d2 && d3 <= d4) // top left is bottom left
 +            rotateRight();
 +        else if(d4 <= d1 && d4 <= d2 && d4 <= d3)      // top left is bottom right
 +        {
 +            rotateLeft();
 +            rotateLeft();
 +        }
 +    }
 +}
 +
 +void Chessboard::Board::rotateRight()
 +{
 +    PointIter p_iter(top_left,BOTTOM_LEFT);
 +    while(p_iter.bottom());
 +
 +    std::vector<Cell*>::iterator iter = cells.begin();
 +    for(;iter != cells.end();++iter)
 +    {
 +        Cell *temp = (*iter)->bottom;
 +        (*iter)->bottom = (*iter)->right;
 +        (*iter)->right= (*iter)->top;
 +        (*iter)->top= (*iter)->left;
 +        (*iter)->left = temp;
 +
 +        cv::Point2f *ptemp = (*iter)->bottom_left;
 +        (*iter)->bottom_left= (*iter)->bottom_right;
 +        (*iter)->bottom_right= (*iter)->top_right;
 +        (*iter)->top_right= (*iter)->top_left;
 +        (*iter)->top_left= ptemp;
 +    }
 +    int temp = rows;
 +    rows = cols;
 +    cols = temp;
 +    top_left = p_iter.getCell();
 +}
 +
 +
 +void Chessboard::Board::rotateLeft()
 +{
 +    PointIter p_iter(top_left,TOP_RIGHT);
 +    while(p_iter.right());
 +
 +    std::vector<Cell*>::iterator iter = cells.begin();
 +    for(;iter != cells.end();++iter)
 +    {
 +        Cell *temp = (*iter)->top;
 +        (*iter)->top = (*iter)->right;
 +        (*iter)->right= (*iter)->bottom;
 +        (*iter)->bottom= (*iter)->left;
 +        (*iter)->left = temp;
 +
 +        cv::Point2f *ptemp = (*iter)->top_left;
 +        (*iter)->top_left = (*iter)->top_right;
 +        (*iter)->top_right= (*iter)->bottom_right;
 +        (*iter)->bottom_right = (*iter)->bottom_left;
 +        (*iter)->bottom_left = ptemp;
 +    }
 +    int temp = rows;
 +    rows = cols;
 +    cols = temp;
 +    top_left = p_iter.getCell();
 +}
 +
 +void Chessboard::Board::flipHorizontal()
 +{
 +    PointIter p_iter(top_left,TOP_RIGHT);
 +    while(p_iter.right());
 +
 +    std::vector<Cell*>::iterator iter = cells.begin();
 +    for(;iter != cells.end();++iter)
 +    {
 +        Cell *temp = (*iter)->right;
 +        (*iter)->right= (*iter)->left;
 +        (*iter)->left = temp;
 +
 +        cv::Point2f *ptemp = (*iter)->top_left;
 +        (*iter)->top_left = (*iter)->top_right;
 +        (*iter)->top_right = ptemp;
 +
 +        ptemp = (*iter)->bottom_left;
 +        (*iter)->bottom_left = (*iter)->bottom_right;
 +        (*iter)->bottom_right = ptemp;
 +    }
 +    top_left = p_iter.getCell();
 +}
 +
 +void Chessboard::Board::flipVertical()
 +{
 +    PointIter p_iter(top_left,BOTTOM_LEFT);
 +    while(p_iter.bottom());
 +
 +    std::vector<Cell*>::iterator iter = cells.begin();
 +    for(;iter != cells.end();++iter)
 +    {
 +        Cell *temp = (*iter)->top;
 +        (*iter)->top= (*iter)->bottom;
 +        (*iter)->bottom = temp;
 +
 +        cv::Point2f *ptemp = (*iter)->top_left;
 +        (*iter)->top_left = (*iter)->bottom_left;
 +        (*iter)->bottom_left = ptemp;
 +
 +        ptemp = (*iter)->top_right;
 +        (*iter)->top_right = (*iter)->bottom_right;
 +        (*iter)->bottom_right = ptemp;
 +    }
 +    top_left = p_iter.getCell();
 +}
 +
 +// returns the best found score
 +// if NaN is returned for a point no point at all was found
 +// if 0 is returned the point lies outside of the ellipse
 +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)
 +    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);
 +    point = ellipse.getCenter();
 +    query[0] = point.x;
 +    query[1] = point.y;
 +    index.knnSearch(query,indices,dists,4,cv::flann::SearchParams(64));
 +    std::vector<int>::const_iterator iter = indices.begin();
 +    float best_score = -std::numeric_limits<float>::max();
 +    point.x = std::numeric_limits<float>::quiet_NaN();
 +    point.y = std::numeric_limits<float>::quiet_NaN();
 +    for(;iter != indices.end();++iter)
 +    {
 +        const float *val = data.ptr<float>(*iter);
 +        const float &response = *(val+3);
 +        if(response < best_score)
 +            continue;
 +        const float &a0 = *(val+2);
 +        float a1 = std::fabs(a0-white_angle);
 +        float a2 = std::fabs(a0-black_angle);
 +        if(a1 > CV_PI*0.5)
 +            a1 = std::fabs(float(a1-CV_PI));
 +        if(a2> CV_PI*0.5)
 +            a2 = std::fabs(float(a2-CV_PI));
 +        if(a1  < MAX_ANGLE || a2 < MAX_ANGLE )
 +        {
 +            cv::Point2f pt(val[0], val[1]);
 +            if(point.x != point.x)       // NaN check
 +                point = pt;
 +            if(best_score < response && ellipse.contains(pt))
 +            {
 +                best_score = response;
 +                point = pt;
 +            }
 +        }
 +    }
 +    if(best_score == -std::numeric_limits<float>::max())
 +        return 0;
 +    else
 +        return best_score;
 +}
 +
 +void Chessboard::Board::clear()
 +{
 +    top_left = NULL; rows = 0; cols = 0;
 +    std::vector<Cell*>::iterator iter = cells.begin();
 +    for(;iter != cells.end();++iter)
 +        delete *iter;
 +    cells.clear();
 +    std::vector<cv::Point2f*>::iterator iter2 = corners.begin();
 +    for(;iter2 != corners.end();++iter2)
 +        delete *iter2;
 +    corners.clear();
 +}
 +
 +// p0 p1 p2
 +// p3 p4 p5
 +// p6 p7 p8
 +bool Chessboard::Board::init(const std::vector<cv::Point2f> points)
 +{
 +    clear();
 +    if(points.size() != 9)
 +        CV_Error(Error::StsBadArg,"exact nine points are expected to initialize the board");
 +
 +    // generate cells
 +    corners.resize(9);
 +    for(int i=0;i < 9;++i)
 +        corners[i] = new cv::Point2f(points[i]);
 +    cells.resize(4);
 +    for(int i=0;i<4;++i)
 +        cells[i] = new Cell();
 +
 +    //cell 0
 +    cells[0]->top_left = corners[0];
 +    cells[0]->top_right = corners[1];
 +    cells[0]->bottom_right = corners[4];
 +    cells[0]->bottom_left = corners[3];
 +    cells[0]->right = cells[1];
 +    cells[0]->bottom = cells[2];
 +
 +    //cell 1
 +    cells[1]->top_left = corners[1];
 +    cells[1]->top_right = corners[2];
 +    cells[1]->bottom_right = corners[5];
 +    cells[1]->bottom_left = corners[4];
 +    cells[1]->left = cells[0];
 +    cells[1]->bottom = cells[3];
 +
 +    //cell 2
 +    cells[2]->top_left = corners[3];
 +    cells[2]->top_right = corners[4];
 +    cells[2]->bottom_right = corners[7];
 +    cells[2]->bottom_left = corners[6];
 +    cells[2]->top = cells[0];
 +    cells[2]->right = cells[3];
 +
 +    //cell 3
 +    cells[3]->top_left = corners[4];
 +    cells[3]->top_right = corners[5];
 +    cells[3]->bottom_right = corners[8];
 +    cells[3]->bottom_left = corners[7];
 +    cells[3]->top = cells[1];
 +    cells[3]->left= cells[2];
 +
 +    top_left = cells.front();
 +    rows = 3;
 +    cols = 3;
 +
 +    // set initial cell colors
 +    Point2f pt1 = *(cells[0]->top_right)-*(cells[0]->bottom_left);
 +    pt1 /= cv::norm(pt1);
 +    cv::Point2f pt2(cos(white_angle),-sin(white_angle));
 +    cv::Point2f pt3(cos(black_angle),-sin(black_angle));
 +    if(fabs(pt1.dot(pt2)) < fabs(pt1.dot(pt3)))
 +    {
 +        cells[0]->black = false;
 +        cells[1]->black = true;
 +        cells[2]->black = true;
 +        cells[3]->black = false;
 +    }
 +    else
 +    {
 +        cells[0]->black = true;
 +        cells[1]->black = false;
 +        cells[2]->black = false;
 +        cells[3]->black = true;
 +    }
 +    return true;
 +}
 +
 +//TODO magic number
 +bool Chessboard::Board::estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2, cv::Point2f &p3)
 +{
 +    // use cross ration to find new point
 +    if(p0 == p1 || p0 == p2 || p1 == p2)
 +        return false;
 +    cv::Point2f p01 = p1-p0;
 +    cv::Point2f p12 = p2-p1;
 +    float a = float(cv::norm(p01));
 +    float b = float(cv::norm(p12));
 +    float t = (0.75F*a-0.25F*b);
 +    if(t <= 0)
 +        return false;
 +    float c = 0.25F*b*(a+b)/t;
 +    if(c < 0.1F)
 +        return false;
 +    p01 = p01/a;
 +    p12 = p12/b;
 +    // check angle between p01 and p12 < 25°
 +    if(p01.dot(p12) < 0.9)
 +        return false;
 +    // calc mean
 +    // p12 = (p01+p12)*0.5;
 +    // p3 = p2+p12*c;
 +    p3 = p2+p12*c;
 +
 +    // compensate radial distortion by fitting polynom
 +    std::vector<double> x,y;
 +    x.resize(3,0); y.resize(3,0);
 +    x[1] = b;
 +    x[2] = b+a;
 +    y[2] = calcSignedDistance(-p12,p2,p0);
 +    cv::Mat dst;
 +    polyfit(cv::Mat(x),cv::Mat(y),dst,2);
 +    double d = dst.at<double>(0)-dst.at<double>(1)*c+dst.at<double>(2)*c*c;
 +    cv::Vec3f v1(p12.x,p12.y,0);
 +    cv::Vec3f v2(0,0,1);
 +    cv::Vec3f v3 = v1.cross(v2);
 +    cv::Point2f n2(v3[0],v3[1]);
 +    p3 += d*n2;
 +    return true;
 +}
 +
 +bool Chessboard::Board::estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2, const cv::Point2f &p3, cv::Point2f &p4)
 +{
 +    // use 1D homography to find fith point minimizing square error
 +    if(p0 == p1 || p0 == p2 || p0 == p3 || p1 == p2 || p1 == p3 || p2 == p3 )
 +        return false;
 +    static const cv::Mat src = (cv::Mat_<double>(1,4) << 0,10,20,30);
 +    cv::Point2f p01 = p1-p0;
 +    cv::Point2f p02 = p2-p0;
 +    cv::Point2f p03 = p3-p0;
 +    float a = float(cv::norm(p01));
 +    float b = float(cv::norm(p02));
 +    float c = float(cv::norm(p03));
 +    cv::Mat dst = (cv::Mat_<double>(1,4) << 0,a,b,c);
 +    cv::Mat h = findHomography1D(src,dst);
 +    float d = float((h.at<double>(0,0)*40+h.at<double>(0,1))/(h.at<double>(1,0)*40+h.at<double>(1,1)));
 +    cv::Point2f p12 = p2-p1;
 +    cv::Point2f p23 = p3-p2;
 +    p01 = p01/a;
 +    p12 = p12/cv::norm(p12);
 +    p23 = p23/cv::norm(p23);
 +    p4 = p3+(d-c)*p23;
 +
 +    // compensate radial distortion by fitting polynom
 +    std::vector<double> x,y;
 +    x.resize(4,0); y.resize(4,0);
 +    x[1] = c-b;
 +    x[2] = c-a;
 +    x[3] = c;
 +    y[2] = calcSignedDistance(-p23,p3,p1);
 +    y[3] = calcSignedDistance(-p23,p3,p0);
 +    polyfit(cv::Mat(x),cv::Mat(y),dst,2);
 +    d = d-c;
 +    double e = dst.at<double>(0)-dst.at<double>(1)*fabs(d)+dst.at<double>(2)*d*d;
 +    cv::Vec3f v1(p23.x,p23.y,0);
 +    cv::Vec3f v2(0,0,1);
 +    cv::Vec3f v3 = v1.cross(v2);
 +    cv::Point2f n2(v3[0],v3[1]);
 +    p4 += e*n2;
 +    return true;
 +}
 +
 +// H is describing the transformation from dummy to reality
 +Ellipse Chessboard::Board::estimateSearchArea(cv::Mat _H,int row, int col,float p,int field_size)
 +{
 +    cv::Matx31d point1,point2,center;
 +    center(0) = (1+col)*field_size;
 +    center(1) = (1+row)*field_size;
 +    center(2) = 1.0;
 +    point1(0) = center(0)-p*field_size;
 +    point1(1) = center(1);
 +    point1(2) = center(2);
 +    point2(0) = center(0);
 +    point2(1) = center(1)-p*field_size;
 +    point2(2) = center(2);
 +
 +    cv::Matx33d H(_H);
 +    point1 = H*point1;
 +    point2 = H*point2;
 +    center = H*center;
 +    cv::Point2f pt(float(center(0)/center(2)),float(center(1)/center(2)));
 +    cv::Point2f pt1(float(point1(0)/point1(2)),float(point1(1)/point1(2)));
 +    cv::Point2f pt2(float(point2(0)/point2(2)),float(point2(1)/point2(2)));
 +
 +    cv::Point2f p01(pt1-pt);
 +    cv::Point2f p02(pt2-pt);
 +    float norm1 = float(cv::norm(p01));
 +    float norm2 = float(cv::norm(p02));
 +    float angle = float(acos(p01.dot(p02)/norm1/norm2));
 +    cv::Size2f axes(norm1,norm2);
 +    return Ellipse(pt,axes,angle);
 +}
 +
 +bool Chessboard::Board::estimateSearchArea(const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3,float p,Ellipse &ellipse,const cv::Point2f *p0)
 +{
 +    cv::Point2f p4,n;
 +    if(p0)
 +    {
 +        // use 1D homography
 +        if(!estimatePoint(*p0,p1,p2,p3,p4))
 +            return false;
 +        n = p4-*p0;
 +    }
 +    else
 +    {
 +        // use cross ratio
 +        if(!estimatePoint(p1,p2,p3,p4))
 +            return false;
 +        n = p4-p1;
 +    }
 +    float norm = float(cv::norm(n));
 +    n = n/norm;
 +    float angle = acos(n.x);
 +    if(n.y > 0)
 +        angle = float(2.0F*CV_PI-angle);
 +    n = p4-p3;
 +    norm = float(cv::norm(n));
 +    double delta = std::max(3.0F,p*norm);
 +    ellipse = Ellipse(p4,cv::Size(int(delta),int(std::max(2.0,delta*ELLIPSE_WIDTH))),angle);
 +    return true;
 +}
 +
 +bool Chessboard::Board::checkRowColumn(const std::vector<cv::Point2f> &points)
 +{
 +    if(points.size() < 4)
 +    {
 +        if(points.size() == 3)
 +            return true;
 +        else
 +            return false;
 +    }
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin();
 +    std::vector<cv::Point2f>::const_iterator iter2 = iter+1;
 +    std::vector<cv::Point2f>::const_iterator iter3 = iter2+1;
 +    std::vector<cv::Point2f>::const_iterator iter4 = iter3+1;
 +    Ellipse ellipse;
 +    if(!estimateSearchArea(*iter4,*iter3,*iter2,CORNERS_SEARCH*3,ellipse))
 +        return false;
 +    if(!ellipse.contains(*iter))
 +        return false;
 +
 +    std::vector<cv::Point2f>::const_iterator iter5 = iter4+1;
 +    for(;iter5 != points.end();++iter5)
 +    {
 +        if(!estimateSearchArea(*iter2,*iter3,*iter4,CORNERS_SEARCH,ellipse,&(*iter)))
 +            return false;
 +        if(!ellipse.contains(*iter5))
 +            return false;
 +        iter = iter2;
 +        iter2 = iter3;
 +        iter3 = iter4;
 +        iter4 = iter5;
 +    }
 +    return true;
 +}
 +
 +cv::Point2f &Chessboard::Board::getCorner(int _row,int _col)
 +{
 +    int _rows = int(rowCount());
 +    int _cols = int(colCount());
 +    if(_row >= _rows || _col >= _cols)
 +        CV_Error(Error::StsBadArg,"out of bound");
 +    if(_row == 0)
 +    {
 +        PointIter iter(top_left,TOP_LEFT);
 +        int count = 0;
 +        do
 +        {
 +            if(count == _col)
 +                return *(*iter);
 +            ++count;
 +        }while(iter.right());
 +    }
 +    else
 +    {
 +        Cell *row_start = top_left;
 +        int count = 1;
 +        do
 +        {
 +            if(count == _row)
 +            {
 +                PointIter iter(row_start,BOTTOM_LEFT);
 +                int count2 = 0;
 +                do
 +                {
 +                    if(count2 == _col)
 +                        return *(*iter);
 +                    ++count2;
 +                }while(iter.right());
 +            }
 +            ++count;
 +            row_start = row_start->bottom;
 +        }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
 +{
 +    return getCell(row,col)->black;
 +}
 +
 +bool Chessboard::Board::isCellEmpty(int row,int col)
 +{
 +    return getCell(row,col)->empty();
 +}
 +
 +Chessboard::Board::Cell* Chessboard::Board::getCell(int row,int col)
 +{
 +    const Cell *cell = const_cast<const Board*>(this)->getCell(row,col);
 +    return const_cast<Cell*>(cell);
 +}
 +
 +const Chessboard::Board::Cell* Chessboard::Board::getCell(int row,int col)const
 +{
 +    if(row > rows-1 || row < 0 || col > cols-1 || col < 0)
 +        CV_Error(Error::StsBadArg,"out of bound");
 +    PointIter p_iter(top_left,BOTTOM_RIGHT);
 +    for(int i=0; i< row; p_iter.bottom(),++i);
 +    for(int i=0; i< col; p_iter.right(),++i);
 +    return p_iter.getCell();
 +}
 +
 +
 +bool Chessboard::Board::isEmpty()const
 +{
 +    return cells.empty();
 +}
 +
 +size_t Chessboard::Board::colCount()const
 +{
 +    return cols;
 +}
 +
 +size_t Chessboard::Board::rowCount()const
 +{
 +    return rows;
 +}
 +
 +cv::Size Chessboard::Board::getSize()const
 +{
 +    return cv::Size(int(colCount()),int(rowCount()));
 +}
 +
 +void Chessboard::Board::drawEllipses(const std::vector<Ellipse> &ellipses)
 +{
 +    // currently there is no global image find way to store global image
 +    // without polluting namespace
 +    if(ellipses.empty())
 +        return;     //avoid compiler warning
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    cv::Mat img;
 +    draw(debug_image,img);
 +    std::vector<Ellipse>::iterator iter;
 +    for(;iter != ellipses.end();++iter)
 +        iter->draw(img);
 +    cv::imshow("chessboard",img);
 +    cv::waitKey(-1);
 +#endif
 +}
 +
 +
 +void Chessboard::Board::growLeft()
 +{
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +    PointIter iter(top_left,TOP_LEFT);
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.right();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.right();
 +        cv::Point2f *p2 = *iter2;
 +        if(iter2.right())
 +            estimatePoint(**iter2,*p2,*p1,*p0,pt);
 +        else
 +            estimatePoint(*p2,*p1,*p0,pt);
 +        points.push_back(pt);
 +    }
 +    while(iter.bottom());
 +    addColumnLeft(points);
 +}
 +
 +bool Chessboard::Board::growLeft(const cv::Mat &map,cv::flann::Index &flann_index)
 +{
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    std::vector<Ellipse> ellipses;
 +#endif
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"growLeft: Board is empty");
 +    PointIter iter(top_left,TOP_LEFT);
 +    std::vector<cv::Point2f> points;
 +    int count = 0;
 +    Ellipse ellipse;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.right();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.right();
 +        cv::Point2f *p2 = *iter2;
 +        cv::Point2f *p3 = NULL;
 +        if(iter2.right())
 +            p3 = *iter2;
 +        if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
 +            return false;
 +        float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
 +        if(pt == *p0)
 +        {
 +            ++count;
 +            points.push_back(ellipse.getCenter());
 +        }
 +        else if(result != 0)
 +        {
 +            points.push_back(pt);
 +            if(result < 0)
 +                ++count;
 +        }
 +        else
 +        {
 +            ++count;
 +            if(pt.x != pt.x)    // NaN check
 +                points.push_back(ellipse.getCenter());
 +            else
 +                points.push_back(pt);
 +        }
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +        ellipses.push_back(ellipse);
 +#endif
 +    }
 +    while(iter.bottom());
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    drawEllipses(ellipses);
 +#endif
 +    if(points.size()-count <= 2)
 +        return false;
 +    if(count > points.size()*0.5 || !checkRowColumn(points))
 +        return false;
 +    addColumnLeft(points);
 +    return true;
 +}
 +
 +void Chessboard::Board::growTop()
 +{
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +    PointIter iter(top_left,TOP_LEFT);
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.bottom();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.bottom();
 +        cv::Point2f *p2 = *iter2;
 +        if(iter2.bottom())
 +            estimatePoint(**iter2,*p2,*p1,*p0,pt);
 +        else
 +            estimatePoint(*p2,*p1,*p0,pt);
 +        points.push_back(pt);
 +    }
 +    while(iter.right());
 +    addRowTop(points);
 +}
 +
 +bool Chessboard::Board::growTop(const cv::Mat &map,cv::flann::Index &flann_index)
 +{
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    std::vector<Ellipse> ellipses;
 +#endif
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +
 +    PointIter iter(top_left,TOP_LEFT);
 +    std::vector<cv::Point2f> points;
 +    int count = 0;
 +    Ellipse ellipse;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.bottom();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.bottom();
 +        cv::Point2f *p2 = *iter2;
 +        cv::Point2f *p3 = NULL;
 +        if(iter2.bottom())
 +            p3 = *iter2;
 +        if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
 +            return false;
 +        float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
 +        if(pt == *p0)
 +        {
 +            ++count;
 +            points.push_back(ellipse.getCenter());
 +        }
 +        else if(result != 0)
 +        {
 +            points.push_back(pt);
 +            if(result < 0)
 +                ++count;
 +        }
 +        else
 +        {
 +            ++count;
 +            if(pt.x != pt.x)    // NaN check
 +                points.push_back(ellipse.getCenter());
 +            else
 +                points.push_back(pt);
 +        }
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +        ellipses.push_back(ellipse);
 +#endif
 +    }
 +    while(iter.right());
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    drawEllipses(ellipses);
 +#endif
 +    if(count > points.size()*0.5 || !checkRowColumn(points))
 +        return false;
 +    addRowTop(points);
 +    return true;
 +}
 +
 +void Chessboard::Board::growRight()
 +{
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +    PointIter iter(top_left,TOP_RIGHT);
 +    while(iter.right());
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.left();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.left();
 +        cv::Point2f *p2 = *iter2;
 +        if(iter2.left())
 +            estimatePoint(**iter2,*p2,*p1,*p0,pt);
 +        else
 +            estimatePoint(*p2,*p1,*p0,pt);
 +        points.push_back(pt);
 +    }
 +    while(iter.bottom());
 +    addColumnRight(points);
 +}
 +
 +bool Chessboard::Board::growRight(const cv::Mat &map,cv::flann::Index &flann_index)
 +{
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    std::vector<Ellipse> ellipses;
 +#endif
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +
 +    PointIter iter(top_left,TOP_RIGHT);
 +    while(iter.right());
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    Ellipse ellipse;
 +    int count = 0;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.left();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.left();
 +        cv::Point2f *p2 = *iter2;
 +        cv::Point2f *p3 = NULL;
 +        if(iter2.left())
 +            p3 = *iter2;
 +        if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
 +            return false;
 +        float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
 +        if(pt == *p0)
 +        {
 +            ++count;
 +            points.push_back(ellipse.getCenter());
 +        }
 +        else if(result != 0)
 +        {
 +            points.push_back(pt);
 +            if(result < 0)
 +                ++count;
 +        }
 +        else
 +        {
 +            ++count;
 +            if(pt.x != pt.x)     // NaN check
 +                points.push_back(ellipse.getCenter());
 +            else
 +                points.push_back(pt);
 +        }
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +        ellipses.push_back(ellipse);
 +#endif
 +    }
 +    while(iter.bottom());
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    drawEllipses(ellipses);
 +#endif
 +    if(count > points.size()*0.5 || !checkRowColumn(points))
 +        return false;
 +    addColumnRight(points);
 +    return true;
 +}
 +
 +void Chessboard::Board::growBottom()
 +{
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +
 +    PointIter iter(top_left,BOTTOM_LEFT);
 +    while(iter.bottom());
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.top();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.top();
 +        cv::Point2f *p2 = *iter2;
 +        if(iter2.top())
 +            estimatePoint(**iter2,*p2,*p1,*p0,pt);
 +        else
 +            estimatePoint(*p2,*p1,*p0,pt);
 +        points.push_back(pt);
 +    }
 +    while(iter.right());
 +    addRowBottom(points);
 +}
 +
 +bool Chessboard::Board::growBottom(const cv::Mat &map,cv::flann::Index &flann_index)
 +{
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    std::vector<Ellipse> ellipses;
 +#endif
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +
 +    PointIter iter(top_left,BOTTOM_LEFT);
 +    while(iter.bottom());
 +    std::vector<cv::Point2f> points;
 +    cv::Point2f pt;
 +    Ellipse ellipse;
 +    int count = 0;
 +    do
 +    {
 +        PointIter iter2(iter);
 +        cv::Point2f *p0 = *iter2;
 +        iter2.top();
 +        cv::Point2f *p1 = *iter2;
 +        iter2.top();
 +        cv::Point2f *p2 = *iter2;
 +        cv::Point2f *p3 = NULL;
 +        if(iter2.top())
 +            p3 = *iter2;
 +        if(!estimateSearchArea(*p2,*p1,*p0,CORNERS_SEARCH,ellipse,p3))
 +            return false;
 +        float result = findMaxPoint(flann_index,map,ellipse,white_angle,black_angle,pt);
 +        if(pt == *p0)
 +        {
 +            ++count;
 +            points.push_back(ellipse.getCenter());
 +        }
 +        else if(result != 0)
 +        {
 +            points.push_back(pt);
 +            if(result < 0)
 +                ++count;
 +        }
 +        else
 +        {
 +            ++count;
 +            if(pt.x != pt.x)     // NaN check
 +                points.push_back(ellipse.getCenter());
 +            else
 +                points.push_back(pt);
 +        }
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +        ellipses.push_back(ellipse);
 +#endif
 +    }
 +    while(iter.right());
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    drawEllipses(ellipses);
 +#endif
 +    if(count > points.size()*0.5 || !checkRowColumn(points))
 +        return false;
 +    addRowBottom(points);
 +    return true;
 +}
 +
 +void Chessboard::Board::addColumnLeft(const std::vector<cv::Point2f> &points)
 +{
 +    if(points.empty() || points.size() != rowCount())
 +        CV_Error(Error::StsBadArg,"wrong number of points");
 +
 +    int offset = int(cells.size());
 +    cells.resize(offset+points.size()-1);
 +    for(int i = offset;i < (int) cells.size();++i)
 +        cells[i] = new Cell();
 +    corners.push_back(new cv::Point2f(points.front()));
 +
 +    Cell *cell = top_left;
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
 +    for(int pos=offset;iter != points.end();++iter,cell = cell->bottom,++pos)
 +    {
 +        cell->left = cells[pos];
 +        cells[pos]->black = !cell->black;
 +        if(pos != offset)
 +            cells[pos]->top = cells[pos-1];
 +        cells[pos]->right = cell;
 +        if(pos +1 < (int)cells.size())
 +            cells[pos]->bottom= cells[pos+1];
 +        cells[pos]->top_left = corners.back();
 +        corners.push_back(new cv::Point2f(*iter));
 +        cells[pos]->bottom_left = corners.back();
 +        cells[pos]->top_right=cell->top_left;
 +        cells[pos]->bottom_right=cell->bottom_left;
 +    }
 +    top_left = cells[offset];
 +    ++cols;
 +}
 +
 +void Chessboard::Board::addRowTop(const std::vector<cv::Point2f> &points)
 +{
 +    if(points.empty() || points.size() != colCount())
 +        CV_Error(Error::StsBadArg,"wrong number of points");
 +
 +    int offset = int(cells.size());
 +    cells.resize(offset+points.size()-1);
 +    for(int i = offset;i < (int) cells.size();++i)
 +        cells[i] = new Cell();
 +    corners.push_back(new cv::Point2f(points.front()));
 +
 +    Cell *cell = top_left;
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
 +    for(int pos=offset;iter != points.end();++iter,cell = cell->right,++pos)
 +    {
 +        cell->top = cells[pos];
 +        cells[pos]->black = !cell->black;
 +        if(pos != offset)
 +            cells[pos]->left= cells[pos-1];
 +        cells[pos]->bottom= cell;
 +        if(pos +1 <(int) cells.size())
 +            cells[pos]->right= cells[pos+1];
 +
 +        cells[pos]->top_left = corners.back();
 +        corners.push_back(new cv::Point2f(*iter));
 +        cells[pos]->top_right = corners.back();
 +        cells[pos]->bottom_left = cell->top_left;
 +        cells[pos]->bottom_right = cell->top_right;
 +    }
 +    top_left = cells[offset];
 +    ++rows;
 +}
 +
 +void Chessboard::Board::addColumnRight(const std::vector<cv::Point2f> &points)
 +{
 +    if(points.empty() || points.size() != rowCount())
 +        CV_Error(Error::StsBadArg,"wrong number of points");
 +
 +    int offset = int(cells.size());
 +    cells.resize(offset+points.size()-1);
 +    for(int i = offset;i < (int) cells.size();++i)
 +        cells[i] = new Cell();
 +    corners.push_back(new cv::Point2f(points.front()));
 +
 +    Cell *cell = top_left;
 +    for(;cell->right;cell = cell->right);
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
 +    for(int pos=offset;iter != points.end();++iter,cell = cell->bottom,++pos)
 +    {
 +        cell->right = cells[pos];
 +        cells[pos]->black = !cell->black;
 +        if(pos != offset)
 +            cells[pos]->top= cells[pos-1];
 +        cells[pos]->left = cell;
 +        if(pos +1 <(int) cells.size())
 +            cells[pos]->bottom= cells[pos+1];
 +
 +        cells[pos]->top_right = corners.back();
 +        corners.push_back(new cv::Point2f(*iter));
 +        cells[pos]->bottom_right = corners.back();
 +        cells[pos]->top_left =cell->top_right;
 +        cells[pos]->bottom_left =cell->bottom_right;
 +    }
 +    ++cols;
 +}
 +
 +void Chessboard::Board::addRowBottom(const std::vector<cv::Point2f> &points)
 +{
 +    if(points.empty() || points.size() != colCount())
 +        CV_Error(Error::StsBadArg,"wrong number of points");
 +
 +    int offset = int(cells.size());
 +    cells.resize(offset+points.size()-1);
 +    for(int i = offset;i < (int) cells.size();++i)
 +        cells[i] = new Cell();
 +    corners.push_back(new cv::Point2f(points.front()));
 +
 +    Cell *cell = top_left;
 +    for(;cell->bottom;cell = cell->bottom);
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin()+1;
 +    for(int pos=offset;iter != points.end();++iter,cell = cell->right,++pos)
 +    {
 +        cell->bottom = cells[pos];
 +        cells[pos]->black = !cell->black;
 +        if(pos != offset)
 +            cells[pos]->left = cells[pos-1];
 +        cells[pos]->top = cell;
 +        if(pos +1 < (int)cells.size())
 +            cells[pos]->right= cells[pos+1];
 +
 +        cells[pos]->bottom_left = corners.back();
 +        corners.push_back(new cv::Point2f(*iter));
 +        cells[pos]->bottom_right = corners.back();
 +        cells[pos]->top_left = cell->bottom_left;
 +        cells[pos]->top_right = cell->bottom_right;
 +    }
 +    ++rows;
 +}
 +
 +bool Chessboard::Board::checkUnique()const
 +{
 +    std::vector<cv::Point2f> points = getCorners(false);
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin();
 +    for(;iter != points.end();++iter)
 +    {
 +        std::vector<cv::Point2f>::const_iterator iter2 = iter+1;
 +        for(;iter2 != points.end();++iter2)
 +        {
 +            if(*iter == *iter2)
 +                return false;
 +        }
 +    }
 +    return true;
 +}
 +
 +int Chessboard::Board::validateCorners(const cv::Mat &data,cv::flann::Index &flann_index,const cv::Mat &h,float min_response)
 +{
 +    // TODO check input
 +    if(isEmpty() || h.empty())
 +        return 0;
 +    int count = 0; int icol = 0;
 +    // first row
 +    PointIter iter(top_left,TOP_LEFT);
 +    cv::Point2f point;
 +    do
 +    {
 +        if((*iter)->x == (*iter)->x)
 +            ++count;
 +        else
 +        {
 +            Ellipse ellipse = estimateSearchArea(h,0,icol,0.4F);
 +            float result = findMaxPoint(flann_index,data,ellipse,white_angle,black_angle,point);
 +            if(fabs(result) >= min_response)
 +            {
 +                ++count;
 +                **iter = point;
 +            }
 +        }
 +        ++icol;
 +    }while(iter.right());
 +
 +    // all other rows
 +    int irow = 1;
 +    Cell *row = top_left;
 +    do
 +    {
 +        PointIter iter2(row,BOTTOM_LEFT);
 +        icol = 0;
 +        do
 +        {
 +            if((*iter2)->x == (*iter2)->x)
 +                ++count;
 +            else
 +            {
 +                Ellipse ellipse = estimateSearchArea(h,irow,icol,0.4F);
 +                if(min_response <= findMaxPoint(flann_index,data,ellipse,white_angle,black_angle,point))
 +                {
 +                    ++count;
 +                    **iter2 = point;
 +                }
 +            }
 +            ++icol;
 +        }while(iter2.right());
 +        row = row->bottom;
 +        ++irow;
 +    }while(row);
 +
 +    // check that there are no points with the same coordinate
 +    std::vector<cv::Point2f> points = getCorners(false);
 +    std::vector<cv::Point2f>::const_iterator iter1 = points.begin();
 +    for(;iter1 != points.end();++iter1)
 +    {
 +        // we do not have to check for NaN because of getCorners(flase)
 +        std::vector<cv::Point2f>::const_iterator iter2 = iter1+1;
 +        for(;iter2 != points.end();++iter2)
 +            if(*iter1 == *iter2)
 +                return -1;  // one corner is there twice -> not valid configuration
 +    }
 +    return count;
 +}
 +
 +bool Chessboard::Board::validateContour()const
 +{
 +    std::vector<cv::Point2f> contour = getContour();
 +    if(contour.size() != 4)
 +    {
 +        return false;
 +    }
 +    cv::Point2f n1 = contour[1]-contour[0];
 +    cv::Point2f n2 = contour[2]-contour[1];
 +    cv::Point2f n3 = contour[3]-contour[2];
 +    cv::Point2f n4 = contour[0]-contour[3];
 +    n1 = n1/cv::norm(n1);
 +    n2 = n2/cv::norm(n2);
 +    n3 = n3/cv::norm(n3);
 +    n4 = n4/cv::norm(n4);
 +    // a > b => cos(a) < cos(b)
 +    if(fabs(n1.dot(n2)) > MIN_COS_ANGLE||
 +            fabs(n2.dot(n3)) > MIN_COS_ANGLE||
 +            fabs(n3.dot(n4)) > MIN_COS_ANGLE||
 +            fabs(n4.dot(n1)) > MIN_COS_ANGLE)
 +        return false;
 +    return true;
 +}
 +
 +std::vector<cv::Point2f> Chessboard::Board::getContour()const
 +{
 +    std::vector<cv::Point2f> points;
 +    if(isEmpty())
 +        return points;
 +
 +    //find start cell part of the contour
 +    Cell* start_cell = NULL;
 +    PointIter iter(top_left,TOP_LEFT);
 +    do
 +    {
 +        PointIter iter2(iter);
 +        do
 +        {
 +            if(!iter2.getCell()->empty())
 +            {
 +                start_cell = iter2.getCell();
 +                iter = iter2;
 +                break;
 +            }
 +        }while(iter2.right());
 +    }while(!start_cell && iter.bottom());
 +    if(start_cell == NULL)
 +        return points;
 +
 +    // trace contour
 +    const cv::Point2f *start_pt = *iter;
 +    int mode = 2; int last = -1;
 +    do
 +    {
 +        PointIter current_iter(iter);
 +        switch(mode)
 +        {
 +        case 1: // top
 +            if(iter.top(true))
 +            {
 +                if(last != 1)
 +                    points.push_back(**current_iter);
 +                mode = 4;
 +                last = 1;
 +                break;
 +            }
 +            /* fallthrough */
 +        case 2: // right
 +            if(iter.right(true))
 +            {
 +                if(last != 2)
 +                    points.push_back(**current_iter);
 +                mode = 1;
 +                last = 2;
 +                break;
 +            }
 +            /* fallthrough */
 +        case 3: // bottom
 +            if(iter.bottom(true))
 +            {
 +                if(last != 3)
 +                    points.push_back(**current_iter);
 +                mode = 2;
 +                last = 3;
 +                break;
 +            }
 +            /* fallthrough */
 +        case 4: // left
 +            if(iter.left(true))
 +            {
 +                if(last != 4)
 +                    points.push_back(**current_iter);
 +                mode = 3;
 +                last = 4;
 +                break;
 +            }
 +            mode = 1;
 +            break;
 +        default:
 +            CV_Error(Error::StsInternal,"cannot retrieve contour");
 +        }
 +    }while(*iter != start_pt);
 +    return points;
 +}
 +
 +
 +cv::Mat Chessboard::Board::estimateHomography(cv::Rect rect,int field_size)const
 +{
 +    int _rows = int(rowCount());
 +    int _cols = int(colCount());
 +    if(_rows < 3  || _cols < 3)
 +        return cv::Mat();
 +    if(rect.width <= 0)
 +        rect.width= _cols;
 +    if(rect.height <= 0)
 +        rect.height= _rows;
 +
 +    int col_end = std::min(rect.x+rect.width,_cols);
 +    int row_end = std::min(rect.y+rect.height,_rows);
 +    std::vector<cv::Point2f> points = getCorners(true);
 +
 +    // build src and dst
 +    std::vector<cv::Point2f> src,dst;
 +    for(int row =rect.y;row < row_end;++row)
 +    {
 +        for(int col=rect.x;col <col_end;++col)
 +        {
 +            const cv::Point2f &pt = points[row*_rows+col];
 +            if(pt.x != pt.x)    // NaN check
 +                continue;
 +            src.push_back(cv::Point2f(float(field_size)*(col+1),float(field_size)*(row+1)));
 +            dst.push_back(pt);
 +        }
 +    }
 +    if(dst.size() < 4)
 +        return cv::Mat();
 +    return cv::findHomography(src, dst,cv::LMEDS);
 +}
 +
 +cv::Mat Chessboard::Board::estimateHomography(int field_size)const
 +{
 +    int _rows = int(rowCount());
 +    int _cols = int(colCount());
 +    if(_rows < 3  || _cols < 3)
 +        return cv::Mat();
 +    std::vector<cv::Point2f> src,dst;
 +    std::vector<cv::Point2f> points = getCorners(true);
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin();
 +    for(int row =0;row < _rows;++row)
 +    {
 +        for(int col=0;col <_cols;++col,++iter)
 +        {
 +            const cv::Point2f &pt = *iter;
 +            if(pt.x == pt.x)
 +            {
 +                src.push_back(cv::Point2f(float(field_size)*(col+1),float(field_size)*(row+1)));
 +                dst.push_back(pt);
 +            }
 +        }
 +    }
 +    if(dst.size() < 4)
 +        return cv::Mat();
 +    return cv::findHomography(src, dst);
 +}
 +
 +bool Chessboard::Board::findNextPoint(cv::flann::Index &index,const cv::Mat &data,
 +        const cv::Point2f &pt1,const cv::Point2f &pt2, const cv::Point2f &pt3,
 +        float white_angle,float black_angle,float min_response,cv::Point2f &point)
 +{
 +    Ellipse ellipse;
 +    if(!estimateSearchArea(pt1,pt2,pt3,0.4F,ellipse))
 +        return false;
 +    if(min_response > fabs(findMaxPoint(index,data,ellipse,white_angle,black_angle,point)))
 +        return false;
 +    return true;
 +}
 +
 +int Chessboard::Board::grow(const cv::Mat &map,cv::flann::Index &flann_index)
 +{
 +    if(isEmpty())
 +        CV_Error(Error::StsInternal,"Board is empty");
 +    bool bleft = true;
 +    bool btop = true;
 +    bool bright = true;
 +    bool bbottom= true;
 +    int count = 0;
 +    do
 +    {
 +        // grow to the left
 +        if(bleft)
 +        {
 +            bleft = growLeft(map,flann_index);
 +            if(bleft)
 +                ++count;
 +        }
 +        if(btop)
 +        {
 +            btop= growTop(map,flann_index);
 +            if(btop)
 +                ++count;
 +        }
 +        if(bright)
 +        {
 +            bright= growRight(map,flann_index);
 +            if(bright)
 +                ++count;
 +        }
 +        if(bbottom)
 +        {
 +            bbottom= growBottom(map,flann_index);
 +            if(bbottom)
 +                ++count;
 +        }
 +    }while(bleft || btop || bright || bbottom );
 +    return count;
 +}
 +
 +std::map<int,int> Chessboard::Board::getMapping()const
 +{
 +    std::map<int,int> map;
 +    std::vector<cv::Point2f> points = getCorners();
 +    std::vector<cv::Point2f>::iterator iter = points.begin();
 +    for(int idx1=0,idx2=0;iter != points.end();++iter,++idx1)
 +    {
 +        if(iter->x != iter->x)  // NaN check
 +            continue;
 +        map[idx1] = idx2++;
 +    }
 +    return map;
 +}
 +
 +std::vector<cv::Point2f> Chessboard::Board::getCorners(bool ball)const
 +{
 +    std::vector<cv::Point2f> points;
 +    if(isEmpty())
 +        return points;
 +
 +    // first row
 +    PointIter iter(top_left,TOP_LEFT);
 +    do
 +    {
 +        if(ball || !iter.isNaN())
 +            points.push_back(*(*iter));
 +    }while(iter.right());
 +
 +    // all other rows
 +    Cell *row = top_left;
 +    do
 +    {
 +        PointIter iter2(row,BOTTOM_LEFT);
 +        do
 +        {
 +            if(ball || !iter2.isNaN())
 +                points.push_back(*(*iter2));
 +        }while(iter2.right());
 +        row = row->bottom;
 +    }while(row);
 +    return points;
 +}
 +
 +std::vector<cv::KeyPoint> Chessboard::Board::getKeyPoints(bool ball)const
 +{
 +    std::vector<cv::KeyPoint> keypoints;
 +    std::vector<cv::Point2f> points = getCorners(ball);
 +    std::vector<cv::Point2f>::const_iterator iter = points.begin();
 +    for(;iter != points.end();++iter)
 +        keypoints.push_back(cv::KeyPoint(iter->x,iter->y,1));
 +    return keypoints;
 +}
 +
 +Chessboard::Chessboard(const Parameters &para)
 +{
 +    reconfigure(para);
 +}
 +
 +void Chessboard::reconfigure(const Parameters &config)
 +{
 +    parameters = config;
 +}
 +
 +Chessboard::Parameters Chessboard::getPara()const
 +{
 +    return parameters;
 +}
 +
 +Chessboard::~Chessboard()
 +{
 +}
 +
 +void Chessboard::findKeyPoints(const cv::Mat& image, std::vector<KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,
 +        std::vector<std::vector<float> > &angles ,const cv::Mat& mask)const
 +{
 +    keypoints.clear();
 +    angles.clear();
 +    vector<KeyPoint> keypoints_temp;
 +    FastX::Parameters para;
 +
 +    para.branches = 2;                    // this is always the case for checssboard corners
 +    para.strength = 10;                   // minimal threshold
 +    para.resolution = float(CV_PI*0.25);   // this gives the best results taking interpolation into account
 +    para.filter = 1;
 +    para.super_resolution = parameters.super_resolution;
 +    para.min_scale = parameters.min_scale;
 +    para.max_scale = parameters.max_scale;
 +
 +    FastX detector(para);
 +    std::vector<cv::Mat> rotated_images;
 +    detector.detectImpl(image,rotated_images,feature_maps,mask);
 +
 +    //calculate seed chessboard corners
 +    detector.findKeyPoints(feature_maps,keypoints_temp,mask);
 +
 +    //sort points and limit number
 +    int max_seeds = std::min((int)keypoints_temp.size(),parameters.max_points);
 +    if(max_seeds < 9)
 +        return;
 +
 +    std::partial_sort(keypoints_temp.begin(),keypoints_temp.begin()+max_seeds-1,
 +            keypoints_temp.end(),sortKeyPoint);
 +    keypoints_temp.resize(max_seeds);
 +    std::vector<std::vector<float> > angles_temp  = detector.calcAngles(rotated_images,keypoints_temp);
 +
 +    // filter out keypoints which are not symmetric
 +    std::vector<KeyPoint>::iterator iter1 = keypoints_temp.begin();
 +    std::vector<std::vector<float> >::const_iterator iter2 = angles_temp.begin();
 +    for(;iter1 != keypoints_temp.end();++iter1,++iter2)
 +    {
 +        cv::KeyPoint &pt = *iter1;
 +        const std::vector<float> &angles_i3 = *iter2;
 +        if(angles_i3.size() != 2)// || pt.response < noise)
 +            continue;
 +        int result = testPointSymmetry(image,pt.pt,pt.size*0.7F,std::max(10.0F,sqrt(pt.response)+0.5F*pt.size));
 +        if(result > MAX_SYMMETRY_ERRORS)
 +            continue;
 +        else if(result > 3)
 +            pt.response = - pt.response;
 +        angles.push_back(angles_i3);
 +        keypoints.push_back(pt);
 +    }
 +}
 +
 +cv::Mat Chessboard::buildData(const std::vector<KeyPoint>& keypoints)const
 +{
 +    cv::Mat data(int(keypoints.size()),4,CV_32FC1);       // x + y + angle + strength
 +    std::vector<cv::KeyPoint>::const_iterator iter = keypoints.begin();
 +    float *val = reinterpret_cast<float*>(data.data);
 +    for(;iter != keypoints.end();++iter)
 +    {
 +        (*val++) = iter->pt.x;
 +        (*val++) = iter->pt.y;
 +        (*val++) = float(2.0*CV_PI-iter->angle/180.0*CV_PI);
 +        (*val++) = iter->response;
 +    }
 +    return data;
 +}
 +
 +std::vector<cv::KeyPoint> Chessboard::getInitialPoints(cv::flann::Index &flann_index,const cv::Mat &data,const cv::KeyPoint &center,float white_angle,float black_angle,float min_response)const
 +{
 +    CV_CheckTypeEQ(data.type(), CV_32FC1, "Unsupported source type");
 +    if(data.cols != 4)
 +        CV_Error(Error::StsBadArg,"wrong data format");
 +
 +    std::vector<float> query,dists;
 +    std::vector<int> indices;
 +    query.resize(2); query[0] = center.pt.x; query[1] = center.pt.y;
 +    flann_index.knnSearch(query,indices,dists,21,cv::flann::SearchParams(32));
 +
 +    // collect all points having a similar angle and response
 +    std::vector<cv::KeyPoint> points;
 +    std::vector<int>::const_iterator ids_iter = indices.begin()+1; // first point is center
 +    points.push_back(center);
 +    for(;ids_iter != indices.end();++ids_iter)
 +    {
 +        // TODO do more angle tests
 +        // test only one angle against the stored one
 +        const float &response = data.at<float>(*ids_iter,3);
 +        if(fabs(response) < min_response)
 +            continue;
 +        const float &angle = data.at<float>(*ids_iter,2);
 +        float angle_temp = fabs(angle-white_angle);
 +        if(angle_temp > CV_PI*0.5)
 +            angle_temp = float(fabs(angle_temp-CV_PI));
 +        if(angle_temp > MAX_ANGLE)
 +        {
 +            angle_temp = fabs(angle-black_angle);
 +            if(angle_temp > CV_PI*0.5)
 +                angle_temp = float(fabs(angle_temp-CV_PI));
 +            if(angle_temp >MAX_ANGLE)
 +                continue;
 +        }
 +        points.push_back(cv::KeyPoint(data.at<float>(*ids_iter,0),data.at<float>(*ids_iter,1),center.size,angle,response));
 +    }
 +    return points;
 +}
 +
 +Chessboard::BState Chessboard::generateBoards(cv::flann::Index &flann_index,const cv::Mat &data,
 +        const cv::KeyPoint &center,float white_angle,float black_angle,float min_response,const cv::Mat& img,
 +        std::vector<Chessboard::Board> &boards)const
 +{
 +    // collect all points having a similar angle
 +    std::vector<cv::KeyPoint> kpoints= getInitialPoints(flann_index,data,center,white_angle,black_angle,min_response);
 +    if(kpoints.size() < 5)
 +        return MISSING_POINTS;
 +
 +    if(!img.empty())
 +    {
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +        cv::Mat out;
 +        cv::drawKeypoints(img,kpoints,out,cv::Scalar(0,0,255,255),4);
 +        std::vector<cv::KeyPoint> temp;
 +        temp.push_back(kpoints.front());
 +        cv::drawKeypoints(out,temp,out,cv::Scalar(0,255,0,255),4);
 +        cv::imshow("chessboard",out);
 +        cv::waitKey(-1);
 +#endif
 +    }
 +
 +    // use angles to filter out points
 +    std::vector<cv::KeyPoint> points;
 +    cv::Vec2f n1(cos(white_angle),-sin(white_angle));
 +    cv::Vec2f n2(cos(black_angle),-sin(black_angle));
 +    std::vector<cv::KeyPoint>::const_iterator iter1 = kpoints.begin()+1; // first point is center
 +    for(;iter1 != kpoints.end();++iter1)
 +    {
 +        // calc angle
 +        cv::Vec2f vec(iter1->pt-center.pt);
 +        vec = vec/cv::norm(vec);
 +        if(fabs(vec.dot(n1)) < 0.96 && fabs(vec.dot(n2)) < 0.96)   //check that angle is bigger than 15°
 +            points.push_back(*iter1);
 +    }
 +
 +    // generate pairs those connection goes through the center
 +    std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> > pairs;
 +    iter1 = points.begin();
 +    for(;iter1 != points.end();++iter1)
 +    {
 +        std::vector<cv::KeyPoint>::const_iterator iter2 = iter1+1;
 +        for(;iter2 != points.end();++iter2)
 +        {
 +            if(isPointOnLine(iter1->pt,iter2->pt,center.pt,0.97F))
 +            {
 +                if(cv::norm(iter1->pt) < cv::norm(iter2->pt))
 +                    pairs.push_back(std::make_pair(*iter1,*iter2));
 +                else
 +                    pairs.push_back(std::make_pair(*iter2,*iter1));
 +            }
 +        }
 +    }
 +
 +    // generate all possible combinations consisting of two pairs
 +    if(pairs.size() < 2)
 +        return MISSING_PAIRS;
 +    std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> >::iterator iter_pair1 = pairs.begin();
 +
 +    BState best_state = MISSING_PAIRS;
 +    for(;iter_pair1 != pairs.end();++iter_pair1)
 +    {
 +        cv::Point2f p1 = iter_pair1->second.pt-iter_pair1->first.pt;
 +        p1 = p1/cv::norm(p1);
 +        std::vector<std::pair<cv::KeyPoint,cv::KeyPoint> >::iterator iter_pair2 = iter_pair1+1;
 +        for(;iter_pair2 != pairs.end();++iter_pair2)
 +        {
 +            cv::Point2f p2 = iter_pair2->second.pt-iter_pair2->first.pt;
 +            p2 = p2/cv::norm(p2);
 +            if(p2.dot(p1) > 0.95)
 +            {
 +                if(best_state < WRONG_PAIR_ANGLE)
 +                    best_state = WRONG_PAIR_ANGLE;
 +            }
 +            else
 +            {
 +                // check orientations
 +                if(checkOrientation(iter_pair1->first.pt,iter_pair1->second.pt,iter_pair2->first.pt,iter_pair2->second.pt))
 +                    std::swap(iter_pair2->first,iter_pair2->second);
 +
 +                // minimal case
 +                std::vector<cv::Point2f> board_points;
 +                board_points.resize(9,cv::Point2f(std::numeric_limits<float>::quiet_NaN(),
 +                            std::numeric_limits<float>::quiet_NaN()));
 +
 +                board_points[1] = iter_pair2->first.pt;
 +                board_points[3] = iter_pair1->first.pt;
 +                board_points[4] = center.pt;
 +                board_points[5] = iter_pair1->second.pt;
 +                board_points[7] = iter_pair2->second.pt;
 +                boards.push_back(Board(cv::Size(3,3),board_points,white_angle,black_angle));
 +                Board &board = boards.back();
 +
 +                if(board.isEmpty())
 +                {
 +                    if(best_state < WRONG_CONFIGURATION)
 +                        best_state = WRONG_CONFIGURATION;
 +                    boards.pop_back(); // MAKE SURE board is no longer used !!!!
 +                    continue;
 +                }
 +                best_state = FOUND_BOARD;
 +            }
 +        }
 +    }
 +    return best_state;
 +}
 +
 +void Chessboard::detectImpl(const Mat& image, vector<KeyPoint>& keypoints,std::vector<Mat> &feature_maps,const Mat& mask)const
 +{
 +    keypoints.clear();
 +    Board board = detectImpl(image,feature_maps,mask);
 +    keypoints = board.getKeyPoints();
 +    return;
 +}
 +
 +Chessboard::Board Chessboard::detectImpl(const Mat& gray,std::vector<cv::Mat> &feature_maps,const Mat& mask)const
 +{
 +#ifdef CV_DETECTORS_CHESSBOARD_DEBUG
 +    debug_image = gray;
 +#endif
 +    CV_CheckTypeEQ(gray.type(),CV_8UC1, "Unsupported image type");
 +
 +    cv::Size chessboard_size2(parameters.chessboard_size.height,parameters.chessboard_size.width);
 +    std::vector<KeyPoint> keypoints_seed;
 +    std::vector<std::vector<float> > angles;
 +    findKeyPoints(gray,keypoints_seed,feature_maps,angles,mask);
 +    if(keypoints_seed.empty())
 +        return Chessboard::Board();
 +
 +    // check how many points are likely a checkerbord corner
 +    float response = fabs(keypoints_seed.front().response*MIN_RESPONSE_RATIO);
 +    std::vector<KeyPoint>::const_iterator seed_iter = keypoints_seed.begin();
 +    int count = 0;
 +    int inum = chessboard_size2.width*chessboard_size2.height;
 +    for(;seed_iter != keypoints_seed.end() && count < inum;++seed_iter,++count)
 +    {
 +        // points are sorted based on response
 +        if(fabs(seed_iter->response) < response)
 +        {
 +            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));
 +
 +    //build kd tree
 +    cv::Mat data = buildData(keypoints_seed);
 +    cv::Mat flann_data(data.rows,2,CV_32FC1);
 +    data(cv::Rect(0,0,2,data.rows)).copyTo(flann_data);
 +    cv::flann::Index flann_index(flann_data,cv::flann::KDTreeIndexParams(1),cvflann::FLANN_DIST_EUCLIDEAN);
 +
 +    // for each point
 +    std::vector<std::vector<float> >::const_iterator angles_iter = angles.begin();
 +    std::vector<cv::KeyPoint>::const_iterator points_iter = keypoints_seed.begin();
 +    cv::Rect bounding_box(5,5,gray.cols-10,gray.rows-10);
 +    int max_tests = std::min(parameters.max_tests,int(keypoints_seed.size()));
 +    for(count=0;count < max_tests;++angles_iter,++points_iter,++count)
 +    {
 +        // regard current point as center point
 +        // which must have two angles!!! (this was already checked)
 +        float min_response = points_iter->response*MIN_RESPONSE_RATIO;
 +        if(min_response <= 0)
 +        {
 +            if(max_tests+1 < int(keypoints_seed.size()))
 +                ++max_tests;
 +            continue;
 +        }
 +        const std::vector<float> &angles_i = *angles_iter;
 +        float white_angle =  fabs(angles_i.front());  // angle is negative if black --> clockwise
 +        float black_angle =  fabs(angles_i.back());   // angle is negative if black --> clockwise
 +        if(angles_i.front() < 0)                 // ensure white angle is first
 +            swap(white_angle,black_angle);
 +
 +        std::vector<Board> boards;
 +        generateBoards(flann_index, data,*points_iter,white_angle,black_angle,min_response,gray,boards);
 +        parallel_for_(Range(0,(int)boards.size()),[&](const Range& range){
 +            for(int i=range.start;i <range.end;++i)
 +            {
 +                auto iter_boards = boards.begin()+i;
 +                cv::Mat h = iter_boards->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;
 +                }
 +
 +                // check bounding box
 +                std::vector<cv::Point2f> contour = iter_boards->getContour();
 +                std::vector<cv::Point2f>::const_iterator iter = contour.begin();
 +                for(;iter != contour.end();++iter)
 +                {
 +                    if(!bounding_box.contains(*iter))
 +                        break;
 +                }
 +                if(iter != contour.end())
 +                {
 +                    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);
 +#endif
 +                }
 +                else
 +                {
 +                    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();
 +}
 +
 +void Chessboard::detectAndCompute(cv::InputArray image,cv::InputArray mask,std::vector<cv::KeyPoint>& keypoints,
 +        cv::OutputArray descriptors,bool useProvidedKeyPoints)
 +{
 +    descriptors.clear();
 +    useProvidedKeyPoints=false;
 +    std::vector<cv::Mat> maps;
 +    detectImpl(image.getMat(),keypoints,maps,mask.getMat());
 +    if(!useProvidedKeyPoints)        // suppress compiler warning
 +        return;
 +    return;
 +}
 +
 +void Chessboard::detectImpl(const Mat& image, vector<KeyPoint>& keypoints,const Mat& mask)const
 +{
 +    std::vector<cv::Mat> maps;
 +    detectImpl(image,keypoints,maps,mask);
 +}
 +
 +void Chessboard::detectImpl(InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask)const
 +{
 +    detectImpl(image.getMat(),keypoints,mask.getMat());
 +}
 +
 +} // end namespace details
 +
 +
 +// public API
 +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);
 +    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)
 +    {
 +        CV_Error(Error::StsOutOfRange, "Both width and height of the pattern should have bigger than 2");
 +    }
 +    if (!corners_.needed())
 +        CV_Error(Error::StsNullPtr, "Null pointer to corners");
 +
 +    Mat img;
 +    if (image_.channels() != 1)
 +        cvtColor(image_, img, COLOR_BGR2GRAY);
 +    else
 +        img = image_.getMat();
 +
 +    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;
 +
 +    // setup search based on flags
 +    if(flags & CALIB_CB_NORMALIZE_IMAGE)
 +    {
 +        Mat tmp;
 +        cv::equalizeHist(img, tmp);
 +        swap(img, tmp);
 +        flags ^= CALIB_CB_NORMALIZE_IMAGE;
 +    }
 +    if(flags & CALIB_CB_EXHAUSTIVE)
 +    {
 +        para.max_tests = 100;
 +        para.max_points = std::max(500,pattern_size.width*pattern_size.height*2);
 +        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 remaining flags %d", (int)flags));
 +
 +    std::vector<cv::KeyPoint> corners;
 +    details::Chessboard board(para);
 +    board.detect(img,corners);
 +    if(corners.empty())
 +    {
 +        corners_.release();
 +        return false;
 +    }
 +    std::vector<cv::Point2f> points;
 +    KeyPoint::convert(corners,points);
 +    Mat(points).copyTo(corners_);
 +    return true;
 +}
 +
 +} // namespace cv
index 99918b4,0000000..41c64cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,770 -1,0 +1,768 @@@
-         Ellipse(const Ellipse &other);
 +// This file is part of OpenCV project.
 +// It is subject to the license terms in the LICENSE file found in the top-level directory
 +// of this distribution and at http://opencv.org/license.html.
 +
 +#ifndef CHESSBOARD_HPP_
 +#define CHESSBOARD_HPP_
 +
 +#include "opencv2/core.hpp"
 +#include "opencv2/features2d.hpp"
 +#include <vector>
 +#include <set>
 +#include <map>
 +
 +namespace cv {
 +namespace details{
 +/**
 + * \brief Fast point sysmetric cross detector based on a localized radon transformation
 + */
 +class FastX : public cv::Feature2D
 +{
 +    public:
 +        struct Parameters
 +        {
 +            float strength;       //!< minimal strength of a valid junction in dB
 +            float resolution;     //!< angle resolution in radians
 +            int branches;         //!< the number of branches
 +            int min_scale;        //!< scale level [0..8]
 +            int max_scale;        //!< scale level [0..8]
 +            bool filter;          //!< post filter feature map to improve impulse response
 +            bool super_resolution; //!< up-sample
 +
 +            Parameters()
 +            {
 +                strength = 40;
 +                resolution = float(CV_PI*0.25);
 +                branches = 2;
 +                min_scale = 2;
 +                max_scale = 5;
 +                super_resolution = 1;
 +                filter = true;
 +            }
 +        };
 +
 +    public:
 +        FastX(const Parameters &config = Parameters());
 +        virtual ~FastX(){};
 +
 +        void reconfigure(const Parameters &para);
 +
 +        //declaration to be wrapped by rbind
 +        void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override
 +        {cv::Feature2D::detect(image.getMat(),keypoints,mask.getMat());}
 +
 +        virtual void detectAndCompute(cv::InputArray image,
 +                                              cv::InputArray mask,
 +                                              std::vector<cv::KeyPoint>& keypoints,
 +                                              cv::OutputArray descriptors,
 +                                              bool useProvidedKeyPoints = false)override;
 +
 +        void detectImpl(const cv::Mat& image,
 +                               std::vector<cv::KeyPoint>& keypoints,
 +                               std::vector<cv::Mat> &feature_maps,
 +                               const cv::Mat& mask=cv::Mat())const;
 +
 +        void detectImpl(const cv::Mat& image,
 +                                std::vector<cv::Mat> &rotated_images,
 +                                std::vector<cv::Mat> &feature_maps,
 +                                const cv::Mat& mask=cv::Mat())const;
 +
 +        void findKeyPoints(const std::vector<cv::Mat> &feature_map,
 +                                   std::vector<cv::KeyPoint>& keypoints,
 +                                   const cv::Mat& mask = cv::Mat())const;
 +
 +        std::vector<std::vector<float> > calcAngles(const std::vector<cv::Mat> &rotated_images,
 +                                                            std::vector<cv::KeyPoint> &keypoints)const;
 +        // define pure virtual methods
 +        virtual int descriptorSize()const override{return 0;};
 +        virtual int descriptorType()const override{return 0;};
 +        virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const
 +        {
 +            descriptors.clear();
 +            detectImpl(image.getMat(),keypoints,mask);
 +            if(!useProvidedKeypoints)        // suppress compiler warning
 +                return;
 +            return;
 +        }
 +
 +    protected:
 +        virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const
 +        {
 +            descriptors = cv::Mat();
 +            detectImpl(image,keypoints);
 +        }
 +
 +    private:
 +        void detectImpl(const cv::Mat& _src, std::vector<cv::KeyPoint>& keypoints, const cv::Mat& mask)const;
 +        virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const;
 +
 +        void rotate(float angle,const cv::Mat &img,cv::Size size,cv::Mat &out)const;
 +        void calcFeatureMap(const cv::Mat &images,cv::Mat& out)const;
 +
 +    private:
 +        Parameters parameters;
 +};
 +
 +/**
 + * \brief Ellipse class
 + */
 +class Ellipse
 +{
 +    public:
 +        Ellipse();
 +        Ellipse(const cv::Point2f &center, const cv::Size2f &axes, float angle);
 +
 +        void draw(cv::InputOutputArray img,const cv::Scalar &color = cv::Scalar::all(120))const;
 +        bool contains(const cv::Point2f &pt)const;
 +        cv::Point2f getCenter()const;
 +        const cv::Size2f &getAxes()const;
 +
 +    private:
 +        cv::Point2f center;
 +        cv::Size2f axes;
 +        float angle,cosf,sinf;
 +};
 +
 +/**
 + * \brief Chessboard corner detector
 + *
 + * The detectors tries to find all chessboard corners of an imaged
 + * chessboard and returns them as an ordered vector of KeyPoints.
 + * Thereby, the left top corner has index 0 and the bottom right
 + * corner n*m-1.
 + */
 +class Chessboard: public cv::Feature2D
 +{
 +    public:
 +        static const int DUMMY_FIELD_SIZE = 100;  // in pixel
 +
 +        /**
 +         * \brief Configuration of a chessboard corner detector
 +         *
 +         */
 +        struct Parameters
 +        {
 +            cv::Size chessboard_size; //!< size of the chessboard
 +            int min_scale;            //!< scale level [0..8]
 +            int max_scale;            //!< scale level [0..8]
 +            int max_points;           //!< maximal number of points regarded
 +            int max_tests;            //!< maximal number of tested hypothesis
 +            bool super_resolution;    //!< use super-repsolution for chessboard detection
 +            bool larger;              //!< indicates if larger boards should be returned
 +
 +            Parameters()
 +            {
 +                chessboard_size = cv::Size(9,6);
 +                min_scale = 2;
 +                max_scale = 4;
 +                super_resolution = true;
 +                max_points = 400;
 +                max_tests = 100;
 +                larger = false;
 +            }
 +
 +            Parameters(int scale,int _max_points):
 +                min_scale(scale),
 +                max_scale(scale),
 +                max_points(_max_points)
 +            {
 +                chessboard_size = cv::Size(9,6);
 +            }
 +        };
 +
 +
 +        /**
 +         * \brief Gets the 3D objects points for the chessboard assuming the
 +         * left top corner is located at the origin.
 +         *
 +         * \param[in] pattern_size Number of rows and cols of the pattern
 +         * \param[in] cell_size Size of one cell
 +         *
 +         * \returns Returns the object points as CV_32FC3
 +         */
 +        static cv::Mat getObjectPoints(const cv::Size &pattern_size,float cell_size);
 +
 +        /**
 +         * \brief Class for searching and storing chessboard corners.
 +         *
 +         * The search is based on a feature map having strong pixel
 +         * values at positions where a chessboard corner is located.
 +         *
 +         * The board must be rectangular but supports empty cells
 +         *
 +         */
 +        class Board
 +        {
 +            public:
 +                /**
 +                 * \brief Estimates the position of the next point on a line using cross ratio constrain
 +                 *
 +                 * cross ratio:
 +                 * d12/d34 = d13/d24
 +                 *
 +                 * point order on the line:
 +                 * pt1 --> pt2 --> pt3 --> pt4
 +                 *
 +                 * \param[in] pt1 First point coordinate
 +                 * \param[in] pt2 Second point coordinate
 +                 * \param[in] pt3 Third point coordinate
 +                 * \param[out] pt4 Forth point coordinate
 +                 *
 +                 */
 +                static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,cv::Point2f &p3);
 +
 +                // using 1D homography
 +                static bool estimatePoint(const cv::Point2f &p0,const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3, cv::Point2f &p4);
 +
 +                /**
 +                 * \brief Checks if all points of a row or column have a valid cross ratio constraint
 +                 *
 +                 * cross ratio:
 +                 * d12/d34 = d13/d24
 +                 *
 +                 * point order on the row/column:
 +                 * pt1 --> pt2 --> pt3 --> pt4
 +                 *
 +                 * \param[in] points THe points of the row/column
 +                 *
 +                 */
 +                static bool checkRowColumn(const std::vector<cv::Point2f> &points);
 +
 +                /**
 +                 * \brief Estimates the search area for the next point on the line using cross ratio
 +                 *
 +                 * point order on the line:
 +                 * (p0) --> p1 --> p2 --> p3 --> search area
 +                 *
 +                 * \param[in] p1 First point coordinate
 +                 * \param[in] p2 Second point coordinate
 +                 * \param[in] p3 Third point coordinate
 +                 * \param[in] p Percentage of d34 used for the search area width and height [0..1]
 +                 * \param[out] ellipse The search area
 +                 * \param[in] p0 optional point to improve accuracy
 +                 *
 +                 * \return Returns false if no search area can be calculated
 +                 *
 +                 */
 +                static bool estimateSearchArea(const cv::Point2f &p1,const cv::Point2f &p2,const cv::Point2f &p3,float p,
 +                                                       Ellipse &ellipse,const cv::Point2f *p0 =NULL);
 +
 +                /**
 +                 * \brief Estimates the search area for a specific point based on the given homography
 +                 *
 +                 * \param[in] H homography describing the transformation from ideal board to real one
 +                 * \param[in] row Row of the point
 +                 * \param[in] col Col of the point
 +                 * \param[in] p Percentage [0..1]
 +                 *
 +                 * \return Returns false if no search area can be calculated
 +                 *
 +                 */
 +                static Ellipse estimateSearchArea(cv::Mat H,int row, int col,float p,int field_size = DUMMY_FIELD_SIZE);
 +
 +                /**
 +                 * \brief Searches for the maximum in a given search area
 +                 *
 +                 * \param[in] map feature map
 +                 * \param[in] ellipse search area
 +                 * \param[in] min_val Minimum value of the maximum to be accepted as maximum
 +                 *
 +                 * \return Returns a negative value if all points are outside the ellipse
 +                 *
 +                 */
 +                static float findMaxPoint(cv::flann::Index &index,const cv::Mat &data,const Ellipse &ellipse,float white_angle,float black_angle,cv::Point2f &pt);
 +
 +                /**
 +                 * \brief Searches for the next point using cross ratio constrain
 +                 *
 +                 * \param[in] index flann index
 +                 * \param[in] data extended flann data
 +                 * \param[in] pt1
 +                 * \param[in] pt2
 +                 * \param[in] pt3
 +                 * \param[in] white_angle
 +                 * \param[in] black_angle
 +                 * \param[in] min_response
 +                 * \param[out] point The resulting point
 +                 *
 +                 * \return Returns false if no point could be found
 +                 *
 +                 */
 +                static bool findNextPoint(cv::flann::Index &index,const cv::Mat &data,
 +                                                  const cv::Point2f &pt1,const cv::Point2f &pt2, const cv::Point2f &pt3,
 +                                                  float white_angle,float black_angle,float min_response,cv::Point2f &point);
 +
 +                /**
 +                 * \brief Creates a new Board object
 +                 *
 +                 */
 +                Board(float white_angle=0,float black_angle=0);
 +                Board(const cv::Size &size, const std::vector<cv::Point2f> &points,float white_angle=0,float black_angle=0);
 +                Board(const Chessboard::Board &other);
 +                virtual ~Board();
 +
 +                Board& operator=(const Chessboard::Board &other);
 +
 +                /**
 +                 * \brief Draws the corners into the given image
 +                 *
 +                 * \param[in] m The image
 +                 * \param[out] m The resulting image
 +                 * \param[in] H optional homography to calculate search area
 +                 *
 +                 */
 +                void draw(cv::InputArray m,cv::OutputArray out,cv::InputArray H=cv::Mat())const;
 +
 +                /**
 +                 * \brief Estimates the pose of the chessboard
 +                 *
 +                 */
 +                bool estimatePose(const cv::Size2f &real_size,cv::InputArray _K,cv::OutputArray rvec,cv::OutputArray tvec)const;
 +
 +                /**
 +                 * \brief Clears all internal data of the object
 +                 *
 +                 */
 +                void clear();
 +
 +                /**
 +                 * \brief Returns the angle of the black diagnonale
 +                 *
 +                 */
 +                float getBlackAngle()const;
 +
 +                /**
 +                 * \brief Returns the angle of the black diagnonale
 +                 *
 +                 */
 +                float getWhiteAngle()const;
 +
 +                /**
 +                 * \brief Initializes a 3x3 grid from 9 corner coordinates
 +                 *
 +                 * All points must be ordered:
 +                 * p0 p1 p2
 +                 * p3 p4 p5
 +                 * p6 p7 p8
 +                 *
 +                 * \param[in] points vector of points
 +                 *
 +                 * \return Returns false if the grid could not be initialized
 +                 */
 +                bool init(const std::vector<cv::Point2f> points);
 +
 +                /**
 +                 * \brief Returns true if the board is empty
 +                 *
 +                 */
 +                bool isEmpty() const;
 +
 +                /**
 +                 * \brief Returns all board corners as ordered vector
 +                 *
 +                 * The left top corner has index 0 and the bottom right
 +                 * corner rows*cols-1. All corners which only belong to
 +                 * empty cells are returned as NaN.
 +                 */
 +                std::vector<cv::Point2f> getCorners(bool ball=true) const;
 +
 +                /**
 +                 * \brief Returns all board corners as ordered vector of KeyPoints
 +                 *
 +                 * The left top corner has index 0 and the bottom right
 +                 * corner rows*cols-1.
 +                 *
 +                 * \param[in] ball if set to false only non empty points are returned
 +                 *
 +                 */
 +                std::vector<cv::KeyPoint> getKeyPoints(bool ball=true) const;
 +
 +                /**
 +                 * \brief Returns the centers of the chessboard cells
 +                 *
 +                 * The left top corner has index 0 and the bottom right
 +                 * corner (rows-1)*(cols-1)-1.
 +                 *
 +                 */
 +                std::vector<cv::Point2f> getCellCenters() const;
 +
 +                /**
 +                 * \brief Estimates the homography between an ideal board
 +                 * and reality based on the already recovered points
 +                 *
 +                 * \param[in] rect selecting a subset of the already recovered points
 +                 * \param[in] field_size The field size of the ideal board
 +                 *
 +                 */
 +                cv::Mat estimateHomography(cv::Rect rect,int field_size = DUMMY_FIELD_SIZE)const;
 +
 +                /**
 +                 * \brief Estimates the homography between an ideal board
 +                 * and reality based on the already recovered points
 +                 *
 +                 * \param[in] field_size The field size of the ideal board
 +                 *
 +                 */
 +                cv::Mat estimateHomography(int field_size = DUMMY_FIELD_SIZE)const;
 +
 +                /**
 +                 * \brief Returns the size of the board
 +                 *
 +                 */
 +                cv::Size getSize() const;
 +
 +                /**
 +                 * \brief Returns the number of cols
 +                 *
 +                 */
 +                size_t colCount() const;
 +
 +                /**
 +                 * \brief Returns the number of rows
 +                 *
 +                 */
 +                size_t rowCount() const;
 +
 +                /**
 +                 * \brief Returns the inner contour of the board including only valid corners
 +                 *
 +                 * \info the contour might be non squared if not all points of the board are defined
 +                 *
 +                 */
 +                std::vector<cv::Point2f> getContour()const;
 +
 +
 +                /**
 +                 * \brief Grows the board in all direction until no more corners are found in the feature map
 +                 *
 +                 * \param[in] data CV_32FC1 data of the flann index
 +                 * \param[in] flann_index flann index
 +                 *
 +                 * \returns the number of grows
 +                 */
 +                int grow(const cv::Mat &data,cv::flann::Index &flann_index);
 +
 +                /**
 +                 * \brief Validates all corners using guided search based on the given homography
 +                 *
 +                 * \param[in] data CV_32FC1 data of the flann index
 +                 * \param[in] flann_index flann index
 +                 * \param[in] h Homography describing the transformation from ideal board to the real one
 +                 * \param[in] min_response Min response
 +                 *
 +                 * \returns the number of valid corners
 +                 */
 +                int validateCorners(const cv::Mat &data,cv::flann::Index &flann_index,const cv::Mat &h,float min_response=0);
 +
 +                /**
 +                 * \brief check that no corner is used more than once
 +                 *
 +                 * \returns Returns false if a corner is used more than once
 +                 */
 +                 bool checkUnique()const;
 +
 +                 /**
 +                  * \brief Returns false if the angles of the contour are smaller than 35°
 +                  *
 +                  */
 +                 bool validateContour()const;
 +
 +                /**
 +                 * \brief Grows the board to the left by adding one column.
 +                 *
 +                 * \param[in] map CV_32FC1 feature map
 +                 *
 +                 * \returns Returns false if the feature map has no maxima at the requested positions
 +                 */
 +                bool growLeft(const cv::Mat &map,cv::flann::Index &flann_index);
 +                void growLeft();
 +
 +                /**
 +                 * \brief Grows the board to the top by adding one row.
 +                 *
 +                 * \param[in] map CV_32FC1 feature map
 +                 *
 +                 * \returns Returns false if the feature map has no maxima at the requested positions
 +                 */
 +                bool growTop(const cv::Mat &map,cv::flann::Index &flann_index);
 +                void growTop();
 +
 +                /**
 +                 * \brief Grows the board to the right by adding one column.
 +                 *
 +                 * \param[in] map CV_32FC1 feature map
 +                 *
 +                 * \returns Returns false if the feature map has no maxima at the requested positions
 +                 */
 +                bool growRight(const cv::Mat &map,cv::flann::Index &flann_index);
 +                void growRight();
 +
 +                /**
 +                 * \brief Grows the board to the bottom by adding one row.
 +                 *
 +                 * \param[in] map CV_32FC1 feature map
 +                 *
 +                 * \returns Returns false if the feature map has no maxima at the requested positions
 +                 */
 +                bool growBottom(const cv::Mat &map,cv::flann::Index &flann_index);
 +                void growBottom();
 +
 +                /**
 +                 * \brief Adds one column on the left side
 +                 *
 +                 * \param[in] points The corner coordinates
 +                 *
 +                 */
 +                void addColumnLeft(const std::vector<cv::Point2f> &points);
 +
 +                /**
 +                 * \brief Adds one column at the top
 +                 *
 +                 * \param[in] points The corner coordinates
 +                 *
 +                 */
 +                void addRowTop(const std::vector<cv::Point2f> &points);
 +
 +                /**
 +                 * \brief Adds one column on the right side
 +                 *
 +                 * \param[in] points The corner coordinates
 +                 *
 +                 */
 +                void addColumnRight(const std::vector<cv::Point2f> &points);
 +
 +                /**
 +                 * \brief Adds one row at the bottom
 +                 *
 +                 * \param[in] points The corner coordinates
 +                 *
 +                 */
 +                void addRowBottom(const std::vector<cv::Point2f> &points);
 +
 +                /**
 +                 * \brief Rotates the board 90° degrees to the left
 +                 */
 +                void rotateLeft();
 +
 +                /**
 +                 * \brief Rotates the board 90° degrees to the right
 +                 */
 +                void rotateRight();
 +
 +                /**
 +                 * \brief Flips the board along its local x(width) coordinate direction
 +                 */
 +                void flipVertical();
 +
 +                /**
 +                 * \brief Flips the board along its local y(height) coordinate direction
 +                 */
 +                void flipHorizontal();
 +
 +                /**
 +                 * \brief Flips and rotates the board so that the angle of
 +                 * either the black or white diagonal is bigger than the x
 +                 * and y axis of the board and from a right handed
 +                 * coordinate system
 +                 */
 +                void normalizeOrientation(bool bblack=true);
 +
 +                /**
 +                 * \brief Exchanges the stored board with the board stored in other
 +                 */
 +                void swap(Chessboard::Board &other);
 +
 +                bool operator==(const Chessboard::Board& other) const {return rows*cols == other.rows*other.cols;};
 +                bool operator< (const Chessboard::Board& other) const {return rows*cols < other.rows*other.cols;};
 +                bool operator> (const Chessboard::Board& other) const {return rows*cols > other.rows*other.cols;};
 +                bool operator>= (const cv::Size& size)const { return rows*cols >= size.width*size.height; };
 +
 +                /**
 +                 * \brief Returns a specific corner
 +                 *
 +                 * \info raises runtime_error if row col does not exists
 +                 */
 +                cv::Point2f& getCorner(int row,int col);
 +
 +                /**
 +                 * \brief Returns true if the cell is empty meaning at least one corner is NaN
 +                 */
 +                bool isCellEmpty(int row,int col);
 +
 +                /**
 +                 * \brief Returns the mapping from all corners idx to only valid corners idx
 +                 */
 +                std::map<int,int> getMapping()const;
 +
 +                /**
 +                 * \brief Estimates rotation of the board around the camera axis
 +                 */
 +                 double estimateRotZ()const;
 +
 +                /**
 +                 * \brief Returns true if the cell is black
 +                 *
 +                 */
 +                 bool isCellBlack(int row,int cola)const;
 +
 +            private:
 +                // stores one cell
 +                // in general a cell is initialized by the Board so that:
 +                // * all corners are always pointing to a valid cv::Point2f
 +                // * depending on the position left,top,right and bottom might be set to NaN
 +                // * A cell is empty if at least one corner is NaN
 +                struct Cell
 +                {
 +                    cv::Point2f *top_left,*top_right,*bottom_right,*bottom_left; // corners
 +                    Cell *left,*top,*right,*bottom;         // neighbouring cells
 +                    bool black;                             // set to true if cell is black
 +                    Cell();
 +                    bool empty()const;                      // indicates if the cell is empty (one of its corners has NaN)
 +                    int getRow()const;
 +                    int getCol()const;
 +                };
 +
 +                // corners
 +                enum CornerIndex
 +                {
 +                    TOP_LEFT,
 +                    TOP_RIGHT,
 +                    BOTTOM_RIGHT,
 +                    BOTTOM_LEFT
 +                };
 +
 +                Cell* getCell(int row,int column); // returns a specific cell
 +                const Cell* getCell(int row,int column)const; // returns a specific cell
 +                void drawEllipses(const std::vector<Ellipse> &ellipses);
 +
 +                // Iterator for iterating over board corners
 +                class PointIter
 +                {
 +                    public:
 +                        PointIter(Cell *cell,CornerIndex corner_index);
 +                        PointIter(const PointIter &other);
 +                        void operator=(const PointIter &other);
 +                        bool valid() const;                   // returns if the pointer is pointing to a cell
 +
 +                        bool left(bool check_empty=false);    // moves one corner to the left or returns false
 +                        bool right(bool check_empty=false);   // moves one corner to the right or returns false
 +                        bool bottom(bool check_empty=false);  // moves one corner to the bottom or returns false
 +                        bool top(bool check_empty=false);     // moves one corner to the top or returns false
 +                        bool checkCorner()const;              // returns true if the current corner belongs to at least one
 +                                                              // none empty cell
 +                        bool isNaN()const;                    // returns true if the currnet corner is NaN
 +
 +                        const cv::Point2f* operator*() const;  // current corner coordinate
 +                        cv::Point2f* operator*();              // current corner coordinate
 +                        const cv::Point2f* operator->() const; // current corner coordinate
 +                        cv::Point2f* operator->();             // current corner coordinate
 +
 +                        Cell *getCell();                 // current cell
 +                    private:
 +                        CornerIndex corner_index;
 +                        Cell *cell;
 +                };
 +
 +                std::vector<Cell*> cells;          // storage for all board cells
 +                std::vector<cv::Point2f*> corners; // storage for all corners
 +                Cell *top_left;                    // pointer to the top left corner of the board in its local coordinate system
 +                int rows;                          // number of row cells
 +                int cols;                          // number of col cells
 +                float white_angle,black_angle;
 +        };
 +    public:
 +
 +        /**
 +         * \brief Creates a chessboard corner detectors
 +         *
 +         * \param[in] config Configuration used to detect chessboard corners
 +         *
 +         */
 +        Chessboard(const Parameters &config = Parameters());
 +        virtual ~Chessboard();
 +        void reconfigure(const Parameters &config = Parameters());
 +        Parameters getPara()const;
 +
 +        /*
 +         * \brief Detects chessboard corners in the given image.
 +         *
 +         * The detectors tries to find all chessboard corners of an imaged
 +         * chessboard and returns them as an ordered vector of KeyPoints.
 +         * Thereby, the left top corner has index 0 and the bottom right
 +         * corner n*m-1.
 +         *
 +         * \param[in] image The image
 +         * \param[out] keypoints The detected corners as a vector of ordered KeyPoints
 +         * \param[in] mask Currently not supported
 +         *
 +         */
 +        void detect(cv::InputArray image,std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::Mat())override
 +        {cv::Feature2D::detect(image.getMat(),keypoints,mask.getMat());}
 +
 +        virtual void detectAndCompute(cv::InputArray image,cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints,cv::OutputArray descriptors,
 +                                              bool useProvidedKeyPoints = false)override;
 +
 +        /*
 +         * \brief Detects chessboard corners in the given image.
 +         *
 +         * The detectors tries to find all chessboard corners of an imaged
 +         * chessboard and returns them as an ordered vector of KeyPoints.
 +         * Thereby, the left top corner has index 0 and the bottom right
 +         * corner n*m-1.
 +         *
 +         * \param[in] image The image
 +         * \param[out] keypoints The detected corners as a vector of ordered KeyPoints
 +         * \param[out] feature_maps The feature map generated by LRJT and used to find the corners
 +         * \param[in] mask Currently not supported
 +         *
 +         */
 +        void detectImpl(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const;
 +        Chessboard::Board detectImpl(const cv::Mat& image,std::vector<cv::Mat> &feature_maps,const cv::Mat& mask)const;
 +
 +        // define pure virtual methods
 +        virtual int descriptorSize()const override{return 0;};
 +        virtual int descriptorType()const override{return 0;};
 +        virtual void operator()( cv::InputArray image, cv::InputArray mask, std::vector<cv::KeyPoint>& keypoints, cv::OutputArray descriptors, bool useProvidedKeypoints=false )const
 +        {
 +            descriptors.clear();
 +            detectImpl(image.getMat(),keypoints,mask);
 +            if(!useProvidedKeypoints)        // suppress compiler warning
 +                return;
 +            return;
 +        }
 +
 +    protected:
 +        virtual void computeImpl( const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints, cv::Mat& descriptors)const
 +        {
 +            descriptors = cv::Mat();
 +            detectImpl(image,keypoints);
 +        }
 +
 +        // indicates why a board could not be initialized for a certain keypoint
 +        enum BState
 +        {
 +            MISSING_POINTS = 0,       // at least 5 points are needed
 +            MISSING_PAIRS = 1,        // at least two pairs are needed
 +            WRONG_PAIR_ANGLE = 2,     // angle between pairs is too small
 +            WRONG_CONFIGURATION = 3,  // point configuration is wrong and does not belong to a board
 +            FOUND_BOARD = 4           // board was found
 +        };
 +
 +        void findKeyPoints(const cv::Mat& image, std::vector<cv::KeyPoint>& keypoints,std::vector<cv::Mat> &feature_maps,
 +                           std::vector<std::vector<float> > &angles ,const cv::Mat& mask)const;
 +        cv::Mat buildData(const std::vector<cv::KeyPoint>& keypoints)const;
 +        std::vector<cv::KeyPoint> getInitialPoints(cv::flann::Index &flann_index,const cv::Mat &data,const cv::KeyPoint &center,float white_angle,float black_angle, float min_response = 0)const;
 +        BState generateBoards(cv::flann::Index &flann_index,const cv::Mat &data, const cv::KeyPoint &center,
 +                             float white_angle,float black_angle,float min_response,const cv::Mat &img,
 +                             std::vector<Chessboard::Board> &boards)const;
 +
 +    private:
 +        void detectImpl(const cv::Mat&,std::vector<cv::KeyPoint>&, const cv::Mat& mast =cv::Mat())const;
 +        virtual void detectImpl(cv::InputArray image, std::vector<cv::KeyPoint>& keypoints, cv::InputArray mask=cv::noArray())const;
 +
 +    private:
 +        Parameters parameters; // storing the configuration of the detector
 +};
 +}}  // end namespace details and cv
 +
 +#endif
Simple merge
@@@ -395,7 -365,7 +395,7 @@@ bool CV_ChessboardDetectorTest::checkBy
  
      Mat bg(Size(800, 600), CV_8UC3, Scalar::all(255));
      randu(bg, Scalar::all(0), Scalar::all(255));
--    GaussianBlur(bg, bg, Size(7,7), 3.0);
++    GaussianBlur(bg, bg, Size(5, 5), 0.0);
  
      Mat_<float> camMat(3, 3);
      camMat << 300.f, 0.f, bg.cols/2.f, 0, 300.f, bg.rows/2.f, 0.f, 0.f, 1.f;
@@@ -499,20 -516,21 +499,22 @@@ public
      CV_WRAP FileNode();
  
      /** @overload
 -    @param fs Pointer to the obsolete file storage structure.
 -    @param node File node to be used as initialization for the created file node.
 -    */
 -    FileNode(const CvFileStorage* fs, const CvFileNode* node);
 +     @param fs Pointer to the file storage structure.
 +     @param blockIdx Index of the memory block where the file node is stored
 +     @param ofs Offset in bytes from the beginning of the serialized storage
 +     */
 +    FileNode(const FileStorage* fs, size_t blockIdx, size_t ofs);
  
      /** @overload
 -    @param node File node to be used as initialization for the created file node.
 -    */
 +     @param node File node to be used as initialization for the created file node.
 +     */
      FileNode(const FileNode& node);
  
+     FileNode& operator=(const FileNode& node);
      /** @brief Returns element of a mapping node or a sequence node.
 -    @param nodename Name of an element in the mapping node.
 -    @returns Returns the element with the given identifier.
 +     @param nodename Name of an element in the mapping node.
 +     @returns Returns the element with the given identifier.
       */
      FileNode operator[](const String& nodename) const;
  
@@@ -626,22 -636,23 +628,24 @@@ public
      FileNodeIterator();
  
      /** @overload
 -    @param fs File storage for the iterator.
 -    @param node File node for the iterator.
 -    @param ofs Index of the element in the node. The created iterator will point to this element.
 -    */
 -    FileNodeIterator(const CvFileStorage* fs, const CvFileNode* node, size_t ofs=0);
 +     @param node File node - the collection to iterate over;
 +        it can be a scalar (equivalent to 1-element collection) or "none" (equivalent to empty collection).
 +     @param seekEnd - true if iterator needs to be set after the last element of the node;
 +        that is:
 +            * node.begin() => FileNodeIterator(node, false)
 +            * node.end() => FileNodeIterator(node, true)
 +     */
 +    FileNodeIterator(const FileNode& node, bool seekEnd);
  
      /** @overload
 -    @param it Iterator to be used as initialization for the created iterator.
 -    */
 +     @param it Iterator to be used as initialization for the created iterator.
 +     */
      FileNodeIterator(const FileNodeIterator& it);
  
+     FileNodeIterator& operator=(const FileNodeIterator& it);
      //! returns the currently observed element
      FileNode operator *() const;
 -    //! accesses the currently observed element methods
 -    FileNode operator ->() const;
  
      //! moves iterator to the next node
      FileNodeIterator& operator ++ ();
@@@ -276,2363 -649,53 +276,2382 @@@ int decodeSimpleFormat( const char* dt 
      return elem_type;
  }
  
 -void icvWriteCollection( CvFileStorage* fs, const CvFileNode* node )
 +}
 +
 +#if defined __i386__ || defined(_M_IX86) || defined __x86_64__ || defined(_M_X64)
 +#define CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS 1
 +#else
 +#define CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS 0
 +#endif
 +
 +static inline int readInt(const uchar* p)
 +{
 +#if CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS
 +    return *(const int*)p;
 +#else
 +    int val = (int)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
 +    return val;
 +#endif
 +}
 +
 +static inline double readReal(const uchar* p)
 +{
 +#if CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS
 +    return *(const double*)p;
 +#else
 +    unsigned val0 = (unsigned)(p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
 +    unsigned val1 = (unsigned)(p[4] | (p[5] << 8) | (p[6] << 16) | (p[7] << 24));
 +    Cv64suf val;
 +    val.u = val0 | ((uint64)val1 << 32);
 +    return val.f;
 +#endif
 +}
 +
 +static inline void writeInt(uchar* p, int ival)
 +{
 +#if CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS
 +    int* ip = (int*)p;
 +    *ip = ival;
 +#else
 +    p[0] = (uchar)ival;
 +    p[1] = (uchar)(ival >> 8);
 +    p[2] = (uchar)(ival >> 16);
 +    p[3] = (uchar)(ival >> 24);
 +#endif
 +}
 +
 +static inline void writeReal(uchar* p, double fval)
  {
 -    int i, total = node->data.seq->total;
 -    int elem_size = node->data.seq->elem_size;
 -    int is_map = CV_NODE_IS_MAP(node->tag);
 -    CvSeqReader reader;
 +#if CV_UNALIGNED_LITTLE_ENDIAN_MEM_ACCESS
 +    double* fp = (double*)p;
 +    *fp = fval;
 +#else
 +    Cv64suf v;
 +    v.f = fval;
 +    p[0] = (uchar)v.u;
 +    p[1] = (uchar)(v.u >> 8);
 +    p[2] = (uchar)(v.u >> 16);
 +    p[3] = (uchar)(v.u >> 24);
 +    p[4] = (uchar)(v.u >> 32);
 +    p[5] = (uchar)(v.u >> 40);
 +    p[6] = (uchar)(v.u >> 48);
 +    p[7] = (uchar)(v.u >> 56);
 +#endif
 +}
 +
 +class FileStorage::Impl : public FileStorage_API
 +{
 +public:
 +    void init()
 +    {
 +        flags = 0;
 +        buffer.clear();
 +        bufofs = 0;
 +        state = UNDEFINED;
 +        is_opened = false;
 +        dummy_eof = false;
 +        write_mode = false;
 +        mem_mode = false;
 +        space = 0;
 +        wrap_margin = 71;
 +        fmt = 0;
 +        file = 0;
 +        gzfile = 0;
 +        empty_stream = true;
 +
 +        strbufv.clear();
 +        strbuf = 0;
 +        strbufsize = strbufpos = 0;
 +        roots.clear();
 +
 +        fs_data.clear();
 +        fs_data_ptrs.clear();
 +        fs_data_blksz.clear();
 +        freeSpaceOfs = 0;
 +
 +        str_hash.clear();
 +        str_hash_data.clear();
 +        str_hash_data.resize(1);
 +        str_hash_data[0] = '\0';
 +
 +        filename.clear();
 +        lineno = 0;
 +    }
 +
 +    Impl(FileStorage* _fs)
 +    {
 +        fs_ext = _fs;
 +        init();
 +    }
  
 -    cvStartReadSeq( node->data.seq, &reader, 0 );
 +    virtual ~Impl()
 +    {
 +        release();
 +    }
  
 -    for( i = 0; i < total; i++ )
 +    void release(String* out=0)
      {
 -        CvFileMapNode* elem = (CvFileMapNode*)reader.ptr;
 -        if( !is_map || CV_IS_SET_ELEM(elem) )
 +        if( is_opened )
          {
 -            const char* name = is_map ? elem->key->str.ptr : 0;
 -            icvWriteFileNode( fs, name, &elem->value );
 +            if(out)
 +                out->clear();
 +            if( write_mode )
 +            {
 +                while( write_stack.size() > 1 )
 +                {
 +                    endWriteStruct();
 +                }
 +                flush();
 +                if( fmt == FileStorage::FORMAT_XML )
 +                    puts( "</opencv_storage>\n" );
 +                else if ( fmt == FileStorage::FORMAT_JSON )
 +                    puts( "}\n" );
 +            }
 +
 +            closeFile();
 +            if( mem_mode && out )
 +            {
 +                *out = cv::String(outbuf.begin(), outbuf.end());
 +            }
 +            init();
          }
 -        CV_NEXT_SEQ_ELEM( elem_size, reader );
      }
 -}
  
 -void icvWriteFileNode( CvFileStorage* fs, const char* name, const CvFileNode* node )
 -{
 -    switch( CV_NODE_TYPE(node->tag) )
 +    void analyze_file_name( const std::string& file_name, std::vector<std::string>& params )
      {
 -    case CV_NODE_INT:
 -        fs->write_int( fs, name, node->data.i );
 -        break;
 -    case CV_NODE_REAL:
 -        fs->write_real( fs, name, node->data.f );
 -        break;
 -    case CV_NODE_STR:
 -        fs->write_string( fs, name, node->data.str.ptr, 0 );
 -        break;
 -    case CV_NODE_SEQ:
 -    case CV_NODE_MAP:
 -        cvStartWriteStruct( fs, name, CV_NODE_TYPE(node->tag) +
 -                (CV_NODE_SEQ_IS_SIMPLE(node->data.seq) ? CV_NODE_FLOW : 0),
 -                node->info ? node->info->type_name : 0 );
 -        icvWriteCollection( fs, node );
 -        cvEndWriteStruct( fs );
 -        break;
 -    case CV_NODE_NONE:
 -        cvStartWriteStruct( fs, name, CV_NODE_SEQ, 0 );
 -        cvEndWriteStruct( fs );
 -        break;
 -    default:
 -        CV_Error( CV_StsBadFlag, "Unknown type of file node" );
 +        params.clear();
 +        static const char not_file_name       = '\n';
 +        static const char parameter_begin     = '?';
 +        static const char parameter_separator = '&';
 +
 +        if( file_name.find(not_file_name, (size_t)0) != std::string::npos )
 +            return;
 +
 +        size_t beg = file_name.find_last_of(parameter_begin);
 +        params.push_back(file_name.substr((size_t)0, beg));
 +
 +        if( beg != std::string::npos )
 +        {
 +            size_t end = file_name.size();
 +            beg++;
 +            for( size_t param_beg = beg, param_end = beg;
 +                 param_end < end;
 +                 param_beg = param_end + 1 )
 +            {
 +                param_end = file_name.find_first_of( parameter_separator, param_beg );
 +                if( (param_end == std::string::npos || param_end != param_beg) && param_beg + 1 < end )
 +                {
 +                    params.push_back( file_name.substr( param_beg, param_end - param_beg ) );
 +                }
 +            }
 +        }
      }
 +
 +    bool open( const char* filename_or_buf, int _flags, const char* encoding )
 +    {
 +        _flags &= ~FileStorage::BASE64;
 +
 +        bool ok = true;
 +        release();
 +
 +        bool append = (_flags & 3) == FileStorage::APPEND;
 +        mem_mode = (_flags & FileStorage::MEMORY) != 0;
 +
 +        write_mode = (_flags & 3) != 0;
 +
 +        bool isGZ = false;
 +        size_t fnamelen = 0;
 +
 +        std::vector<std::string> params;
 +        //if ( !mem_mode )
 +        {
 +            analyze_file_name( filename_or_buf, params );
 +            if( !params.empty() )
 +                filename = params[0];
 +
 +            /*if( !write_base64 && params.size() >= 2 &&
 +                std::find(params.begin()+1, params.end(), std::string("base64")) != params.end())
 +                write_base64 = (write_mode || append);*/
 +        }
 +
 +        if( filename.size() == 0 && !mem_mode && !write_mode )
 +            CV_Error( CV_StsNullPtr, "NULL or empty filename" );
 +
 +        if( mem_mode && append )
 +            CV_Error( CV_StsBadFlag, "FileStorage::APPEND and FileStorage::MEMORY are not currently compatible" );
 +
 +        flags = _flags;
 +
 +        if( !mem_mode )
 +        {
 +            char* dot_pos = strrchr((char*)filename.c_str(), '.');
 +            char compression = '\0';
 +
 +            if( dot_pos && dot_pos[1] == 'g' && dot_pos[2] == 'z' &&
 +               (dot_pos[3] == '\0' || (cv_isdigit(dot_pos[3]) && dot_pos[4] == '\0')) )
 +            {
 +                if( append )
 +                {
 +                    CV_Error(CV_StsNotImplemented, "Appending data to compressed file is not implemented" );
 +                }
 +                isGZ = true;
 +                compression = dot_pos[3];
 +                if( compression )
 +                    dot_pos[3] = '\0', fnamelen--;
 +            }
 +
 +            if( !isGZ )
 +            {
 +                file = fopen(filename.c_str(), !write_mode ? "rt" : !append ? "wt" : "a+t" );
 +                if( !file )
 +                    return false;
 +            }
 +            else
 +            {
 +#if USE_ZLIB
 +                char mode[] = { write_mode ? 'w' : 'r', 'b', compression ? compression : '3', '\0' };
 +                gzfile = gzopen(filename.c_str(), mode);
 +                if( !gzfile )
 +                    return false;
 +#else
 +                CV_Error(CV_StsNotImplemented, "There is no compressed file storage support in this configuration");
 +#endif
 +            }
 +        }
 +
 +        roots.clear();
 +        fs_data.clear();
 +        wrap_margin = 71;
 +        fmt = FileStorage::FORMAT_AUTO;
 +
 +        if( write_mode )
 +        {
 +            fmt = flags & FileStorage::FORMAT_MASK;
 +
 +            if( mem_mode )
 +                outbuf.clear();
 +
 +            if( fmt == FileStorage::FORMAT_AUTO && !filename.empty() )
 +            {
 +                const char* dot_pos = NULL;
 +                const char* dot_pos2 = NULL;
 +                // like strrchr() implementation, but save two last positions simultaneously
 +                for (const char* pos = &filename[0]; pos[0] != 0; pos++)
 +                {
 +                    if( pos[0] == '.' )
 +                    {
 +                        dot_pos2 = dot_pos;
 +                        dot_pos = pos;
 +                    }
 +                }
 +                if (fs::strcasecmp(dot_pos, ".gz") == 0 && dot_pos2 != NULL)
 +                {
 +                    dot_pos = dot_pos2;
 +                }
 +                fmt = (fs::strcasecmp(dot_pos, ".xml") == 0 || fs::strcasecmp(dot_pos, ".xml.gz") == 0 )
 +                        ? FileStorage::FORMAT_XML
 +                    : (fs::strcasecmp(dot_pos, ".json") == 0 || fs::strcasecmp(dot_pos, ".json.gz") == 0)
 +                        ? FileStorage::FORMAT_JSON
 +                    : FileStorage::FORMAT_YAML;
 +            }
 +            else if( fmt == FileStorage::FORMAT_AUTO )
 +            {
 +                fmt = FileStorage::FORMAT_XML;
 +            }
 +
 +            // we use factor=6 for XML (the longest characters (' and ") are encoded with 6 bytes (&apos; and &quot;)
 +            // and factor=4 for YAML ( as we use 4 bytes for non ASCII characters (e.g. \xAB))
 +            int buf_size = CV_FS_MAX_LEN*(fmt == FileStorage::FORMAT_XML ? 6 : 4) + 1024;
 +
 +            if (append)
 +            {
 +                fseek( file, 0, SEEK_END );
 +                if (ftell(file) == 0)
 +                    append = false;
 +            }
 +
 +            write_stack.clear();
 +            empty_stream = true;
 +            write_stack.push_back(FStructData("", FileNode::MAP | FileNode::EMPTY, 0));
 +            buffer.reserve(buf_size + 1024);
 +            buffer.resize(buf_size);
 +            bufofs = 0;
 +
 +            if( fmt == FileStorage::FORMAT_XML )
 +            {
 +                size_t file_size = file ? (size_t)ftell(file) : (size_t)0;
 +                if( !append || file_size == 0 )
 +                {
 +                    if( encoding && *encoding != '\0' )
 +                    {
 +                        if( fs::strcasecmp(encoding, "UTF-16" ) == 0 )
 +                        {
 +                            release();
 +                            CV_Error( CV_StsBadArg, "UTF-16 XML encoding is not supported! Use 8-bit encoding\n");
 +                        }
 +
 +                        CV_Assert( strlen(encoding) < 1000 );
 +                        char buf[1100];
 +                        sprintf(buf, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", encoding);
 +                        puts( buf );
 +                    }
 +                    else
 +                        puts( "<?xml version=\"1.0\"?>\n" );
 +                    puts( "<opencv_storage>\n" );
 +                }
 +                else
 +                {
 +                    int xml_buf_size = 1 << 10;
 +                    char substr[] = "</opencv_storage>";
 +                    int last_occurrence = -1;
 +                    xml_buf_size = MIN(xml_buf_size, int(file_size));
 +                    fseek( file, -xml_buf_size, SEEK_END );
 +                    std::vector<char> xml_buf_(xml_buf_size+2);
 +                    // find the last occurrence of </opencv_storage>
 +                    for(;;)
 +                    {
 +                        int line_offset = (int)ftell( file );
 +                        const char* ptr0 = this->gets(&xml_buf_[0], xml_buf_size );
 +                        const char* ptr = NULL;
 +                        if( !ptr0 )
 +                            break;
 +                        ptr = ptr0;
 +                        for(;;)
 +                        {
 +                            ptr = strstr( ptr, substr );
 +                            if( !ptr )
 +                                break;
 +                            last_occurrence = line_offset + (int)(ptr - ptr0);
 +                            ptr += strlen(substr);
 +                        }
 +                    }
 +                    if( last_occurrence < 0 )
 +                    {
 +                        release();
 +                        CV_Error( CV_StsError, "Could not find </opencv_storage> in the end of file.\n" );
 +                    }
 +                    closeFile();
 +                    file = fopen( filename.c_str(), "r+t" );
 +                    CV_Assert(file != 0);
 +                    fseek( file, last_occurrence, SEEK_SET );
 +                    // replace the last "</opencv_storage>" with " <!-- resumed -->", which has the same length
 +                    puts( " <!-- resumed -->" );
 +                    fseek( file, 0, SEEK_END );
 +                    puts( "\n" );
 +                }
 +
 +                emitter = createXMLEmitter(this);
 +            }
 +            else if( fmt == FileStorage::FORMAT_YAML )
 +            {
 +                if( !append)
 +                    puts( "%YAML:1.0\n---\n" );
 +                else
 +                    puts( "...\n---\n" );
 +
 +                emitter = createYAMLEmitter(this);
 +            }
 +            else
 +            {
 +                CV_Assert( fmt == FileStorage::FORMAT_JSON );
 +                if( !append )
 +                    puts( "{\n" );
 +                else
 +                {
 +                    bool valid = false;
 +                    long roffset = 0;
 +                    for ( ;
 +                         fseek( file, roffset, SEEK_END ) == 0;
 +                         roffset -= 1 )
 +                    {
 +                        const char end_mark = '}';
 +                        if ( fgetc( file ) == end_mark )
 +                        {
 +                            fseek( file, roffset, SEEK_END );
 +                            valid = true;
 +                            break;
 +                        }
 +                    }
 +
 +                    if ( valid )
 +                    {
 +                        closeFile();
 +                        file = fopen( filename.c_str(), "r+t" );
 +                        CV_Assert(file != 0);
 +                        fseek( file, roffset, SEEK_END );
 +                        fputs( ",", file );
 +                    }
 +                    else
 +                    {
 +                        CV_Error( CV_StsError, "Could not find '}' in the end of file.\n" );
 +                    }
 +                }
 +                write_stack.back().indent = 4;
 +                emitter = createJSONEmitter(this);
 +            }
 +            is_opened = true;
 +        }
 +        else
 +        {
 +            const size_t buf_size0 = 1 << 20;
 +            size_t buf_size = buf_size0;
 +            if( mem_mode )
 +            {
 +                strbuf = (char*)filename_or_buf;
 +                strbufsize = strlen(strbuf);
 +            }
 +
 +            const char* yaml_signature = "%YAML";
 +            const char* json_signature = "{";
 +            const char* xml_signature  = "<?xml";
 +            char buf[16];
 +            this->gets( buf, sizeof(buf)-2 );
 +            char* bufPtr = cv_skip_BOM(buf);
 +            size_t bufOffset = bufPtr - buf;
 +
 +            if(strncmp( bufPtr, yaml_signature, strlen(yaml_signature) ) == 0)
 +                fmt = FileStorage::FORMAT_YAML;
 +            else if(strncmp( bufPtr, json_signature, strlen(json_signature) ) == 0)
 +                fmt = FileStorage::FORMAT_JSON;
 +            else if(strncmp( bufPtr, xml_signature, strlen(xml_signature) ) == 0)
 +                fmt = FileStorage::FORMAT_XML;
 +            else if(strbufsize  == bufOffset)
 +                CV_Error(CV_BADARG_ERR, "Input file is invalid");
 +            else
 +                CV_Error(CV_BADARG_ERR, "Unsupported file storage format");
 +
 +            if( !isGZ )
 +            {
 +                if( !mem_mode )
 +                {
 +                    fseek( file, 0, SEEK_END );
 +                    buf_size = ftell( file );
 +                }
 +                else
 +                    buf_size = strbufsize;
 +                buf_size = MIN( buf_size, buf_size0 );
 +                buf_size = MAX( buf_size, (size_t)(CV_FS_MAX_LEN*6 + 1024) );
 +            }
 +            rewind();
 +            strbufpos = bufOffset;
 +
 +            buffer.reserve(buf_size + 256);
 +            buffer.resize(buf_size);
 +            bufofs = 0;
 +
 +            try
 +            {
 +                char* ptr = bufferStart();
 +                ptr[0] = ptr[1] = ptr[2] = '\0';
 +                FileNode root_nodes(fs_ext, 0, 0);
 +
 +                uchar* rptr = reserveNodeSpace(root_nodes, 9);
 +                *rptr = FileNode::SEQ;
 +                writeInt(rptr + 1, 4);
 +                writeInt(rptr + 5, 0);
 +
 +                roots.clear();
 +
 +                switch (fmt)
 +                {
 +                    case FileStorage::FORMAT_XML: parser = createXMLParser(this); break;
 +                    case FileStorage::FORMAT_YAML: parser = createYAMLParser(this); break;
 +                    case FileStorage::FORMAT_JSON: parser = createJSONParser(this); break;
 +                    default: parser = Ptr<FileStorageParser>();
 +                }
 +
 +                if( !parser.empty() )
 +                {
 +                    ok = parser->parse(ptr);
 +                    if( ok )
 +                    {
 +                        finalizeCollection(root_nodes);
 +
 +                        CV_Assert( !fs_data_ptrs.empty() );
 +                        FileNode roots_node(fs_ext, 0, 0);
 +                        size_t i, nroots = roots_node.size();
 +                        FileNodeIterator it = roots_node.begin();
 +
 +                        for( i = 0; i < nroots; i++, ++it )
 +                            roots.push_back(*it);
 +                    }
 +                }
 +            }
 +            catch(...)
 +            {
 +                is_opened = true;
 +                release();
 +                throw;
 +            }
 +
 +            // release resources that we do not need anymore
 +            closeFile();
 +            is_opened = true;
 +            std::vector<char> tmpbuf;
 +            std::swap(buffer, tmpbuf);
 +            bufofs = 0;
 +        }
 +        return ok;
 +    }
 +
 +    void puts( const char* str )
 +    {
 +        CV_Assert( write_mode );
 +        if( mem_mode )
 +            std::copy(str, str + strlen(str), std::back_inserter(outbuf));
 +        else if( file )
 +            fputs( str, file );
 +#if USE_ZLIB
 +        else if( gzfile )
 +            gzputs( gzfile, str );
 +#endif
 +        else
 +            CV_Error( CV_StsError, "The storage is not opened" );
 +    }
 +
 +    char* gets( char* str, int maxCount )
 +    {
 +        if( strbuf )
 +        {
 +            size_t i = strbufpos, len = strbufsize;
 +            int j = 0;
 +            const char* instr = strbuf;
 +            while( i < len && j < maxCount-1 )
 +            {
 +                char c = instr[i++];
 +                if( c == '\0' )
 +                    break;
 +                str[j++] = c;
 +                if( c == '\n' )
 +                    break;
 +            }
 +            str[j++] = '\0';
 +            strbufpos = i;
 +            if (maxCount > 256 && !(flags & cv::FileStorage::BASE64))
 +                CV_Assert(j < maxCount - 1 && "OpenCV persistence doesn't support very long lines");
 +            return j > 1 ? str : 0;
 +        }
 +        if( file )
 +        {
 +            char* ptr = fgets( str, maxCount, file );
 +            if (ptr && maxCount > 256 && !(flags & cv::FileStorage::BASE64))
 +            {
 +                size_t sz = strnlen(ptr, maxCount);
 +                CV_Assert(sz < (size_t)(maxCount - 1) && "OpenCV persistence doesn't support very long lines");
 +            }
 +            return ptr;
 +        }
 +#if USE_ZLIB
 +        if( gzfile )
 +        {
 +            char* ptr = gzgets( gzfile, str, maxCount );
 +            if (ptr && maxCount > 256 && !(flags & cv::FileStorage::BASE64))
 +            {
 +                size_t sz = strnlen(ptr, maxCount);
 +                CV_Assert(sz < (size_t)(maxCount - 1) && "OpenCV persistence doesn't support very long lines");
 +            }
 +            return ptr;
 +        }
 +#endif
 +        CV_Error(CV_StsError, "The storage is not opened");
 +    }
 +
 +    char* gets()
 +    {
 +        char* ptr = this->gets(bufferStart(), (int)(bufferEnd() - bufferStart()));
 +        if( !ptr )
 +        {
 +            ptr = bufferStart();  // FIXIT Why do we need this hack? What is about other parsers JSON/YAML?
 +            *ptr = '\0';
 +            setEof();
 +            return 0;
 +        }
 +        else
 +        {
 +            FileStorage_API* fs = this;
 +            int l = (int)strlen(ptr);
 +            if( l > 0 && ptr[l-1] != '\n' && ptr[l-1] != '\r' && !eof() )
 +                CV_PARSE_ERROR_CPP("Too long string or a last string w/o newline");
 +        }
 +        lineno++;
 +        return ptr;
 +    }
 +
 +    bool eof()
 +    {
 +        if( dummy_eof )
 +            return true;
 +        if( strbuf )
 +            return strbufpos >= strbufsize;
 +        if( file )
 +            return feof(file) != 0;
 +#if USE_ZLIB
 +        if( gzfile )
 +            return gzeof(gzfile) != 0;
 +#endif
 +        return false;
 +    }
 +
 +    void setEof()
 +    {
 +        dummy_eof = true;
 +    }
 +
 +    void closeFile()
 +    {
 +        if( file )
 +            fclose( file );
 +#if USE_ZLIB
 +        else if( gzfile )
 +            gzclose( gzfile );
 +#endif
 +        file = 0;
 +        gzfile = 0;
 +        strbuf = 0;
 +        strbufpos = 0;
 +        is_opened = false;
 +    }
 +
 +    void rewind()
 +    {
 +        if( file )
 +            ::rewind(file);
 +#if USE_ZLIB
 +        else if( gzfile )
 +            gzrewind(gzfile);
 +#endif
 +        strbufpos = 0;
 +    }
 +
 +    char* resizeWriteBuffer( char* ptr, int len )
 +    {
 +        const char* buffer_end = &buffer[0] + buffer.size();
 +        if( ptr + len < buffer_end )
 +            return ptr;
 +
 +        const char* buffer_start = &buffer[0];
 +        int written_len = (int)(ptr - buffer_start);
 +
 +        CV_Assert(written_len <= (int)buffer.size());
 +        int new_size = (int)((buffer_end - buffer_start)*3/2);
 +        new_size = MAX( written_len + len, new_size );
 +        buffer.reserve( new_size + 256 );
 +        buffer.resize( new_size );
 +        bufofs = written_len;
 +        return &buffer[0] + bufofs;
 +    }
 +
 +    char* flush()
 +    {
 +        char* buffer_start = &buffer[0];
 +        char* ptr = buffer_start + bufofs;
 +
 +        if( ptr > buffer_start + space )
 +        {
 +            ptr[0] = '\n';
 +            ptr[1] = '\0';
 +            puts( buffer_start );
 +            bufofs = 0;
 +        }
 +
 +        int indent = write_stack.back().indent;
 +
 +        if( space != indent )
 +        {
 +            memset( buffer_start, ' ', indent );
 +            space = indent;
 +        }
 +        bufofs = space;
 +        ptr = buffer_start + bufofs;
 +
 +        return ptr;
 +    }
 +
 +    void endWriteStruct()
 +    {
 +        CV_Assert( write_mode );
 +        CV_Assert( !write_stack.empty() );
 +
 +        FStructData& current_struct = write_stack.back();
 +        if( fmt == FileStorage::FORMAT_JSON && !FileNode::isFlow(current_struct.flags) && write_stack.size() > 1 )
 +            current_struct.indent = write_stack[write_stack.size() - 2].indent;
 +
 +        emitter->endWriteStruct(current_struct);
 +
 +        write_stack.pop_back();
 +        if( !write_stack.empty() )
 +            write_stack.back().flags &= ~FileNode::EMPTY;
 +    }
 +
 +    void startWriteStruct( const char* key, int struct_flags,
 +                           const char* type_name )
 +    {
 +        CV_Assert( write_mode );
 +
 +        struct_flags = (struct_flags & (FileNode::TYPE_MASK|FileNode::FLOW)) | FileNode::EMPTY;
 +        if( !FileNode::isCollection(struct_flags))
 +            CV_Error( CV_StsBadArg,
 +                     "Some collection type: FileNode::SEQ or FileNode::MAP must be specified" );
 +
 +        if( type_name && type_name[0] == '\0' )
 +            type_name = 0;
 +
 +        FStructData s = emitter->startWriteStruct( write_stack.back(), key, struct_flags, type_name );
 +        write_stack.push_back(s);
 +        size_t write_stack_size = write_stack.size();
 +        if( write_stack_size > 1 )
 +            write_stack[write_stack_size-2].flags &= ~FileNode::EMPTY;
 +
 +        if( !FileNode::isFlow(s.flags) )
 +            flush();
 +
 +        if( fmt == FileStorage::FORMAT_JSON && type_name && type_name[0] && FileNode::isMap(struct_flags))
 +        {
 +            emitter->write("type_id", type_name, false);
 +        }
 +    }
 +
 +    void writeComment( const char* comment, bool eol_comment )
 +    {
 +        CV_Assert(write_mode);
 +        emitter->writeComment( comment, eol_comment );
 +    }
 +
 +    void startNextStream()
 +    {
 +        CV_Assert(write_mode);
 +        if( !empty_stream )
 +        {
 +            while( !write_stack.empty() )
 +                endWriteStruct();
 +            flush();
 +            emitter->startNextStream();
 +            empty_stream = true;
 +            write_stack.push_back(FStructData("", FileNode::EMPTY, 0));
 +            bufofs = 0;
 +        }
 +    }
 +
 +    void write( const String& key, int value )
 +    {
 +        CV_Assert(write_mode);
 +        emitter->write(key.c_str(), value);
 +    }
 +
 +    void write( const String& key, double value )
 +    {
 +        CV_Assert(write_mode);
 +        emitter->write(key.c_str(), value);
 +    }
 +
 +    void write( const String& key, const String& value )
 +    {
 +        CV_Assert(write_mode);
 +        emitter->write(key.c_str(), value.c_str(), false);
 +    }
 +
 +    void writeRawData( const std::string& dt, const void* _data, size_t len )
 +    {
 +        CV_Assert(write_mode);
 +
 +        size_t elemSize = fs::calcStructSize(dt.c_str(), 0);
 +        CV_Assert( len % elemSize == 0 );
 +        len /= elemSize;
 +
 +        bool explicitZero = fmt == FileStorage::FORMAT_JSON;
 +        const uchar* data0 = (const uchar*)_data;
 +        int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2], k, fmt_pair_count;
 +        char buf[256] = "";
 +
 +        fmt_pair_count = fs::decodeFormat( dt.c_str(), fmt_pairs, CV_FS_MAX_FMT_PAIRS );
 +
 +        if( !len )
 +            return;
 +
 +        if( !data0 )
 +            CV_Error( CV_StsNullPtr, "Null data pointer" );
 +
 +        if( fmt_pair_count == 1 )
 +        {
 +            fmt_pairs[0] *= (int)len;
 +            len = 1;
 +        }
 +
 +        for(;len--; data0 += elemSize)
 +        {
 +            int offset = 0;
 +            for( k = 0; k < fmt_pair_count; k++ )
 +            {
 +                int i, count = fmt_pairs[k*2];
 +                int elem_type = fmt_pairs[k*2+1];
 +                int elem_size = CV_ELEM_SIZE(elem_type);
 +                const char *ptr;
 +
 +                offset = cvAlign( offset, elem_size );
 +                const uchar* data = data0 + offset;
 +
 +                for( i = 0; i < count; i++ )
 +                {
 +                    switch( elem_type )
 +                    {
 +                    case CV_8U:
 +                        ptr = fs::itoa( *(uchar*)data, buf, 10 );
 +                        data++;
 +                        break;
 +                    case CV_8S:
 +                        ptr = fs::itoa( *(char*)data, buf, 10 );
 +                        data++;
 +                        break;
 +                    case CV_16U:
 +                        ptr = fs::itoa( *(ushort*)data, buf, 10 );
 +                        data += sizeof(ushort);
 +                        break;
 +                    case CV_16S:
 +                        ptr = fs::itoa( *(short*)data, buf, 10 );
 +                        data += sizeof(short);
 +                        break;
 +                    case CV_32S:
 +                        ptr = fs::itoa( *(int*)data, buf, 10 );
 +                        data += sizeof(int);
 +                        break;
 +                    case CV_32F:
 +                        ptr = fs::floatToString( buf, *(float*)data, false, explicitZero );
 +                        data += sizeof(float);
 +                        break;
 +                    case CV_64F:
 +                        ptr = fs::doubleToString( buf, *(double*)data, explicitZero );
 +                        data += sizeof(double);
 +                        break;
 +                    case CV_16F: /* reference */
 +                        ptr = fs::floatToString( buf, (float)*(float16_t*)data, true, explicitZero );
 +                        data += sizeof(float16_t);
 +                        break;
 +                    default:
 +                        CV_Error( CV_StsUnsupportedFormat, "Unsupported type" );
 +                        return;
 +                    }
 +
 +                    emitter->writeScalar(0, ptr);
 +                }
 +
 +                offset = (int)(data - data0);
 +            }
 +        }
 +    }
 +
 +    void writeRawDataBase64(const void* /*data*/, int /*len*/, const char* /*dt*/ )
 +    {
 +
 +    }
 +
 +    String releaseAndGetString();
 +
 +    FileNode getFirstTopLevelNode() const
 +    {
 +        return roots.empty() ? FileNode() : roots[0];
 +    }
 +
 +    FileNode root(int streamIdx=0) const
 +    {
 +        return streamIdx >= 0 && streamIdx < (int)roots.size() ? roots[streamIdx] : FileNode();
 +    }
 +
 +    FileNode operator[](const String& nodename) const
 +    {
 +        return this->operator[](nodename.c_str());
 +    }
 +
 +    FileNode operator[](const char* /*nodename*/) const
 +    {
 +        return FileNode();
 +    }
 +
 +    int getFormat() const { return fmt; }
 +
 +    char* bufferPtr() const { return (char*)(&buffer[0] + bufofs); }
 +    char* bufferStart() const { return (char*)&buffer[0]; }
 +    char* bufferEnd() const { return (char*)(&buffer[0] + buffer.size()); }
 +    void setBufferPtr(char* ptr)
 +    {
 +        char* bufferstart = bufferStart();
 +        CV_Assert( ptr >= bufferstart && ptr <= bufferEnd() );
 +        bufofs = ptr - bufferstart;
 +    }
 +    int wrapMargin() const { return wrap_margin; }
 +
 +    FStructData& getCurrentStruct()
 +    {
 +        CV_Assert(!write_stack.empty());
 +        return write_stack.back();
 +    }
 +
 +    void setNonEmpty()
 +    {
 +        empty_stream = false;
 +    }
 +
 +    void processSpecialDouble( char* buf, double* value, char** endptr )
 +    {
 +        FileStorage_API* fs = this;
 +        char c = buf[0];
 +        int inf_hi = 0x7ff00000;
 +
 +        if( c == '-' || c == '+' )
 +        {
 +            inf_hi = c == '-' ? 0xfff00000 : 0x7ff00000;
 +            c = *++buf;
 +        }
 +
 +        if( c != '.' )
 +            CV_PARSE_ERROR_CPP( "Bad format of floating-point constant" );
 +
 +        Cv64suf v;
 +        v.f = 0.;
 +        if( toupper(buf[1]) == 'I' && toupper(buf[2]) == 'N' && toupper(buf[3]) == 'F' )
 +            v.u = (uint64)inf_hi << 32;
 +        else if( toupper(buf[1]) == 'N' && toupper(buf[2]) == 'A' && toupper(buf[3]) == 'N' )
 +            v.u = (uint64)-1;
 +        else
 +            CV_PARSE_ERROR_CPP( "Bad format of floating-point constant" );
 +        *value = v.f;
 +        *endptr = buf + 4;
 +    }
 +
 +    double strtod( char* ptr, char** endptr )
 +    {
 +        double fval = ::strtod( ptr, endptr );
 +        if( **endptr == '.' )
 +        {
 +            char* dot_pos = *endptr;
 +            *dot_pos = ',';
 +            double fval2 = ::strtod( ptr, endptr );
 +            *dot_pos = '.';
 +            if( *endptr > dot_pos )
 +                fval = fval2;
 +            else
 +                *endptr = dot_pos;
 +        }
 +
 +        if( *endptr == ptr || cv_isalpha(**endptr) )
 +            processSpecialDouble( ptr, &fval, endptr );
 +
 +        return fval;
 +    }
 +
 +    void convertToCollection(int type, FileNode& node)
 +    {
 +        CV_Assert( type == FileNode::SEQ || type == FileNode::MAP );
 +
 +        int node_type = node.type();
 +        if( node_type == type )
 +            return;
 +
 +        bool named = node.isNamed();
 +        uchar* ptr = node.ptr() + 1 + (named ? 4 : 0);
 +
 +        int ival = 0;
 +        double fval = 0;
 +        std::string sval;
 +        bool add_first_scalar = false;
 +
 +        if( node_type != FileNode::NONE )
 +        {
 +            // scalar nodes can only be converted to sequences, e.g. in XML:
 +            // <a>5[parser_position]... => create 5 with name "a"
 +            // <a>5 6[parser_position]... => 5 is converted to [5] and then 6 is added to it
 +            //
 +            // otherwise we don't know where to get the element names from
 +            CV_Assert( type == FileNode::SEQ );
 +            if( node_type == FileNode::INT )
 +            {
 +                ival = readInt(ptr);
 +                add_first_scalar = true;
 +            }
 +            else if( node_type == FileNode::REAL )
 +            {
 +                fval = readReal(ptr);
 +                add_first_scalar = true;
 +            }
 +            else if( node_type == FileNode::STRING )
 +            {
 +                sval = std::string(node);
 +                add_first_scalar = true;
 +            }
 +            else
 +                CV_Error_(Error::StsError, ("The node of type %d cannot be converted to collection", node_type));
 +        }
 +
 +        ptr = reserveNodeSpace(node, 1 + (named ? 4 : 0) + 4 + 4);
 +        *ptr++ = (uchar)(type | (named ? FileNode::NAMED : 0));
 +        // name has been copied automatically
 +        if( named )
 +            ptr += 4;
 +        // set raw_size(collection)==4, nelems(collection)==1
 +        writeInt(ptr, 4);
 +        writeInt(ptr + 4, 0);
 +
 +        if( add_first_scalar )
 +            addNode(node, std::string(), node_type,
 +                    node_type == FileNode::INT ? (const void*)&ival :
 +                    node_type == FileNode::REAL ? (const void*)&fval :
 +                    node_type == FileNode::STRING ? (const void*)sval.c_str() : 0,
 +                    -1);
 +    }
 +
 +    // a) allocates new FileNode (for that just set blockIdx to the last block and ofs to freeSpaceOfs) or
 +    // b) reallocates just created new node (blockIdx and ofs must be taken from FileNode).
 +    //    If there is no enough space in the current block (it should be the last block added so far),
 +    //    the last block is shrunk so that it ends immediately before the reallocated node. Then,
 +    //    a new block of sufficient size is allocated and the FileNode is placed in the beginning of it.
 +    // The case (a) can be used to allocate the very first node by setting blockIdx == ofs == 0.
 +    // In the case (b) the existing tag and the name are copied automatically.
 +    uchar* reserveNodeSpace(FileNode& node, size_t sz)
 +    {
 +        bool shrinkBlock = false;
 +        size_t shrinkBlockIdx = 0, shrinkSize = 0;
 +
 +        uchar *ptr = 0, *blockEnd = 0;
 +
 +        if( !fs_data_ptrs.empty() )
 +        {
 +            size_t blockIdx = node.blockIdx;
 +            size_t ofs = node.ofs;
 +            CV_Assert( blockIdx == fs_data_ptrs.size()-1 );
 +            CV_Assert( ofs <= fs_data_blksz[blockIdx] );
 +            CV_Assert( freeSpaceOfs <= fs_data_blksz[blockIdx] );
 +            //CV_Assert( freeSpaceOfs <= ofs + sz );
 +
 +            ptr = fs_data_ptrs[blockIdx] + ofs;
 +            blockEnd = fs_data_ptrs[blockIdx] + fs_data_blksz[blockIdx];
 +
 +            CV_Assert(ptr >= fs_data_ptrs[blockIdx] && ptr <= blockEnd);
 +            if( ptr + sz <= blockEnd )
 +            {
 +                freeSpaceOfs = ofs + sz;
 +                return ptr;
 +            }
 +
 +            if (ofs == 0)  // FileNode is a first component of this block. Resize current block instead of allocation of new one.
 +            {
 +                fs_data[blockIdx]->resize(sz);
 +                ptr = &fs_data[blockIdx]->at(0);
 +                fs_data_ptrs[blockIdx] = ptr;
 +                fs_data_blksz[blockIdx] = sz;
 +                freeSpaceOfs = sz;
 +                return ptr;
 +            }
 +
 +            shrinkBlock = true;
 +            shrinkBlockIdx = blockIdx;
 +            shrinkSize = ofs;
 +        }
 +
 +        size_t blockSize = std::max((size_t)CV_FS_MAX_LEN*4 - 256, sz) + 256;
 +        Ptr<std::vector<uchar> > pv = makePtr<std::vector<uchar> >(blockSize);
 +        fs_data.push_back(pv);
 +        uchar* new_ptr = &pv->at(0);
 +        fs_data_ptrs.push_back(new_ptr);
 +        fs_data_blksz.push_back(blockSize);
 +        node.blockIdx = fs_data_ptrs.size()-1;
 +        node.ofs = 0;
 +        freeSpaceOfs = sz;
 +
 +        if( ptr && ptr + 5 <= blockEnd )
 +        {
 +            new_ptr[0] = ptr[0];
 +            if( ptr[0] & FileNode::NAMED )
 +            {
 +                new_ptr[1] = ptr[1];
 +                new_ptr[2] = ptr[2];
 +                new_ptr[3] = ptr[3];
 +                new_ptr[4] = ptr[4];
 +            }
 +        }
 +
 +        if (shrinkBlock)
 +        {
 +            fs_data[shrinkBlockIdx]->resize(shrinkSize);
 +            fs_data_blksz[shrinkBlockIdx] = shrinkSize;
 +        }
 +
 +        return new_ptr;
 +    }
 +
 +    unsigned getStringOfs( const std::string& key )
 +    {
 +        str_hash_t::iterator it = str_hash.find(key);
 +        return it != str_hash.end() ? it->second : 0;
 +    }
 +
 +    FileNode addNode( FileNode& collection, const std::string& key,
 +                       int elem_type, const void* value, int len )
 +    {
 +        FileStorage_API* fs = this;
 +        bool noname = key.empty() || (fmt == FileStorage::FORMAT_XML && strcmp(key.c_str(), "_") == 0);
 +        convertToCollection( noname ? FileNode::SEQ : FileNode::MAP, collection );
 +
 +        bool isseq = collection.empty() ? false : collection.isSeq();
 +        if( noname != isseq )
 +            CV_PARSE_ERROR_CPP( noname ? "Map element should have a name" :
 +                                "Sequence element should not have name (use <_></_>)" );
 +        unsigned strofs = 0;
 +        if( !noname )
 +        {
 +            strofs = getStringOfs(key);
 +            if( !strofs )
 +            {
 +                strofs = (unsigned)str_hash_data.size();
 +                size_t keysize = key.size() + 1;
 +                str_hash_data.resize(strofs + keysize);
 +                memcpy(&str_hash_data[0] + strofs, &key[0], keysize);
 +                str_hash.insert(std::make_pair(key, strofs));
 +            }
 +        }
 +
 +        uchar* cp = collection.ptr();
 +
 +        size_t blockIdx = fs_data_ptrs.size() - 1;
 +        size_t ofs = freeSpaceOfs;
 +        FileNode node(fs_ext, blockIdx, ofs);
 +
 +        size_t sz0 = 1 + (noname ? 0 : 4) + 8;
 +        uchar* ptr = reserveNodeSpace(node, sz0);
 +
 +        *ptr++ = (uchar)(elem_type | (noname ? 0 : FileNode::NAMED));
 +        if( elem_type == FileNode::NONE )
 +            freeSpaceOfs -= 8;
 +
 +        if( !noname )
 +        {
 +            writeInt(ptr, (int)strofs);
 +            ptr += 4;
 +        }
 +
 +        if( elem_type == FileNode::SEQ || elem_type == FileNode::MAP )
 +        {
 +            writeInt(ptr, 4);
 +            writeInt(ptr, 0);
 +        }
 +
 +        if( value )
 +            node.setValue(elem_type, value, len);
 +
 +        if( collection.isNamed() )
 +            cp += 4;
 +        int nelems = readInt(cp + 5);
 +        writeInt(cp + 5, nelems + 1);
 +
 +        return node;
 +    }
 +
 +    void finalizeCollection( FileNode& collection )
 +    {
 +        if( !collection.isSeq() && !collection.isMap() )
 +            return;
 +        uchar* ptr0 = collection.ptr(), *ptr = ptr0 + 1;
 +        if( *ptr0 & FileNode::NAMED )
 +            ptr += 4;
 +        size_t blockIdx = collection.blockIdx;
 +        size_t ofs = collection.ofs + (size_t)(ptr + 8 - ptr0);
 +        size_t rawSize = 4;
 +        unsigned sz = (unsigned)readInt(ptr + 4);
 +        if( sz > 0 )
 +        {
 +            size_t lastBlockIdx = fs_data_ptrs.size() - 1;
 +
 +            for( ; blockIdx < lastBlockIdx; blockIdx++ )
 +            {
 +                rawSize += fs_data_blksz[blockIdx] - ofs;
 +                ofs = 0;
 +            }
 +        }
 +        rawSize += freeSpaceOfs - ofs;
 +        writeInt(ptr, (int)rawSize);
 +    }
 +
 +    void normalizeNodeOfs(size_t& blockIdx, size_t& ofs)
 +    {
 +        while( ofs >= fs_data_blksz[blockIdx] )
 +        {
 +            if( blockIdx == fs_data_blksz.size() - 1 )
 +            {
 +                CV_Assert( ofs == fs_data_blksz[blockIdx] );
 +                break;
 +            }
 +            ofs -= fs_data_blksz[blockIdx];
 +            blockIdx++;
 +        }
 +    }
 +
 +    class Base64Decoder
 +    {
 +    public:
 +        Base64Decoder() { ofs = 0; ptr = 0; indent = 0; totalchars = 0; eos = true; }
 +        void init(Ptr<FileStorageParser>& _parser, char* _ptr, int _indent)
 +        {
 +            parser = _parser;
 +            ptr = _ptr;
 +            indent = _indent;
 +            encoded.clear();
 +            decoded.clear();
 +            ofs = 0;
 +            totalchars = 0;
 +            eos = false;
 +        }
 +
 +        bool readMore(int needed)
 +        {
 +            static const uchar base64tab[] =
 +            {
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62,  0,  0,  0, 63,
 +               52, 53, 54, 55, 56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,
 +                0,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 +               15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,  0,  0,  0,  0,
 +                0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 +               41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
 +                0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
 +            };
 +
 +            if( eos )
 +                return false;
 +
 +            size_t sz = decoded.size();
 +            CV_Assert( ofs <= sz );
 +            sz -= ofs;
 +            for( size_t i = 0; i < sz; i++ )
 +                decoded[i] = decoded[ofs + i];
 +
 +            decoded.resize(sz);
 +            ofs = 0;
 +
 +            CV_Assert( !parser.empty() && ptr );
 +            char *beg = 0, *end = 0;
 +            bool ok = parser->getBase64Row(ptr, indent, beg, end);
 +            ptr = end;
 +            std::copy(beg, end, std::back_inserter(encoded));
 +            totalchars += end - beg;
 +
 +            if( !ok || beg == end )
 +            {
 +                // in the end of base64 sequence pad it with '=' characters so that
 +                // its total length is multiple of
 +                eos = true;
 +                size_t tc = totalchars;
 +                for( ; tc % 4 != 0; tc++ )
 +                    encoded.push_back('=');
 +            }
 +
 +            int i = 0, j, n = (int)encoded.size();
 +            if( n > 0 )
 +            {
 +                const uchar* tab = base64tab;
 +                char* src = &encoded[0];
 +
 +                for( ; i <= n - 4; i += 4 )
 +                {
 +                    // dddddd cccccc bbbbbb aaaaaa => ddddddcc ccccbbbb bbaaaaaa
 +                    uchar d = tab[(int)(uchar)src[i]], c = tab[(int)(uchar)src[i+1]];
 +                    uchar b = tab[(int)(uchar)src[i+2]], a = tab[(int)(uchar)src[i+3]];
 +
 +                    decoded.push_back((uchar)((d << 2) | (c >> 4)));
 +                    decoded.push_back((uchar)((c << 4) | (b >> 2)));
 +                    decoded.push_back((uchar)((b << 6) | a));
 +                }
 +            }
 +
 +            if( i > 0 && encoded[i-1] == '=' )
 +            {
 +                if( i > 1 && encoded[i-2] == '=' && !decoded.empty() )
 +                    decoded.pop_back();
 +                if( !decoded.empty() )
 +                    decoded.pop_back();
 +            }
 +
 +            n -= i;
 +            for( j = 0; j < n; j++ )
 +                encoded[j] = encoded[i + j];
 +            encoded.resize(n);
 +
 +            return (int)decoded.size() >= needed;
 +        }
 +
 +        uchar getUInt8()
 +        {
 +            size_t sz = decoded.size();
 +            if( ofs >= sz && !readMore(1) )
 +                return (uchar)0;
 +            return decoded[ofs++];
 +        }
 +
 +        ushort getUInt16()
 +        {
 +            size_t sz = decoded.size();
 +            if( ofs + 2 > sz && !readMore(2) )
 +                return (ushort)0;
 +            ushort val = (decoded[ofs] + (decoded[ofs + 1] << 8));
 +            ofs += 2;
 +            return val;
 +        }
 +
 +        int getInt32()
 +        {
 +            size_t sz = decoded.size();
 +            if( ofs + 4 > sz && !readMore(4) )
 +                return 0;
 +            int ival = readInt(&decoded[ofs]);
 +            ofs += 4;
 +            return ival;
 +        }
 +
 +        double getFloat64()
 +        {
 +            size_t sz = decoded.size();
 +            if( ofs + 8 > sz && !readMore(8) )
 +                return 0;
 +            double fval = readReal(&decoded[ofs]);
 +            ofs += 8;
 +            return fval;
 +        }
 +
 +        bool endOfStream() const { return eos; }
 +        char* getPtr() const { return ptr; }
 +    protected:
 +
 +        Ptr<FileStorageParser> parser;
 +        char* ptr;
 +        int indent;
 +        std::vector<char> encoded;
 +        std::vector<uchar> decoded;
 +        size_t ofs;
 +        size_t totalchars;
 +        bool eos;
 +    };
 +
 +    char* parseBase64(char* ptr, int indent, FileNode& collection)
 +    {
 +        const int BASE64_HDR_SIZE = 24;
 +        char dt[BASE64_HDR_SIZE+1] = {0};
 +        base64decoder.init(parser, ptr, indent);
 +
 +        int i, k;
 +
 +        for( i = 0; i < BASE64_HDR_SIZE; i++ )
 +            dt[i] = (char)base64decoder.getUInt8();
 +        for( i = 0; i < BASE64_HDR_SIZE; i++ )
 +            if( isspace(dt[i]))
 +                break;
 +        dt[i] = '\0';
 +
 +        CV_Assert( !base64decoder.endOfStream() );
 +
 +        int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2];
 +        int fmt_pair_count = fs::decodeFormat( dt, fmt_pairs, CV_FS_MAX_FMT_PAIRS );
 +        int ival = 0;
 +        double fval = 0;
 +
 +        for(;;)
 +        {
 +            for( k = 0; k < fmt_pair_count; k++ )
 +            {
 +                int elem_type = fmt_pairs[k*2+1];
 +                int count = fmt_pairs[k*2];
 +
 +                for( i = 0; i < count; i++ )
 +                {
 +                    int node_type = FileNode::INT;
 +                    switch( elem_type )
 +                    {
 +                    case CV_8U:
 +                        ival = base64decoder.getUInt8();
 +                        break;
 +                    case CV_8S:
 +                        ival = (char)base64decoder.getUInt8();
 +                        break;
 +                    case CV_16U:
 +                        ival = base64decoder.getUInt16();
 +                        break;
 +                    case CV_16S:
 +                        ival = (short)base64decoder.getUInt16();
 +                        break;
 +                    case CV_32S:
 +                        ival = base64decoder.getInt32();
 +                        break;
 +                    case CV_32F:
 +                        {
 +                        Cv32suf v;
 +                        v.i = base64decoder.getInt32();
 +                        fval = v.f;
 +                        node_type = FileNode::REAL;
 +                        }
 +                        break;
 +                    case CV_64F:
 +                        fval = base64decoder.getFloat64();
 +                        node_type = FileNode::REAL;
 +                        break;
 +                    case CV_16F:
 +                        fval = (float)float16_t::fromBits(base64decoder.getUInt16());
 +                        node_type = FileNode::REAL;
 +                        break;
 +                    default:
 +                        CV_Error( Error::StsUnsupportedFormat, "Unsupported type" );
 +                    }
 +
 +                    if( base64decoder.endOfStream() )
 +                        break;
 +                    addNode(collection, std::string(), node_type,
 +                            node_type == FileNode::INT ? (void*)&ival : (void*)&fval, -1);
 +                }
 +            }
 +            if( base64decoder.endOfStream() )
 +                break;
 +        }
 +
 +        finalizeCollection(collection);
 +        return base64decoder.getPtr();
 +    }
 +
 +    void parseError( const char* func_name, const std::string& err_msg, const char* source_file, int source_line )
 +    {
 +        std::string msg = format("%s(%d): %s", filename.c_str(), lineno, err_msg.c_str());
 +        error(Error::StsParseError, func_name, msg.c_str(), source_file, source_line );
 +    }
 +
 +    const uchar* getNodePtr(size_t blockIdx, size_t ofs) const
 +    {
 +        CV_Assert( blockIdx < fs_data_ptrs.size());
 +        CV_Assert( ofs < fs_data_blksz[blockIdx]);
 +
 +        return fs_data_ptrs[blockIdx] + ofs;
 +    }
 +
 +    std::string getName( size_t nameofs ) const
 +    {
 +        CV_Assert( nameofs < str_hash_data.size() );
 +        return std::string(&str_hash_data[nameofs]);
 +    }
 +
 +    FileStorage* getFS() { return fs_ext; }
 +
 +    FileStorage* fs_ext;
 +
 +    std::string filename;
 +    int flags;
 +    bool empty_stream;
 +
 +    FILE* file;
 +    gzFile gzfile;
 +
 +    bool is_opened;
 +    bool dummy_eof;
 +    bool write_mode;
 +    bool mem_mode;
 +    int fmt;
 +
 +    State state; //!< current state of the FileStorage (used only for writing)
 +    int space, wrap_margin;
 +    std::deque<FStructData> write_stack;
 +    std::vector<char> buffer;
 +    size_t bufofs;
 +
 +    std::deque<char> outbuf;
 +
 +    Ptr<FileStorageEmitter> emitter;
 +    Ptr<FileStorageParser> parser;
 +    Base64Decoder base64decoder;
 +
 +    std::vector<FileNode> roots;
 +    std::vector<Ptr<std::vector<uchar> > > fs_data;
 +    std::vector<uchar*> fs_data_ptrs;
 +    std::vector<size_t> fs_data_blksz;
 +    size_t freeSpaceOfs;
 +    typedef std::unordered_map<std::string, unsigned> str_hash_t;
 +    str_hash_t str_hash;
 +    std::vector<char> str_hash_data;
 +
 +    std::vector<char> strbufv;
 +    char* strbuf;
 +    size_t strbufsize;
 +    size_t strbufpos;
 +    int lineno;
 +};
 +
 +FileStorage::FileStorage()
 +    : state(0)
 +{
 +    p = makePtr<FileStorage::Impl>(this);
 +}
 +
 +FileStorage::FileStorage(const String& filename, int flags, const String& encoding)
 +    : state(0)
 +{
 +    p = makePtr<FileStorage::Impl>(this);
 +    bool ok = p->open(filename.c_str(), flags, encoding.c_str());
 +    if(ok)
 +        state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP;
 +}
 +
 +void FileStorage::startWriteStruct(const String& name, int struct_flags, const String& typeName)
 +{
 +    p->startWriteStruct(name.c_str(), struct_flags, typeName.c_str());
 +    elname = String();
 +    if ((struct_flags & FileNode::TYPE_MASK) == FileNode::SEQ)
 +        state = FileStorage::VALUE_EXPECTED;
 +    else
 +        state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP;
 +}
 +
 +void FileStorage::endWriteStruct()
 +{
 +    p->endWriteStruct();
 +    state = p->write_stack.empty() || FileNode::isMap(p->write_stack.back().flags) ?
 +        FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP :
 +        FileStorage::VALUE_EXPECTED;
 +    elname = String();
 +}
 +
 +FileStorage::~FileStorage()
 +{
 +    p.release();
 +}
 +
 +bool FileStorage::open(const String& filename, int flags, const String& encoding)
 +{
 +    bool ok = p->open(filename.c_str(), flags, encoding.c_str());
 +    if(ok)
 +        state = FileStorage::NAME_EXPECTED + FileStorage::INSIDE_MAP;
 +    return ok;
 +}
 +
 +bool FileStorage::isOpened() const { return p->is_opened; }
 +
 +void FileStorage::release()
 +{
 +    p->release();
 +}
 +
 +FileNode FileStorage::root(int i) const
 +{
 +    if( p.empty() || p->roots.empty() || i < 0 || i >= (int)p->roots.size() )
 +        return FileNode();
 +
 +    return p->roots[i];
 +}
 +
 +FileNode FileStorage::getFirstTopLevelNode() const
 +{
 +    FileNode r = root();
 +    FileNodeIterator it = r.begin();
 +    return it != r.end() ? *it : FileNode();
 +}
 +
 +std::string FileStorage::getDefaultObjectName(const std::string& _filename)
 +{
 +    static const char* stubname = "unnamed";
 +    const char* filename = _filename.c_str();
 +    const char* ptr2 = filename + _filename.size();
 +    const char* ptr = ptr2 - 1;
 +    cv::AutoBuffer<char> name_buf(_filename.size()+1);
 +
 +    while( ptr >= filename && *ptr != '\\' && *ptr != '/' && *ptr != ':' )
 +    {
 +        if( *ptr == '.' && (!*ptr2 || strncmp(ptr2, ".gz", 3) == 0) )
 +            ptr2 = ptr;
 +        ptr--;
 +    }
 +    ptr++;
 +    if( ptr == ptr2 )
 +        CV_Error( CV_StsBadArg, "Invalid filename" );
 +
 +    char* name = name_buf.data();
 +
 +    // name must start with letter or '_'
 +    if( !cv_isalpha(*ptr) && *ptr!= '_' ){
 +        *name++ = '_';
 +    }
 +
 +    while( ptr < ptr2 )
 +    {
 +        char c = *ptr++;
 +        if( !cv_isalnum(c) && c != '-' && c != '_' )
 +            c = '_';
 +        *name++ = c;
 +    }
 +    *name = '\0';
 +    name = name_buf.data();
 +    if( strcmp( name, "_" ) == 0 )
 +        strcpy( name, stubname );
 +    return name;
 +}
 +
 +
 +int FileStorage::getFormat() const
 +{
 +    return p->fmt;
 +}
 +
 +FileNode FileStorage::operator [](const char* key) const
 +{
 +    return this->operator[](std::string(key));
 +}
 +
 +FileNode FileStorage::operator [](const std::string& key) const
 +{
 +    FileNode res;
 +    for (size_t i = 0; i < p->roots.size(); i++)
 +    {
 +        res = p->roots[i][key];
 +        if (!res.empty())
 +            break;
 +    }
 +    return res;
 +}
 +
 +String FileStorage::releaseAndGetString()
 +{
 +    String buf;
 +    p->release(&buf);
 +    return buf;
 +}
 +
 +void FileStorage::writeRaw( const String& fmt, const void* vec, size_t len )
 +{
 +    p->writeRawData(fmt, (const uchar*)vec, len);
 +}
 +
 +void FileStorage::writeComment( const String& comment, bool eol_comment )
 +{
 +    p->writeComment(comment.c_str(), eol_comment);
 +}
 +
 +void writeScalar( FileStorage& fs, int value )
 +{
 +    fs.p->write(String(), value);
 +}
 +
 +void writeScalar( FileStorage& fs, float value )
 +{
 +    fs.p->write(String(), (double)value);
 +}
 +
 +void writeScalar( FileStorage& fs, double value )
 +{
 +    fs.p->write(String(), value);
 +}
 +
 +void writeScalar( FileStorage& fs, const String& value )
 +{
 +    fs.p->write(String(), value);
 +}
 +
 +void write( FileStorage& fs, const String& name, int value )
 +{
 +    fs.p->write(name, value);
 +}
 +
 +void write( FileStorage& fs, const String& name, float value )
 +{
 +    fs.p->write(name, (double)value);
 +}
 +
 +void write( FileStorage& fs, const String& name, double value )
 +{
 +    fs.p->write(name, value);
 +}
 +
 +void write( FileStorage& fs, const String& name, const String& value )
 +{
 +    fs.p->write(name, value);
 +}
 +
 +void FileStorage::write(const String& name, int val) { p->write(name, val); }
 +void FileStorage::write(const String& name, double val) { p->write(name, val); }
 +void FileStorage::write(const String& name, const String& val) { p->write(name, val); }
 +void FileStorage::write(const String& name, const Mat& val) { cv::write(*this, name, val); }
 +void FileStorage::write(const String& name, const std::vector<String>& val) { cv::write(*this, name, val); }
 +
 +FileStorage& operator << (FileStorage& fs, const String& str)
 +{
 +    enum { NAME_EXPECTED = FileStorage::NAME_EXPECTED,
 +        VALUE_EXPECTED = FileStorage::VALUE_EXPECTED,
 +        INSIDE_MAP = FileStorage::INSIDE_MAP };
 +    const char* _str = str.c_str();
 +    if( !fs.isOpened() || !_str )
 +        return fs;
 +    Ptr<FileStorage::Impl>& fs_impl = fs.p;
 +    char c = *_str;
 +
 +    if( c == '}' || c == ']' )
 +    {
 +        if( fs_impl->write_stack.empty() )
 +            CV_Error_( CV_StsError, ("Extra closing '%c'", *_str) );
 +
 +        int struct_flags = fs_impl->write_stack.back().flags;
 +        char expected_bracket = FileNode::isMap(struct_flags) ? '}' : ']';
 +        if( c != expected_bracket )
 +            CV_Error_( CV_StsError, ("The closing '%c' does not match the opening '%c'", c, expected_bracket));
 +        fs_impl->endWriteStruct();
 +        CV_Assert(!fs_impl->write_stack.empty());
 +        struct_flags = fs_impl->write_stack.back().flags;
 +        fs.state = FileNode::isMap(struct_flags) ? INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED;
 +        fs.elname = String();
 +    }
 +    else if( fs.state == NAME_EXPECTED + INSIDE_MAP )
 +    {
 +        if (!cv_isalpha(c) && c != '_')
 +            CV_Error_( CV_StsError, ("Incorrect element name %s; should start with a letter or '_'", _str) );
 +        fs.elname = str;
 +        fs.state = VALUE_EXPECTED + INSIDE_MAP;
 +    }
 +    else if( (fs.state & 3) == VALUE_EXPECTED )
 +    {
 +        if( c == '{' || c == '[' )
 +        {
 +            int struct_flags = c == '{' ? FileNode::MAP : FileNode::SEQ;
 +            fs.state = struct_flags == FileNode::MAP ? INSIDE_MAP + NAME_EXPECTED : VALUE_EXPECTED;
 +            _str++;
 +            if( *_str == ':' )
 +            {
 +                _str++;
 +                if( !*_str )
 +                    struct_flags |= FileNode::FLOW;
 +            }
 +            fs_impl->startWriteStruct(!fs.elname.empty() ? fs.elname.c_str() : 0, struct_flags, *_str ? _str : 0 );
 +            fs.elname = String();
 +        }
 +        else
 +        {
 +            write( fs, fs.elname, (c == '\\' && (_str[1] == '{' || _str[1] == '}' ||
 +                                _str[1] == '[' || _str[1] == ']')) ? String(_str+1) : str );
 +            if( fs.state == INSIDE_MAP + VALUE_EXPECTED )
 +                fs.state = INSIDE_MAP + NAME_EXPECTED;
 +        }
 +    }
 +    else
 +        CV_Error( CV_StsError, "Invalid fs.state" );
 +    return fs;
 +}
 +
 +
 +FileNode::FileNode()
 +{
 +    fs = 0;
 +    blockIdx = ofs = 0;
 +}
 +
 +FileNode::FileNode(const FileStorage* _fs, size_t _blockIdx, size_t _ofs)
 +{
 +    fs = _fs;
 +    blockIdx = _blockIdx;
 +    ofs = _ofs;
 +}
 +
 +FileNode::FileNode(const FileNode& node)
 +{
 +    fs = node.fs;
 +    blockIdx = node.blockIdx;
 +    ofs = node.ofs;
 +}
 +
++FileNode& FileNode::operator=(const FileNode& node)
++{
++    fs = node.fs;
++    blockIdx = node.blockIdx;
++    ofs = node.ofs;
++    return *this;
++}
++
 +FileNode FileNode::operator[](const std::string& nodename) const
 +{
 +    if(!fs)
 +        return FileNode();
 +
 +    CV_Assert( isMap() );
 +
 +    unsigned key = fs->p->getStringOfs(nodename);
 +    size_t i, sz = size();
 +    FileNodeIterator it = begin();
 +
 +    for( i = 0; i < sz; i++, ++it )
 +    {
 +        FileNode n = *it;
 +        const uchar* p = n.ptr();
 +        unsigned key2 = (unsigned)readInt(p + 1);
 +        CV_Assert( key2 < fs->p->str_hash_data.size() );
 +        if( key == key2 )
 +            return n;
 +    }
 +    return FileNode();
 +}
 +
 +FileNode FileNode::operator[](const char* nodename) const
 +{
 +    return this->operator[](std::string(nodename));
 +}
 +
 +FileNode FileNode::operator[](int i) const
 +{
 +    if(!fs)
 +        return FileNode();
 +
 +    CV_Assert( isSeq() );
 +
 +    int sz = (int)size();
 +    CV_Assert( 0 <= i && i < sz );
 +
 +    FileNodeIterator it = begin();
 +    it += i;
 +
 +    return *it;
 +}
 +
 +std::vector<String> FileNode::keys() const
 +{
 +    CV_Assert(isMap());
 +
 +    std::vector<String> res;
 +    res.reserve(size());
 +    for (FileNodeIterator it = begin(); it != end(); ++it)
 +    {
 +        res.push_back((*it).name());
 +    }
 +    return res;
 +}
 +
 +int FileNode::type() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return NONE;
 +    return (*p & TYPE_MASK);
 +}
 +
 +bool FileNode::isMap(int flags) { return (flags & TYPE_MASK) == MAP; }
 +bool FileNode::isSeq(int flags) { return (flags & TYPE_MASK) == SEQ; }
 +bool FileNode::isCollection(int flags) { return isMap(flags) || isSeq(flags); }
 +bool FileNode::isFlow(int flags) { return (flags & FLOW) != 0; }
 +bool FileNode::isEmptyCollection(int flags) { return (flags & EMPTY) != 0; }
 +
 +bool FileNode::empty() const   { return fs == 0; }
 +bool FileNode::isNone() const  { return type() == NONE; }
 +bool FileNode::isSeq() const   { return type() == SEQ; }
 +bool FileNode::isMap() const   { return type() == MAP; }
 +bool FileNode::isInt() const   { return type() == INT;  }
 +bool FileNode::isReal() const  { return type() == REAL; }
 +bool FileNode::isString() const { return type() == STRING;  }
 +bool FileNode::isNamed() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return false;
 +    return (*p & NAMED) != 0;
 +}
 +
 +std::string FileNode::name() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return std::string();
 +    size_t nameofs = p[1] | (p[2]<<8) | (p[3]<<16) | (p[4]<<24);
 +    return fs->p->getName(nameofs);
 +}
 +
 +FileNode::operator int() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return 0;
 +    int tag = *p;
 +    int type = (tag & TYPE_MASK);
 +    p += (tag & NAMED) ? 5 : 1;
 +
 +    if( type == INT )
 +    {
 +        return readInt(p);
 +    }
 +    else if( type == REAL )
 +    {
 +        return cvRound(readReal(p));
 +    }
 +    else
 +        return 0x7fffffff;
 +}
 +
 +FileNode::operator float() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return 0.f;
 +    int tag = *p;
 +    int type = (tag & TYPE_MASK);
 +    p += (tag & NAMED) ? 5 : 1;
 +
 +    if( type == INT )
 +    {
 +        return (float)readInt(p);
 +    }
 +    else if( type == REAL )
 +    {
 +        return (float)readReal(p);
 +    }
 +    else
 +        return FLT_MAX;
 +}
 +
 +FileNode::operator double() const
 +{
 +    const uchar* p = ptr();
 +    if(!p)
 +        return 0.f;
 +    int tag = *p;
 +    int type = (tag & TYPE_MASK);
 +    p += (tag & NAMED) ? 5 : 1;
 +
 +    if( type == INT )
 +    {
 +        return (double)readInt(p);
 +    }
 +    else if( type == REAL )
 +    {
 +        return readReal(p);
 +    }
 +    else
 +        return DBL_MAX;
 +}
 +
 +double FileNode::real() const  { return double(*this); }
 +std::string FileNode::string() const
 +{
 +    const uchar* p = ptr();
 +    if( !p || (*p & TYPE_MASK) != STRING )
 +        return std::string();
 +    p += (*p & NAMED) ? 5 : 1;
 +    size_t sz = (size_t)(unsigned)readInt(p);
 +    return std::string((const char*)(p + 4), sz - 1);
 +}
 +Mat FileNode::mat() const { Mat value; read(*this, value, Mat()); return value; }
 +
 +FileNodeIterator FileNode::begin() const { return FileNodeIterator(*this, false); }
 +FileNodeIterator FileNode::end() const   { return FileNodeIterator(*this, true); }
 +
 +void FileNode::readRaw( const std::string& fmt, void* vec, size_t len ) const
 +{
 +    FileNodeIterator it = begin();
 +    it.readRaw( fmt, vec, len );
 +}
 +
 +size_t FileNode::size() const
 +{
 +    const uchar* p = ptr();
 +    if( !p )
 +        return 0;
 +    int tag = *p;
 +    int tp = tag & TYPE_MASK;
 +    if( tp == MAP || tp == SEQ )
 +    {
 +        if( tag & NAMED )
 +            p += 4;
 +        return (size_t)(unsigned)readInt(p + 5);
 +    }
 +    return tp != NONE;
 +}
 +
 +size_t FileNode::rawSize() const
 +{
 +    const uchar* p0 = ptr(), *p = p0;
 +    if( !p )
 +        return 0;
 +    int tag = *p++;
 +    int tp = tag & TYPE_MASK;
 +    if( tag & NAMED )
 +        p += 4;
 +    size_t sz0 = (size_t)(p - p0);
 +    if( tp == INT )
 +        return sz0 + 4;
 +    if( tp == REAL )
 +        return sz0 + 8;
 +    if( tp == NONE )
 +        return sz0;
 +    CV_Assert( tp == STRING || tp == SEQ || tp == MAP );
 +    return sz0 + 4 + readInt(p);
 +}
 +
 +uchar* FileNode::ptr()
 +{
 +    return !fs ? 0 : (uchar*)fs->p->getNodePtr(blockIdx, ofs);
 +}
 +
 +const uchar* FileNode::ptr() const
 +{
 +    return !fs ? 0 : fs->p->getNodePtr(blockIdx, ofs);
 +}
 +
 +void FileNode::setValue( int type, const void* value, int len )
 +{
 +    uchar *p = ptr();
 +    CV_Assert(p != 0);
 +
 +    int tag = *p;
 +    int current_type = tag & TYPE_MASK;
 +    CV_Assert( current_type == NONE || current_type == type );
 +
 +    int sz = 1;
 +
 +    if( tag & NAMED )
 +        sz += 4;
 +
 +    if( type == INT )
 +        sz += 4;
 +    else if( type == REAL )
 +        sz += 8;
 +    else if( type == STRING )
 +    {
 +        if( len < 0 )
 +            len = (int)strlen((const char*)value);
 +        sz += 4 + len + 1; // besides the string content,
 +                           // take the size (4 bytes) and the final '\0' into account
 +    }
 +    else
 +        CV_Error(Error::StsNotImplemented, "Only scalar types can be dynamically assigned to a file node");
 +
 +    p = fs->p->reserveNodeSpace(*this, sz);
 +    *p++ = (uchar)(type | (tag & NAMED));
 +    if( tag & NAMED )
 +        p += 4;
 +
 +    if( type == INT )
 +    {
 +        int ival = *(const int*)value;
 +        writeInt(p, ival);
 +    }
 +    else if( type == REAL )
 +    {
 +        double dbval = *(const double*)value;
 +        writeReal(p, dbval);
 +    }
 +    else if( type == STRING )
 +    {
 +        const char* str = (const char*)value;
 +        writeInt(p, len + 1);
 +        memcpy(p + 4, str, len);
 +        p[4 + len] = (uchar)'\0';
 +    }
 +}
 +
 +FileNodeIterator::FileNodeIterator()
 +{
 +    fs = 0;
 +    blockIdx = 0;
 +    ofs = 0;
 +    blockSize = 0;
 +    nodeNElems = 0;
 +    idx = 0;
 +}
 +
 +FileNodeIterator::FileNodeIterator( const FileNode& node, bool seekEnd )
 +{
 +    fs = node.fs;
 +    idx = 0;
 +    if( !fs )
 +        blockIdx = ofs = blockSize = nodeNElems = 0;
 +    else
 +    {
 +        blockIdx = node.blockIdx;
 +        ofs = node.ofs;
 +
 +        bool collection = node.isSeq() || node.isMap();
 +        if( node.isNone() )
 +        {
 +            nodeNElems = 0;
 +        }
 +        else if( !collection )
 +        {
 +            nodeNElems = 1;
 +            if( seekEnd )
 +            {
 +                idx = 1;
 +                ofs += node.rawSize();
 +            }
 +        }
 +        else
 +        {
 +            nodeNElems = node.size();
 +            const uchar* p0 = node.ptr(), *p = p0 + 1;
 +            if(*p0 & FileNode::NAMED )
 +                p += 4;
 +            if( !seekEnd )
 +                ofs += (p - p0) + 8;
 +            else
 +            {
 +                size_t rawsz = (size_t)(unsigned)readInt(p);
 +                ofs += (p - p0) + 4 + rawsz;
 +                idx = nodeNElems;
 +            }
 +        }
 +        fs->p->normalizeNodeOfs(blockIdx, ofs);
 +        blockSize = fs->p->fs_data_blksz[blockIdx];
 +    }
 +}
 +
 +FileNodeIterator::FileNodeIterator(const FileNodeIterator& it)
 +{
 +    fs = it.fs;
 +    blockIdx = it.blockIdx;
 +    ofs = it.ofs;
 +    blockSize = it.blockSize;
 +    nodeNElems = it.nodeNElems;
 +    idx = it.idx;
 +}
++
++FileNodeIterator& FileNodeIterator::operator=(const FileNodeIterator& it)
++{
++    fs = it.fs;
++    blockIdx = it.blockIdx;
++    ofs = it.ofs;
++    blockSize = it.blockSize;
++    nodeNElems = it.nodeNElems;
++    idx = it.idx;
++    return *this;
++}
 +
 +FileNode FileNodeIterator::operator *() const
 +{
 +    return FileNode(idx < nodeNElems ? fs : 0, blockIdx, ofs);
 +}
 +
 +FileNodeIterator& FileNodeIterator::operator ++ ()
 +{
 +    if( idx == nodeNElems || !fs )
 +        return *this;
 +    idx++;
 +    FileNode n(fs, blockIdx, ofs);
 +    ofs += n.rawSize();
 +    if( ofs >= blockSize )
 +    {
 +        fs->p->normalizeNodeOfs(blockIdx, ofs);
 +        blockSize = fs->p->fs_data_blksz[blockIdx];
 +    }
 +    return *this;
 +}
 +
 +FileNodeIterator FileNodeIterator::operator ++ (int)
 +{
 +    FileNodeIterator it = *this;
 +    ++(*this);
 +    return it;
 +}
 +
 +FileNodeIterator& FileNodeIterator::operator += (int _ofs)
 +{
 +    CV_Assert( _ofs >= 0 );
 +    for( ; _ofs > 0; _ofs-- )
 +        this->operator ++();
 +    return *this;
 +}
 +
 +FileNodeIterator& FileNodeIterator::readRaw( const String& fmt, void* _data0, size_t maxsz)
 +{
 +    if( fs && idx < nodeNElems )
 +    {
 +        uchar* data0 = (uchar*)_data0;
 +        int fmt_pairs[CV_FS_MAX_FMT_PAIRS*2];
 +        int fmt_pair_count = fs::decodeFormat( fmt.c_str(), fmt_pairs, CV_FS_MAX_FMT_PAIRS );
 +        size_t esz = fs::calcStructSize( fmt.c_str(), 0 );
 +
 +        CV_Assert( maxsz % esz == 0 );
 +        maxsz /= esz;
 +
 +        for( ; maxsz > 0; maxsz--, data0 += esz )
 +        {
 +            size_t offset = 0;
 +            for( int k = 0; k < fmt_pair_count; k++ )
 +            {
 +                int elem_type = fmt_pairs[k*2+1];
 +                int elem_size = CV_ELEM_SIZE(elem_type);
 +
 +                int count = fmt_pairs[k*2];
 +                offset = alignSize( offset, elem_size );
 +                uchar* data = data0 + offset;
 +
 +                for( int i = 0; i < count; i++, ++(*this) )
 +                {
 +                    FileNode node = *(*this);
 +                    if( node.isInt() )
 +                    {
 +                        int ival = (int)node;
 +                        switch( elem_type )
 +                        {
 +                        case CV_8U:
 +                            *(uchar*)data = saturate_cast<uchar>(ival);
 +                            data++;
 +                            break;
 +                        case CV_8S:
 +                            *(char*)data = saturate_cast<schar>(ival);
 +                            data++;
 +                            break;
 +                        case CV_16U:
 +                            *(ushort*)data = saturate_cast<ushort>(ival);
 +                            data += sizeof(ushort);
 +                            break;
 +                        case CV_16S:
 +                            *(short*)data = saturate_cast<short>(ival);
 +                            data += sizeof(short);
 +                            break;
 +                        case CV_32S:
 +                            *(int*)data = ival;
 +                            data += sizeof(int);
 +                            break;
 +                        case CV_32F:
 +                            *(float*)data = (float)ival;
 +                            data += sizeof(float);
 +                            break;
 +                        case CV_64F:
 +                            *(double*)data = (double)ival;
 +                            data += sizeof(double);
 +                            break;
 +                        case CV_16F:
 +                            *(float16_t*)data = float16_t((float)ival);
 +                            data += sizeof(float16_t);
 +                            break;
 +                        default:
 +                            CV_Error( Error::StsUnsupportedFormat, "Unsupported type" );
 +                        }
 +                    }
 +                    else if( node.isReal() )
 +                    {
 +                        double fval = (double)node;
 +
 +                        switch( elem_type )
 +                        {
 +                        case CV_8U:
 +                            *(uchar*)data = saturate_cast<uchar>(fval);
 +                            data++;
 +                            break;
 +                        case CV_8S:
 +                            *(char*)data = saturate_cast<schar>(fval);
 +                            data++;
 +                            break;
 +                        case CV_16U:
 +                            *(ushort*)data = saturate_cast<ushort>(fval);
 +                            data += sizeof(ushort);
 +                            break;
 +                        case CV_16S:
 +                            *(short*)data = saturate_cast<short>(fval);
 +                            data += sizeof(short);
 +                            break;
 +                        case CV_32S:
 +                            *(int*)data = saturate_cast<int>(fval);
 +                            data += sizeof(int);
 +                            break;
 +                        case CV_32F:
 +                            *(float*)data = (float)fval;
 +                            data += sizeof(float);
 +                            break;
 +                        case CV_64F:
 +                            *(double*)data = fval;
 +                            data += sizeof(double);
 +                            break;
 +                        case CV_16F:
 +                            *(float16_t*)data = float16_t((float)fval);
 +                            data += sizeof(float16_t);
 +                            break;
 +                        default:
 +                            CV_Error( Error::StsUnsupportedFormat, "Unsupported type" );
 +                        }
 +                    }
 +                    else
 +                        CV_Error( Error::StsError, "readRawData can only be used to read plain sequences of numbers" );
 +                }
 +                offset = (int)(data - data0);
 +            }
 +        }
 +    }
 +
 +    return *this;
 +}
 +
 +bool FileNodeIterator::equalTo(const FileNodeIterator& it) const
 +{
 +    return fs == it.fs && blockIdx == it.blockIdx && ofs == it.ofs &&
 +           idx == it.idx && nodeNElems == it.nodeNElems;
 +}
 +
 +size_t FileNodeIterator::remaining() const
 +{
 +    return nodeNElems - idx;
 +}
 +
 +bool operator == ( const FileNodeIterator& it1, const FileNodeIterator& it2 )
 +{
 +    return it1.equalTo(it2);
 +}
 +
 +bool operator != ( const FileNodeIterator& it1, const FileNodeIterator& it2 )
 +{
 +    return !it1.equalTo(it2);
 +}
 +
 +void read(const FileNode& node, int& val, int default_val)
 +{
 +    val = default_val;
 +    if( !node.empty() )
 +    {
 +        val = (int)node;
 +    }
 +}
 +
 +void read(const FileNode& node, double& val, double default_val)
 +{
 +    val = default_val;
 +    if( !node.empty() )
 +    {
 +        val = (double)node;
 +    }
 +}
 +
 +void read(const FileNode& node, float& val, float default_val)
 +{
 +    val = default_val;
 +    if( !node.empty() )
 +    {
 +        val = (float)node;
 +    }
 +}
 +
 +void read(const FileNode& node, std::string& val, const std::string& default_val)
 +{
 +    val = default_val;
 +    if( !node.empty() )
 +    {
 +        val = (std::string)node;
 +    }
 +}
 +
 +FileStorage_API::~FileStorage_API() {}
 +
 +namespace internal
 +{
 +
 +WriteStructContext::WriteStructContext(FileStorage& _fs, const std::string& name,
 +                                       int flags, const std::string& typeName)
 +{
 +    fs = &_fs;
 +    fs->startWriteStruct(name, flags, typeName);
 +}
 +
 +WriteStructContext::~WriteStructContext()
 +{
 +    fs->endWriteStruct();
 +}
 +
 +}
 +
  }
Simple merge
Simple merge
                if (text) Module.printErr('[post-exception status] ' + text);
              };
            };
+           function opencvjs_LoadError() {
+             Module.printErr('Failed to load/initialize opencv.js');
+             QUnit.module('LoaderFatalError', {});
+             QUnit.config.module = 'LoaderFatalError';
+             QUnit.only("Failed to load OpenCV.js", function(assert) {
+               assert.ok(false, "Can't load/initialize opencv.js");
+             });
+             QUnit.start();
+           }
          </script>
+     </head>
+     <body>
+         <div id="qunit"></div>
+         <div id="qunit-fixture"></div>
+         <script type="application/javascript" async src="opencv.js" onerror="opencvjs_LoadError()"></script>
+         <script type="application/javascript" src="test_mat.js"></script>
+         <script type="application/javascript" src="test_utils.js"></script>
+         <script type="application/javascript" src="test_imgproc.js"></script>
+         <script type="application/javascript" src="test_objdetect.js"></script>
+         <script type="application/javascript" src="test_video.js"></script>
++        <script type="application/javascript" src="test_photo.js"></script>
+         <script type="application/javascript" src="test_features2d.js"></script>
+         <script type="application/javascript" src="test_calib3d.js"></script>
      </body>
  </html>
@@@ -428,8 -428,11 +428,11 @@@ protected
      // updates progress bar
      virtual int update_progress( int progress, int test_case_idx, int count, double dt );
  
+     // dump test case input parameters
+     virtual void dump_test_case(int test_case_idx, std::ostream* out);
      // finds test parameter
 -    const CvFileNode* find_param( CvFileStorage* fs, const char* param_name );
 +    cv::FileNode find_param( const cv::FileStorage& fs, const char* param_name );
  
      // name of the test (it is possible to locate a test by its name)
      string name;
Simple merge