int cvFindChessboardCorners()

in modules/calib3d/src/calibinit.cpp [228:569]


int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
                             CvPoint2D32f* out_corners, int* out_corner_count,
                             int flags )
{
    int found = 0;
    CvCBQuad *quads = 0, **quad_group = 0;
    CvCBCorner *corners = 0, **corner_group = 0;

    try
    {
    int k = 0;
    const int min_dilations = 0;
    const int max_dilations = 7;
    cv::Ptr<CvMat> norm_img, thresh_img;
#ifdef DEBUG_CHESSBOARD
    cv::Ptr<IplImage> dbg_img;
    cv::Ptr<IplImage> dbg1_img;
    cv::Ptr<IplImage> dbg2_img;
#endif
    cv::Ptr<CvMemStorage> storage;

    CvMat stub, *img = (CvMat*)arr;

    int expected_corners_num = (pattern_size.width/2+1)*(pattern_size.height/2+1);

    int prev_sqr_size = 0;

    if( out_corner_count )
        *out_corner_count = 0;

    IplImage _img;
    int quad_count = 0, group_idx = 0, dilations = 0;

    img = cvGetMat( img, &stub );
    //debug_img = img;

    if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 )
        CV_Error( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" );

    if( pattern_size.width <= 2 || pattern_size.height <= 2 )
        CV_Error( CV_StsOutOfRange, "Both width and height of the pattern should have bigger than 2" );

    if( !out_corners )
        CV_Error( CV_StsNullPtr, "Null pointer to corners" );

    storage.reset(cvCreateMemStorage(0));
    thresh_img.reset(cvCreateMat( img->rows, img->cols, CV_8UC1 ));

#ifdef DEBUG_CHESSBOARD
    dbg_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 );
    dbg1_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 );
    dbg2_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 3 );
#endif

    if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) )
    {
        // equalize the input image histogram -
        // that should make the contrast between "black" and "white" areas big enough
        norm_img.reset(cvCreateMat( img->rows, img->cols, CV_8UC1 ));

        if( CV_MAT_CN(img->type) != 1 )
        {
            cvCvtColor( img, norm_img, CV_BGR2GRAY );
            img = norm_img;
        }

        if( flags & CV_CALIB_CB_NORMALIZE_IMAGE )
        {
            cvEqualizeHist( img, norm_img );
            img = norm_img;
        }
    }

    if( flags & CV_CALIB_CB_FAST_CHECK)
    {
        cvGetImage(img, &_img);
        int check_chessboard_result = cvCheckChessboard(&_img, pattern_size);
        if(check_chessboard_result <= 0)
        {
            return 0;
        }
    }

    // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
    // This is necessary because some squares simply do not separate properly with a single dilation.  However,
    // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
    // making it difficult to detect smaller squares.
    for( k = 0; k < 6; k++ )
    {
        for( dilations = min_dilations; dilations <= max_dilations; dilations++ )
        {
            if (found)
                break;      // already found it

            cvFree(&quads);
            cvFree(&corners);

            /*if( k == 1 )
            {
                //Pattern was not found using binarization
                // Run multi-level quads extraction
                // In case one-level binarization did not give enough number of quads
                CV_CALL( quad_count = icvGenerateQuadsEx( &quads, &corners, storage, img, thresh_img, dilations, flags ));
                PRINTF("EX quad count: %d/%d\n", quad_count, expected_corners_num);
            }
            else*/
            {
                // convert the input grayscale image to binary (black-n-white)
                if( flags & CV_CALIB_CB_ADAPTIVE_THRESH )
                {
                    int block_size = cvRound(prev_sqr_size == 0 ?
                        MIN(img->cols,img->rows)*(k%2 == 0 ? 0.2 : 0.1): prev_sqr_size*2)|1;

                    // convert to binary
                    cvAdaptiveThreshold( img, thresh_img, 255,
                        CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, (k/2)*5 );
                    if (dilations > 0)
                        cvDilate( thresh_img, thresh_img, 0, dilations-1 );
                }
                else
                {
                    // Make dilation before the thresholding.
                    // It splits chessboard corners
                    //cvDilate( img, thresh_img, 0, 1 );

                    // empiric threshold level
                    double mean = cvAvg( img ).val[0];
                    int thresh_level = cvRound( mean - 10 );
                    thresh_level = MAX( thresh_level, 10 );

                    cvThreshold( img, thresh_img, thresh_level, 255, CV_THRESH_BINARY );
                    cvDilate( thresh_img, thresh_img, 0, dilations );
                }

#ifdef DEBUG_CHESSBOARD
                cvCvtColor(thresh_img,dbg_img,CV_GRAY2BGR);
#endif

                // So we can find rectangles that go to the edge, we draw a white line around the image edge.
                // Otherwise FindContours will miss those clipped rectangle contours.
                // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
                cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1,
                    thresh_img->rows-1), CV_RGB(255,255,255), 3, 8);

                quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags );

                PRINTF("Quad count: %d/%d\n", quad_count, expected_corners_num);
            }


#ifdef DEBUG_CHESSBOARD
            cvCopy(dbg_img, dbg1_img);
            cvNamedWindow("all_quads", 1);
            // copy corners to temp array
            for(int i = 0; i < quad_count; i++ )
            {
                for (int k=0; k<4; k++)
                {
                    CvPoint2D32f pt1, pt2;
                    CvScalar color = CV_RGB(30,255,30);
                    pt1 = quads[i].corners[k]->pt;
                    pt2 = quads[i].corners[(k+1)%4]->pt;
                    pt2.x = (pt1.x + pt2.x)/2;
                    pt2.y = (pt1.y + pt2.y)/2;
                    if (k>0)
                        color = CV_RGB(200,200,0);
                    cvLine( dbg1_img, cvPointFrom32f(pt1), cvPointFrom32f(pt2), color, 3, 8);
                }
            }


            cvShowImage("all_quads", (IplImage*)dbg1_img);
            cvWaitKey();
#endif

            if( quad_count <= 0 )
                continue;

            // Find quad's neighbors
            icvFindQuadNeighbors( quads, quad_count );

            // allocate extra for adding in icvOrderFoundQuads
            cvFree(&quad_group);
            cvFree(&corner_group);
            quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * (quad_count+quad_count / 2));
            corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * (quad_count+quad_count / 2)*4 );

            for( group_idx = 0; ; group_idx++ )
            {
                int count = 0;
                count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage );

                int icount = count;
                if( count == 0 )
                    break;

                // order the quad corners globally
                // maybe delete or add some
                PRINTF("Starting ordering of inner quads\n");
                count = icvOrderFoundConnectedQuads(count, quad_group, &quad_count, &quads, &corners,
                    pattern_size, storage );
                PRINTF("Orig count: %d  After ordering: %d\n", icount, count);


#ifdef DEBUG_CHESSBOARD
                cvCopy(dbg_img,dbg2_img);
                cvNamedWindow("connected_group", 1);
                // copy corners to temp array
                for(int i = 0; i < quad_count; i++ )
                {
                    if (quads[i].group_idx == group_idx)
                        for (int k=0; k<4; k++)
                        {
                            CvPoint2D32f pt1, pt2;
                            CvScalar color = CV_RGB(30,255,30);
                            if (quads[i].ordered)
                                color = CV_RGB(255,30,30);
                            pt1 = quads[i].corners[k]->pt;
                            pt2 = quads[i].corners[(k+1)%4]->pt;
                            pt2.x = (pt1.x + pt2.x)/2;
                            pt2.y = (pt1.y + pt2.y)/2;
                            if (k>0)
                                color = CV_RGB(200,200,0);
                            cvLine( dbg2_img, cvPointFrom32f(pt1), cvPointFrom32f(pt2), color, 3, 8);
                        }
                }
                cvShowImage("connected_group", (IplImage*)dbg2_img);
                cvWaitKey();
#endif

                if (count == 0)
                    continue;       // haven't found inner quads


                // If count is more than it should be, this will remove those quads
                // which cause maximum deviation from a nice square pattern.
                count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size );
                PRINTF("Connected group: %d  orig count: %d cleaned: %d\n", group_idx, icount, count);

                count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size );
                PRINTF("Connected group: %d  count: %d  cleaned: %d\n", group_idx, icount, count);

                {
                int n = count > 0 ? pattern_size.width * pattern_size.height : -count;
                n = MIN( n, pattern_size.width * pattern_size.height );
                float sum_dist = 0;
                int total = 0;

                for(int i = 0; i < n; i++ )
                {
                    int ni = 0;
                    float avgi = corner_group[i]->meanDist(&ni);
                    sum_dist += avgi*ni;
                    total += ni;
                }
                prev_sqr_size = cvRound(sum_dist/MAX(total, 1));

                if( count > 0 || (out_corner_count && -count > *out_corner_count) )
                {
                    // copy corners to output array
                    for(int i = 0; i < n; i++ )
                        out_corners[i] = corner_group[i]->pt;

                    if( out_corner_count )
                        *out_corner_count = n;

                    if( count == pattern_size.width*pattern_size.height &&
                        icvCheckBoardMonotony( out_corners, pattern_size ))
                    {
                        found = 1;
                        break;
                    }
                }
                }
            }
        }//dilations
    }//

    if( found )
        found = icvCheckBoardMonotony( out_corners, pattern_size );

    // check that none of the found corners is too close to the image boundary
    if( found )
    {
        const int BORDER = 8;
        for( k = 0; k < pattern_size.width*pattern_size.height; k++ )
        {
            if( out_corners[k].x <= BORDER || out_corners[k].x > img->cols - BORDER ||
                out_corners[k].y <= BORDER || out_corners[k].y > img->rows - BORDER )
                break;
        }

        found = k == pattern_size.width*pattern_size.height;
    }

    if( found && pattern_size.height % 2 == 0 && pattern_size.width % 2 == 0 )
    {
        int last_row = (pattern_size.height-1)*pattern_size.width;
        double dy0 = out_corners[last_row].y - out_corners[0].y;
        if( dy0 < 0 )
        {
            int n = pattern_size.width*pattern_size.height;
            for(int i = 0; i < n/2; i++ )
            {
                CvPoint2D32f temp;
                CV_SWAP(out_corners[i], out_corners[n-i-1], temp);
            }
        }
    }

    if( found )
    {
        cv::Ptr<CvMat> gray;
        if( CV_MAT_CN(img->type) != 1 )
        {
            gray.reset(cvCreateMat(img->rows, img->cols, CV_8UC1));
            cvCvtColor(img, gray, CV_BGR2GRAY);
        }
        else
        {
            gray.reset(cvCloneMat(img));
        }
        int wsize = 2;
        cvFindCornerSubPix( gray, out_corners, pattern_size.width*pattern_size.height,
            cvSize(wsize, wsize), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 15, 0.1));
    }
    }
    catch(...)
    {
        cvFree(&quads);
        cvFree(&corners);
        cvFree(&quad_group);
        cvFree(&corner_group);
        throw;
    }

    cvFree(&quads);
    cvFree(&corners);
    cvFree(&quad_group);
    cvFree(&corner_group);
    return found;
}