diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb50090 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +cToMatlab +outputImages +*~ +*.jpg diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2dd9004 --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ + +INCS = +LIBS = + + +DEFINCS = -I/usr/include -I/usr/include/opencv2 +DEFLIBS = -L/usr/lib +LINKLIBS = -lopencv_core -lopencv_highgui -lopencv_imgproc + +CPP = g++ +CC = gcc +OBJ = main.o cvcalibinit3.o +LINKOBJ = main.o cvcalibinit3.o +BIN = FindCorners.exe +RM = rm -f + + +all: $(BIN) + +clean: + ${RM} $(OBJ) $(BIN) + +$(BIN): $(OBJ) + $(CPP) $(LINKOBJ) -g -o $(BIN) $(LIBS) $(DEFLIBS) $(LINKLIBS) + +main.o: main.cpp + $(CPP) -c -g main.cpp -o main.o $(INCS) $(DEFINCS) + +cvcalibinit3.o: cvcalibinit3.cpp + $(CPP) -c -g cvcalibinit3.cpp -o cvcalibinit3.o $(INCS) $(DEFINCS) diff --git a/README_FOR_LINUX.TXT b/README_FOR_LINUX.TXT new file mode 100644 index 0000000..75b76a8 --- /dev/null +++ b/README_FOR_LINUX.TXT @@ -0,0 +1,28 @@ +How to use this code: + +<1> make sure you have opencv library installed. If you have it, please go to step <2>; if not, follow the tips below: + - sudo apt-get install libopencv-dev + - All the header files will be installed in /usr/include/opencv by default + - All the libraries will be installed in /usr/lib by default + - go to step <3> +<2> if you have opencv already installed from source code, please modify the Makefile as: + - INCS= your correct opencv include path, e.g. /usr/local/include/opencv (which is the default path of opencv library) + - LIBS= your correct opencv lib path, e.g. /usr/local/lib +<3> make clean; +<4> make; +<5> copy the generated FindCorners.exe into the "autoCornerFinder" folder in your toolbox path, replacing the existing one. +<6> test it by the "Extract grid corners" button in the toolbox, after "Load images". + +NOTE THAT THE CODE WORKS ONLY WITH JPEG IMAGES. +Note, if you want to display all the processing steps of the corner extraction, just change the flag VIS in cvcalibinit3.cpp to 1. +Please observe that the exetutable is called withing the "click_ima_calib_rufli.m". in the toolbox path. + +If you use this code, please cite the following articles: + +1. Scaramuzza, D., Martinelli, A. and Siegwart, R. (2006), A Toolbox for Easily Calibrating Omnidirectional Cameras, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2006), Beijing, China, October 2006. + +2. Scaramuzza, D., Martinelli, A. and Siegwart, R., (2006). "A Flexible Technique for Accurate Omnidirectional Camera Calibration and Structure from Motion", Proceedings of IEEE International Conference of Vision Systems (ICVS'06), New York, January 5-7, 2006. + +3. Rufli, M., Scaramuzza, D., and Siegwart, R. (2008), Automatic Detection of Checkerboards on Blurred and Distorted Images, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2008), Nice, France, September 2008. + + diff --git a/cvcalibinit3.cpp b/cvcalibinit3.cpp new file mode 100644 index 0000000..9ffc933 --- /dev/null +++ b/cvcalibinit3.cpp @@ -0,0 +1,2461 @@ +/************************************************************************************\ + This is improved variant of chessboard corner detection algorithm that + uses a graph of connected quads. It is based on the code contributed + by Vladimir Vezhnevets and Philip Gruebele. + Here is the copyright notice from the original Vladimir's code: + =============================================================== + + The algorithms developed and implemented by Vezhnevets Vldimir + aka Dead Moroz (vvp@graphics.cs.msu.ru) + See http://graphics.cs.msu.su/en/research/calibration/opencv.html + for detailed information. + + Reliability additions and modifications made by Philip Gruebele. + pgruebele@cox.net + + His code was adapted for use with low resolution and omnidirectional cameras + by Martin Rufli during his Master Thesis under supervision of Davide Scaramuzza, at the ETH Zurich. Further enhancements include: + - Increased chance of correct corner matching. + - Corner matching over all dilation runs. + +If you use this code, please cite the following articles: + +1. Scaramuzza, D., Martinelli, A. and Siegwart, R. (2006), A Toolbox for Easily Calibrating Omnidirectional Cameras, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2006), Beijing, China, October 2006. +2. Scaramuzza, D., Martinelli, A. and Siegwart, R., (2006). "A Flexible Technique for Accurate Omnidirectional Camera Calibration and Structure from Motion", Proceedings of IEEE International Conference of Vision Systems (ICVS'06), New York, January 5-7, 2006. +3. Rufli, M., Scaramuzza, D., and Siegwart, R. (2008), Automatic Detection of Checkerboards on Blurred and Distorted Images, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2008), Nice, France, September 2008. + +\************************************************************************************/ + +//=========================================================================== +// CODE STARTS HERE +//=========================================================================== +// Include files +#include +#include + +#include +#include +using namespace std; +using std::ifstream; + + +// Defines +#define MAX_CONTOUR_APPROX 7 + + +//Ming #define VIS 1 +#define VIS 0 +// Turn on visualization +#define TIMER 0 // Elapse the function duration times + + +// Definition Contour Struct +typedef struct CvContourEx +{ + CV_CONTOUR_FIELDS() + int counter; +} +CvContourEx; + + +// Definition Corner Struct +typedef struct CvCBCorner +{ + CvPoint2D32f pt; // X and y coordinates + int row; // Row and column of the corner + int column; // in the found pattern + bool needsNeighbor; // Does the corner require a neighbor? + int count; // number of corner neighbors + struct CvCBCorner* neighbors[4]; // pointer to all corner neighbors +} +CvCBCorner; + + +// Definition Quadrangle Struct +// This structure stores information about the chessboard quadrange +typedef struct CvCBQuad +{ + int count; // Number of quad neihbors + int group_idx; // Quad group ID + float edge_len; // Smallest side length^2 + CvCBCorner *corners[4]; // Coordinates of quad corners + struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors + bool labeled; // Has this corner been labeled? +} +CvCBQuad; + + + +//=========================================================================== +// FUNCTION PROTOTYPES +//=========================================================================== +static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners, + CvMemStorage *storage, CvMat *image, int flags, int dilation, + bool firstRun ); + +static void mrFindQuadNeighbors2( CvCBQuad *quads, int quad_count, int dilation); + +static int mrAugmentBestRun( CvCBQuad *new_quads, int new_quad_count, int new_dilation, + CvCBQuad **old_quads, int old_quad_count, int old_dilation ); + +static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count, CvCBQuad **quad_group, + int group_idx, + CvMemStorage* storage, int dilation ); + +static void mrLabelQuadGroup( CvCBQuad **quad_group, int count, CvSize pattern_size, + bool firstRun ); + +static void mrCopyQuadGroup( CvCBQuad **temp_quad_group, CvCBQuad **out_quad_group, + int count ); + +static int icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quads, + CvSize pattern_size ); + +static int mrWriteCorners( CvCBQuad **output_quads, int count, CvSize pattern_size, + int min_number_of_corners ); + + + +//=========================================================================== +// MAIN FUNCTION +//=========================================================================== +int cvFindChessboardCorners3( const void* arr, CvSize pattern_size, + CvPoint2D32f* out_corners, int* out_corner_count, + int min_number_of_corners ) +{ +//START TIMER +#if TIMER + ofstream FindChessboardCorners2; + time_t start_time = clock(); +#endif + + // PART 0: INITIALIZATION + //----------------------------------------------------------------------- + // Initialize variables + int flags = 1; // not part of the function call anymore! + int max_count = 0; + int max_dilation_run_ID = -1; + const int min_dilations = 1; + const int max_dilations = 6; + int found = 0; + CvMat* norm_img = 0; + CvMat* thresh_img = 0; + CvMat* thresh_img_save = 0; + CvMemStorage* storage = 0; + + CvCBQuad *quads = 0; + CvCBQuad **quad_group = 0; + CvCBCorner *corners = 0; + CvCBCorner **corner_group = 0; + CvCBQuad **output_quad_group = 0; + + // debug trial. Martin Rufli, 28. Ocober, 2008 + int block_size = 0; + + + // Create error message file + ofstream error("cToMatlab/error.txt"); + + + // Set openCV function name and label the function start + CV_FUNCNAME( "cvFindChessBoardCornerGuesses2" ); + __BEGIN__; + + + // Further initializations + int quad_count, group_idx, dilations; + CvMat stub, *img = (CvMat*)arr; + + + // Read image from input + CV_CALL( img = cvGetMat( img, &stub )); + + + // Error handling, write error message to error.txt + if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 ) + { + error << "Only 8-bit grayscale or color images are supported" << endl; + error.close(); + return -1; + } + if( pattern_size.width < 2 || pattern_size.height < 2 ) + { + error << "Pattern should have at least 2x2 size" << endl; + error.close(); + return -1; + } + if( pattern_size.width > 127 || pattern_size.height > 127 ) + { + error << "Pattern should not have a size larger than 127 x 127" << endl; + error.close(); + return -1; + } + /* + if( pattern_size.width != pattern_size.height ) + { + error << "In this implementation only square sized checker boards are supported" << endl; + error.close(); + return -1; + } + */ + if( !out_corners ) + { + error << "Null pointer to corners encountered" << endl; + error.close(); + return -1; + } + + + // Create memory storage + CV_CALL( storage = cvCreateMemStorage(0) ); + CV_CALL( thresh_img = cvCreateMat( img->rows, img->cols, CV_8UC1 )); + CV_CALL( thresh_img_save = cvCreateMat( img->rows, img->cols, CV_8UC1 )); + + + // Image histogramm normalization and + // BGR to Grayscale image conversion (if applicable) + // MARTIN: Set to "false" + if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) ) + { + CV_CALL( norm_img = cvCreateMat( img->rows, img->cols, CV_8UC1 )); + + if( CV_MAT_CN(img->type) != 1 ) + { + CV_CALL( cvCvtColor( img, norm_img, CV_BGR2GRAY )); + img = norm_img; + } + + if(false) + { + cvEqualizeHist( img, norm_img ); + img = norm_img; + } + } + +// EVALUATE TIMER +#if TIMER + float time0_1 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2.open("timer/FindChessboardCorners2.txt", ofstream::app); + FindChessboardCorners2 << "Time 0.1 for cvFindChessboardCorners2 was " << time0_1 << " seconds." << endl; +#endif + + // For image binarization (thresholding) + // we use an adaptive threshold with a gaussian mask + // ATTENTION: Gaussian thresholding takes MUCH more time than Mean thresholding! + block_size = cvRound(MIN(img->cols,img->rows)*0.2)|1; + cvAdaptiveThreshold( img, thresh_img, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, block_size, 0 ); + cvCopy( thresh_img, thresh_img_save); + + + // PART 1: FIND LARGEST PATTERN + //----------------------------------------------------------------------- + // Checker patterns are tried to be found by dilating the background and + // then applying a canny edge finder on the closed contours (checkers). + // Try one dilation run, but if the pattern is not found, repeat until + // max_dilations is reached. + for( dilations = min_dilations; dilations <= max_dilations; dilations++ ) + { + // Calling "cvCopy" again is much faster than rerunning "cvAdaptiveThreshold" + cvCopy( thresh_img_save, thresh_img); + +// EVALUATE TIMER +#if TIMER + float time0_2 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2 << "Time 0.2 for cvFindChessboardCorners2 was " << time0_2 << " seconds." << endl; +#endif + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + cvNamedWindow( "Original Image", 1 ); + cvShowImage( "Original Image", img); + //cvSaveImage("pictureVis/OrigImg.png", img); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // MARTIN's Code + // Use both a rectangular and a cross kernel. In this way, a more + // homogeneous dilation is performed, which is crucial for small, + // distorted checkers. Use the CROSS kernel first, since its action + // on the image is more subtle + IplConvKernel *kernel1 = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL); + IplConvKernel *kernel2 = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_RECT,NULL); + + if (dilations >= 1) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 2) + cvDilate( thresh_img, thresh_img, kernel2, 1); + if (dilations >= 3) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 4) + cvDilate( thresh_img, thresh_img, kernel2, 1); + if (dilations >= 5) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 6) + cvDilate( thresh_img, thresh_img, kernel2, 1); + +// EVALUATE TIMER +#if TIMER + float time0_3 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2 << "Time 0.3 for cvFindChessboardCorners2 was " << time0_3 << " seconds." << endl; +#endif + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + cvNamedWindow( "After adaptive Threshold (and Dilation)", 1 ); + cvShowImage( "After adaptive Threshold (and Dilation)", thresh_img); + //cvSaveImage("pictureVis/afterDilation.png", thresh_img); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // In order to 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); + + + // Generate quadrangles in the following function + // "quad_count" is the number of cound quadrangles + CV_CALL( quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, dilations, true )); + if( quad_count <= 0 ) + continue; + +// EVALUATE TIMER +#if TIMER + float time0_4 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2 << "Time 0.4 for cvFindChessboardCorners2 was " << time0_4 << " seconds." << endl; +#endif + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + cvNamedWindow( "all found quads per dilation run", 1 ); + IplImage* imageCopy2 = cvCreateImage( cvGetSize(thresh_img), 8, 1 ); + IplImage* imageCopy22 = cvCreateImage( cvGetSize(thresh_img), 8, 3 ); + cvCopy( thresh_img, imageCopy2); + cvCvtColor( imageCopy2, imageCopy22, CV_GRAY2BGR ); + + for( int kkk = 0; kkk < quad_count; kkk++ ) + { + CvCBQuad* print_quad = &quads[kkk]; + CvPoint pt[4]; + pt[0].x = (int)print_quad->corners[0]->pt.x; + pt[0].y = (int)print_quad->corners[0]->pt.y; + pt[1].x = (int)print_quad->corners[1]->pt.x; + pt[1].y = (int)print_quad->corners[1]->pt.y; + pt[2].x = (int)print_quad->corners[2]->pt.x; + pt[2].y = (int)print_quad->corners[2]->pt.y; + pt[3].x = (int)print_quad->corners[3]->pt.x; + pt[3].y = (int)print_quad->corners[3]->pt.y; + cvLine( imageCopy22, pt[0], pt[1], CV_RGB(255,255,0), 1, 8 ); + cvLine( imageCopy22, pt[1], pt[2], CV_RGB(255,255,0), 1, 8 ); + cvLine( imageCopy22, pt[2], pt[3], CV_RGB(255,255,0), 1, 8 ); + cvLine( imageCopy22, pt[3], pt[0], CV_RGB(255,255,0), 1, 8 ); + } + cvShowImage( "all found quads per dilation run", imageCopy22); + //cvSaveImage("pictureVis/allFoundQuads.png", imageCopy22); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // The following function finds and assigns neighbor quads to every + // quadrangle in the immediate vicinity fulfilling certain + // prerequisites + CV_CALL( mrFindQuadNeighbors2( quads, quad_count, dilations)); + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + cvNamedWindow( "quads with neighbors", 1 ); + IplImage* imageCopy3 = cvCreateImage( cvGetSize(thresh_img), 8, 3 ); + cvCopy( imageCopy22, imageCopy3); + CvPoint pt; + int scale = 0; + int line_type = CV_AA; + CvScalar color = {{0,0,255}}; + for( int kkk = 0; kkk < quad_count; kkk++ ) + { + CvCBQuad* print_quad2 = &quads[kkk]; + for( int kkkk = 0; kkkk < 4; kkkk++ ) + { + if( print_quad2->neighbors[kkkk] ) + { + pt.x = (int)(print_quad2->corners[kkkk]->pt.x); + pt.y = (int)(print_quad2->corners[kkkk]->pt.y); + cvCircle( imageCopy3, pt, 3, color, 1, line_type, scale); + } + } + } + cvShowImage( "quads with neighbors", imageCopy3); + //cvSaveImage("pictureVis/allFoundNeighbors.png", imageCopy3); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // Allocate memory + CV_CALL( quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * quad_count)); + CV_CALL( corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * quad_count*4 )); + + + // The connected quads will be organized in groups. The following loop + // increases a "group_idx" identifier. + // The function "icvFindConnectedQuads assigns all connected quads + // a unique group ID. + // If more quadrangles were assigned to a given group (i.e. connected) + // than are expected by the input variable "pattern_size", the + // function "icvCleanFoundConnectedQuads" erases the surplus + // quadrangles by minimizing the convex hull of the remaining pattern. + for( group_idx = 0; ; group_idx++ ) + { + int count; + CV_CALL( count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage, dilations )); + + if( count == 0 ) + break; + + CV_CALL( count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size )); + + + // MARTIN's Code + // To save computational time, only proceed, if the number of + // found quads during this dilation run is larger than the + // largest previous found number + if( count >= max_count) + { + // set max_count to its new value + max_count = count; + max_dilation_run_ID = dilations; + + // The following function labels all corners of every quad + // with a row and column entry. + // "count" specifies the number of found quads in "quad_group" + // with group identifier "group_idx" + // The last parameter is set to "true", because this is the + // first function call and some initializations need to be + // made. + mrLabelQuadGroup( quad_group, max_count, pattern_size, true ); + + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + // display all corners in INCREASING ROW AND COLUMN ORDER + cvNamedWindow( "Corners in increasing order", 1 ); + IplImage* imageCopy11 = cvCreateImage( cvGetSize(thresh_img), 8, 3 ); + cvCopy( imageCopy22, imageCopy11); + // Assume min and max rows here, since we are outside of the + // relevant function + int min_row = -15; + int max_row = 15; + int min_column = -15; + int max_column = 15; + for(int i = min_row; i <= max_row; i++) + { + for(int j = min_column; j <= max_column; j++) + { + for(int k = 0; k < count; k++) + { + for(int l = 0; l < 4; l++) + { + if( ((quad_group[k])->corners[l]->row == i) && ((quad_group[k])->corners[l]->column == j) ) + { + // draw the row and column numbers + char str[255]; + sprintf(str,"%i/%i",i,j); + CvFont font; + cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.2, 0.2, 0, 1); + CvPoint ptt; + ptt.x = (int) quad_group[k]->corners[l]->pt.x; + ptt.y = (int) quad_group[k]->corners[l]->pt.y; + // Mark central corners with a different color than + // border corners + if ((quad_group[k])->corners[l]->needsNeighbor == false) + { + cvPutText(imageCopy11, str, ptt, &font, CV_RGB(0,255,0)); + } + else + { + cvPutText(imageCopy11, str, ptt, &font, CV_RGB(255,0,0)); + } + cvShowImage( "Corners in increasing order", imageCopy11); + //cvSaveImage("pictureVis/CornersIncreasingOrder.tif", imageCopy11); + //cvWaitKey(0); + } + } + } + } + } + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // Allocate memory + CV_CALL( output_quad_group = (CvCBQuad**)cvAlloc( sizeof(output_quad_group[0]) * ((pattern_size.height+2) * (pattern_size.width+2)) )); + + + // The following function copies every member of "quad_group" + // to "output_quad_group", because "quad_group" will be + // overwritten during the next loop pass. + // "output_quad_group" is a true copy of "quad_group" and + // later used for output + mrCopyQuadGroup( quad_group, output_quad_group, max_count ); + } + } + + + // Free the allocated variables + cvFree( &quads ); + cvFree( &corners ); + } + + +// EVALUATE TIMER +#if TIMER + float time1 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2.open("timer/FindChessboardCorners2.txt", ofstream::app); + FindChessboardCorners2 << "Time 1 for cvFindChessboardCorners2 was " << time1 << " seconds." << endl; +#endif + + // If enough corners have been found already, then there is no need for PART 2 ->EXIT + found = mrWriteCorners( output_quad_group, max_count, pattern_size, min_number_of_corners); + if (found == -1 || found == 1) + EXIT; + + // PART 2: AUGMENT LARGEST PATTERN + //----------------------------------------------------------------------- + // Instead of saving all found quads of all dilation runs from PART 1, we + // just recompute them again, but skipping the dilation run which + // produced the maximum number of found quadrangles. + // In essence the first section of PART 2 is identical to the first + // section of PART 1. + for( dilations = max_dilations; dilations >= min_dilations; dilations-- ) + { + //if(max_dilation_run_ID == dilations) + // continue; + + // Calling "cvCopy" again is much faster than rerunning "cvAdaptiveThreshold" + cvCopy( thresh_img_save, thresh_img); + + IplConvKernel *kernel1 = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_CROSS,NULL); + IplConvKernel *kernel2 = cvCreateStructuringElementEx(3,3,1,1,CV_SHAPE_RECT,NULL); + + if (dilations >= 1) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 2) + cvDilate( thresh_img, thresh_img, kernel2, 1); + if (dilations >= 3) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 4) + cvDilate( thresh_img, thresh_img, kernel2, 1); + if (dilations >= 5) + cvDilate( thresh_img, thresh_img, kernel1, 1); + if (dilations >= 6) + cvDilate( thresh_img, thresh_img, kernel2, 1); + + cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1, + thresh_img->rows-1), CV_RGB(255,255,255), 3, 8); + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + cvNamedWindow( "PART2: Starting Point", 1 ); + IplImage* imageCopy23 = cvCreateImage( cvGetSize(thresh_img), 8, 3 ); + cvCvtColor( thresh_img, imageCopy23, CV_GRAY2BGR ); + + CvPoint *pt = new CvPoint[4]; + for( int kkk = 0; kkk < max_count; kkk++ ) + { + CvCBQuad* print_quad2 = output_quad_group[kkk]; + for( int kkkk = 0; kkkk < 4; kkkk++ ) + { + pt[kkkk].x = (int) print_quad2->corners[kkkk]->pt.x; + pt[kkkk].y = (int) print_quad2->corners[kkkk]->pt.y; + } + // draw a filled polygon + cvFillConvexPoly ( imageCopy23, pt, 4, CV_RGB(255*0.1,255*0.25,255*0.6)); + } + // indicate the dilation run + char str[255]; + sprintf(str,"Dilation Run No.: %i",dilations); + CvFont font; + cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 2); + //cvPutText(imageCopy23, str, cvPoint(20,20), &font, CV_RGB(0,255,0)); + + cvShowImage( "PART2: Starting Point", imageCopy23); + cvSaveImage("pictureVis/part2Start.png", imageCopy23); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + CV_CALL( quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags, dilations, false )); + if( quad_count <= 0 ) + continue; + + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + //draw on top of previous image + for( int kkk = 0; kkk < quad_count; kkk++ ) + { + CvCBQuad* print_quad = &quads[kkk]; + + CvPoint pt[4]; + pt[0].x = (int)print_quad->corners[0]->pt.x; + pt[0].y = (int)print_quad->corners[0]->pt.y; + pt[1].x = (int)print_quad->corners[1]->pt.x; + pt[1].y = (int)print_quad->corners[1]->pt.y; + pt[2].x = (int)print_quad->corners[2]->pt.x; + pt[2].y = (int)print_quad->corners[2]->pt.y; + pt[3].x = (int)print_quad->corners[3]->pt.x; + pt[3].y = (int)print_quad->corners[3]->pt.y; + cvLine( imageCopy23, pt[0], pt[1], CV_RGB(255,0,0), 1, 8 ); + cvLine( imageCopy23, pt[1], pt[2], CV_RGB(255,0,0), 1, 8 ); + cvLine( imageCopy23, pt[2], pt[3], CV_RGB(255,0,0), 1, 8 ); + cvLine( imageCopy23, pt[3], pt[0], CV_RGB(255,0,0), 1, 8 ); + //compute center of print_quad + int x1 = (pt[0].x + pt[1].x)/2; + int y1 = (pt[0].y + pt[1].y)/2; + int x2 = (pt[2].x + pt[3].x)/2; + int y2 = (pt[2].y + pt[3].y)/2; + + int x3 = (x1 + x2)/2; + int y3 = (y1 + y2)/2; + // indicate the quad number in the image + char str[255]; + sprintf(str,"%i",kkk); + //CvFont font; + //cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1); + //cvPutText(imageCopy23, str, cvPoint(x3,y3), &font, CV_RGB(0,255,255)); + } + + for( int kkk = 0; kkk < max_count; kkk++ ) + { + CvCBQuad* print_quad = output_quad_group[kkk]; + + CvPoint pt[4]; + pt[0].x = (int)print_quad->corners[0]->pt.x; + pt[0].y = (int)print_quad->corners[0]->pt.y; + pt[1].x = (int)print_quad->corners[1]->pt.x; + pt[1].y = (int)print_quad->corners[1]->pt.y; + pt[2].x = (int)print_quad->corners[2]->pt.x; + pt[2].y = (int)print_quad->corners[2]->pt.y; + pt[3].x = (int)print_quad->corners[3]->pt.x; + pt[3].y = (int)print_quad->corners[3]->pt.y; + //compute center of print_quad + int x1 = (pt[0].x + pt[1].x)/2; + int y1 = (pt[0].y + pt[1].y)/2; + int x2 = (pt[2].x + pt[3].x)/2; + int y2 = (pt[2].y + pt[3].y)/2; + + int x3 = (x1 + x2)/2; + int y3 = (y1 + y2)/2; + + // indicate the quad number in the image + char str[255]; + sprintf(str,"%i",kkk); + //CvFont font; + //cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1); + //cvPutText(imageCopy23, str, cvPoint(x3,y3), &font, CV_RGB(0,0,0)); + } + + cvShowImage( "PART2: Starting Point", imageCopy23); + cvSaveImage("pictureVis/part2StartAndNewQuads.png", imageCopy23); + cvWaitKey(0); +#endif +//END------------------------------------------------------------------------ + + + // MARTIN's Code + // The following loop is executed until no more newly found quads + // can be matched to one of the border corners of the largest found + // pattern from PART 1. + // The function "mrAugmentBestRun" tests whether a quad can be linked + // to the existng pattern. + // The function "mrLabelQuadGroup" then labels the newly added corners + // with the respective row and column entries. + int feedBack = -1; + while ( feedBack == -1) + { + feedBack = mrAugmentBestRun( quads, quad_count, dilations, + output_quad_group, max_count, max_dilation_run_ID ); + + +//VISUALIZATION-------------------------------------------------------------- +#if VIS + if( feedBack == -1) + { + CvCBQuad* remember_quad; + for( int kkk = max_count; kkk < max_count+1; kkk++ ) + { + CvCBQuad* print_quad = output_quad_group[kkk]; + remember_quad = print_quad; + CvPoint pt[4]; + pt[0].x = (int)print_quad->corners[0]->pt.x; + pt[0].y = (int)print_quad->corners[0]->pt.y; + pt[1].x = (int)print_quad->corners[1]->pt.x; + pt[1].y = (int)print_quad->corners[1]->pt.y; + pt[2].x = (int)print_quad->corners[2]->pt.x; + pt[2].y = (int)print_quad->corners[2]->pt.y; + pt[3].x = (int)print_quad->corners[3]->pt.x; + pt[3].y = (int)print_quad->corners[3]->pt.y; + cvLine( imageCopy23, pt[0], pt[1], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[1], pt[2], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[2], pt[3], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[3], pt[0], CV_RGB(255,0,0), 2, 8 ); + } + + cvWaitKey(0); + // also draw the corner to which it is connected + // Remember it is not yet completely linked!!! + for( int kkk = 0; kkk < max_count; kkk++ ) + { + CvCBQuad* print_quad = output_quad_group[kkk]; + + for( int kkkk = 0; kkkk < 4; kkkk++) + { + if(print_quad->neighbors[kkkk] == remember_quad) + { + CvPoint pt[4]; + pt[0].x = (int)print_quad->corners[0]->pt.x; + pt[0].y = (int)print_quad->corners[0]->pt.y; + pt[1].x = (int)print_quad->corners[1]->pt.x; + pt[1].y = (int)print_quad->corners[1]->pt.y; + pt[2].x = (int)print_quad->corners[2]->pt.x; + pt[2].y = (int)print_quad->corners[2]->pt.y; + pt[3].x = (int)print_quad->corners[3]->pt.x; + pt[3].y = (int)print_quad->corners[3]->pt.y; + cvLine( imageCopy23, pt[0], pt[1], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[1], pt[2], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[2], pt[3], CV_RGB(255,0,0), 2, 8 ); + cvLine( imageCopy23, pt[3], pt[0], CV_RGB(255,0,0), 2, 8 ); + } + } + } + cvShowImage( "PART2: Starting Point", imageCopy23); + cvSaveImage("pictureVis/part2StartAndSelectedQuad.png", imageCopy23); + cvWaitKey(0); + } +#endif +//END------------------------------------------------------------------------ + + + // if we have found a new matching quad + if (feedBack == -1) + { + // increase max_count by one + max_count = max_count + 1; + mrLabelQuadGroup( output_quad_group, max_count, pattern_size, false ); + + + // write the found corners to output array + // Go to __END__, if enough corners have been found + found = mrWriteCorners( output_quad_group, max_count, pattern_size, min_number_of_corners); + if (found == -1 || found == 1) + EXIT; + } + } + } + + + // "End of file" jump point + // After the command "EXIT" the code jumps here + __END__; + + + /* + // MARTIN: + found = mrWriteCorners( output_quad_group, max_count, pattern_size, min_number_of_corners); + */ + + // If a linking problem was encountered, throw an error message + if( found == -1 ) + { + error << "While linking the corners a problem was encountered. No corner sequence is returned. " << endl; + error.close(); + return -1; + } + + + // Release allocated memory + cvReleaseMemStorage( &storage ); + cvReleaseMat( &norm_img ); + cvReleaseMat( &thresh_img ); + cvFree( &quads ); + cvFree( &corners ); + cvFree( &quad_group ); + cvFree( &corner_group ); + cvFree( &output_quad_group ); + error.close(); + +// EVALUATE TIMER +#if TIMER + float time3 = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindChessboardCorners2 << "Time 3 for cvFindChessboardCorners2 was " << time3 << " seconds." << endl; + FindChessboardCorners2.close(); +#endif + + // Return found + // Found can have the values + // -1 -> Error or corner linking problem, see error.txt for more information + // 0 -> Not enough corners were found + // 1 -> Enough corners were found + return found; +} + + +//=========================================================================== +// ERASE OVERHEAD +//=========================================================================== +// If we found too many connected quads, remove those which probably do not +// belong. +static int +icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize pattern_size ) +{ + CvMemStorage *temp_storage = 0; + CvPoint2D32f *centers = 0; + + CV_FUNCNAME( "icvCleanFoundConnectedQuads" ); + + __BEGIN__; + + CvPoint2D32f center = {0,0}; + int i, j, k; + + + // Number of quads this pattern should contain + int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2; + + + // If we have more quadrangles than we should, try to eliminate duplicates + // or ones which don't belong to the pattern rectangle. Else go to the end + // of the function + if( quad_count <= count ) + EXIT; + + + // Create an array of quadrangle centers + CV_CALL( centers = (CvPoint2D32f *)cvAlloc( sizeof(centers[0])*quad_count )); + CV_CALL( temp_storage = cvCreateMemStorage(0)); + + for( i = 0; i < quad_count; i++ ) + { + CvPoint2D32f ci = {0,0}; + CvCBQuad* q = quad_group[i]; + + for( j = 0; j < 4; j++ ) + { + CvPoint2D32f pt = q->corners[j]->pt; + ci.x += pt.x; + ci.y += pt.y; + } + + ci.x *= 0.25f; + ci.y *= 0.25f; + + + // Centers(i), is the geometric center of quad(i) + // Center, is the center of all found quads + centers[i] = ci; + center.x += ci.x; + center.y += ci.y; + } + center.x /= quad_count; + center.y /= quad_count; + + // If we have more quadrangles than we should, we try to eliminate bad + // ones based on minimizing the bounding box. We iteratively remove the + // point which reduces the size of the bounding box of the blobs the most + // (since we want the rectangle to be as small as possible) remove the + // quadrange that causes the biggest reduction in pattern size until we + // have the correct number + for( ; quad_count > count; quad_count-- ) + { + double min_box_area = DBL_MAX; + int skip, min_box_area_index = -1; + CvCBQuad *q0, *q; + + + // For each point, calculate box area without that point + for( skip = 0; skip < quad_count; skip++ ) + { + // get bounding rectangle + CvPoint2D32f temp = centers[skip]; + centers[skip] = center; + CvMat pointMat = cvMat(1, quad_count, CV_32FC2, centers); + CvSeq *hull = cvConvexHull2( &pointMat, temp_storage, CV_CLOCKWISE, 1 ); + centers[skip] = temp; + double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ)); + + + // remember smallest box area + if( hull_area < min_box_area ) + { + min_box_area = hull_area; + min_box_area_index = skip; + } + cvClearMemStorage( temp_storage ); + } + + q0 = quad_group[min_box_area_index]; + + + // remove any references to this quad as a neighbor + for( i = 0; i < quad_count; i++ ) + { + q = quad_group[i]; + for( j = 0; j < 4; j++ ) + { + if( q->neighbors[j] == q0 ) + { + q->neighbors[j] = 0; + q->count--; + for( k = 0; k < 4; k++ ) + if( q0->neighbors[k] == q ) + { + q0->neighbors[k] = 0; + q0->count--; + break; + } + break; + } + } + } + + // remove the quad by copying th last quad in the list into its place + quad_count--; + quad_group[min_box_area_index] = quad_group[quad_count]; + centers[min_box_area_index] = centers[quad_count]; + } + + __END__; + + cvReleaseMemStorage( &temp_storage ); + cvFree( ¢ers ); + + return quad_count; +} + + + +//=========================================================================== +// FIND COONECTED QUADS +//=========================================================================== +static int +icvFindConnectedQuads( CvCBQuad *quad, int quad_count, CvCBQuad **out_group, + int group_idx, CvMemStorage* storage, int dilation ) +{ +//START TIMER +#if TIMER + ofstream FindConnectedQuads; + time_t start_time = clock(); +#endif + + // initializations + CvMemStorage* temp_storage = cvCreateChildMemStorage( storage ); + CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage ); + int i, count = 0; + + + // Scan the array for a first unlabeled quad + for( i = 0; i < quad_count; i++ ) + { + if( quad[i].count > 0 && quad[i].group_idx < 0) + break; + } + + + // Recursively find a group of connected quads starting from the seed + // quad[i] + if( i < quad_count ) + { + CvCBQuad* q = &quad[i]; + cvSeqPush( stack, &q ); + out_group[count++] = q; + q->group_idx = group_idx; + + while( stack->total ) + { + cvSeqPop( stack, &q ); + for( i = 0; i < 4; i++ ) + { + CvCBQuad *neighbor = q->neighbors[i]; + + + // If he neighbor exists and the neighbor has more than 0 + // neighbors and the neighbor has not been classified yet. + if( neighbor && neighbor->count > 0 && neighbor->group_idx < 0 ) + { + cvSeqPush( stack, &neighbor ); + out_group[count++] = neighbor; + neighbor->group_idx = group_idx; + } + } + } + } + + cvReleaseMemStorage( &temp_storage ); + +// EVALUATE TIMER +#if TIMER + float time = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindConnectedQuads.open("timer/FindConnectedQuads.txt", ofstream::app); + FindConnectedQuads << "Time for cvFindConnectedQuads was " << time << " seconds." << endl; + FindConnectedQuads.close(); +#endif + + return count; +} + + + +//=========================================================================== +// LABEL CORNER WITH ROW AND COLUMN //DONE +//=========================================================================== +static void mrLabelQuadGroup( CvCBQuad **quad_group, int count, CvSize pattern_size, bool firstRun ) +{ +//START TIMER +#if TIMER + ofstream LabelQuadGroup; + time_t start_time = clock(); +#endif + + // If this is the first function call, a seed quad needs to be selected + if (firstRun == true) + { + // Search for the (first) quad with the maximum number of neighbors + // (usually 4). This will be our starting point. + int max_id = -1; + int max_number = -1; + for(int i = 0; i < count; i++ ) + { + CvCBQuad* q = quad_group[i]; + if( q->count > max_number) + { + max_number = q->count; + max_id = i; + + if (max_number == 4) + break; + } + } + + + // Mark the starting quad's (per definition) upper left corner with + //(0,0) and then proceed clockwise + // The following labeling sequence enshures a "right coordinate system" + (quad_group[max_id])->labeled = true; + + (quad_group[max_id])->corners[0]->row = 0; + (quad_group[max_id])->corners[0]->column = 0; + (quad_group[max_id])->corners[1]->row = 0; + (quad_group[max_id])->corners[1]->column = 1; + (quad_group[max_id])->corners[2]->row = 1; + (quad_group[max_id])->corners[2]->column = 1; + (quad_group[max_id])->corners[3]->row = 1; + (quad_group[max_id])->corners[3]->column = 0; + } + + + // Mark all other corners with their respective row and column + bool flag_changed = true; + while( flag_changed == true ) + { + // First reset the flag to "false" + flag_changed = false; + + + // Go through all quads top down is faster, since unlabeled quads will + // be inserted at the end of the list + for( int i = (count-1); i >= 0; i-- ) + { + // Check whether quad "i" has been labeled already + if ( (quad_group[i])->labeled == false ) + { + // Check its neighbors, whether some of them have been labeled + // already + for( int j = 0; j < 4; j++ ) + { + // Check whether the neighbor exists (i.e. is not the NULL + // pointer) + if( (quad_group[i])->neighbors[j] ) + { + CvCBQuad *quadNeighborJ = (quad_group[i])->neighbors[j]; + + + // Only proceed, if neighbor "j" was labeled + if( quadNeighborJ->labeled == true) + { + // For every quad it could happen to pass here + // multiple times. We therefore "break" later. + // Check whitch of the neighbors corners is + // connected to the current quad + int connectedNeighborCornerId = -1; + for( int k = 0; k < 4; k++) + { + if( quadNeighborJ->neighbors[k] == quad_group[i] ) + { + connectedNeighborCornerId = k; + + + // there is only one, therefore + break; + } + } + + + // For the following calculations we need the row + // and column of the connected neighbor corner and + // all other corners of the connected quad "j", + // clockwise (CW) + CvCBCorner *conCorner = quadNeighborJ->corners[connectedNeighborCornerId]; + CvCBCorner *conCornerCW1 = quadNeighborJ->corners[(connectedNeighborCornerId+1)%4]; + CvCBCorner *conCornerCW2 = quadNeighborJ->corners[(connectedNeighborCornerId+2)%4]; + CvCBCorner *conCornerCW3 = quadNeighborJ->corners[(connectedNeighborCornerId+3)%4]; + + (quad_group[i])->corners[j]->row = conCorner->row; + (quad_group[i])->corners[j]->column = conCorner->column; + (quad_group[i])->corners[(j+1)%4]->row = conCorner->row - conCornerCW2->row + conCornerCW3->row; + (quad_group[i])->corners[(j+1)%4]->column = conCorner->column - conCornerCW2->column + conCornerCW3->column; + (quad_group[i])->corners[(j+2)%4]->row = conCorner->row + conCorner->row - conCornerCW2->row; + (quad_group[i])->corners[(j+2)%4]->column = conCorner->column + conCorner->column - conCornerCW2->column; + (quad_group[i])->corners[(j+3)%4]->row = conCorner->row - conCornerCW2->row + conCornerCW1->row; + (quad_group[i])->corners[(j+3)%4]->column = conCorner->column - conCornerCW2->column + conCornerCW1->column; + + + // Mark this quad as labeled + (quad_group[i])->labeled = true; + + + // Changes have taken place, set the flag + flag_changed = true; + + + // once is enough! + break; + } + } + } + } + } + } + + + // All corners are marked with row and column + // Record the minimal and maximal row and column indices + // It is unlikely that more than 8bit checkers are used per dimension, if there are + // an error would have been thrown at the beginning of "cvFindChessboardCorners2" + int min_row = 127; + int max_row = -127; + int min_column = 127; + int max_column = -127; + + for(int i = 0; i < count; i++ ) + { + CvCBQuad* q = quad_group[i]; + + for(int j = 0; j < 4; j++ ) + { + if( (q->corners[j])->row > max_row) + max_row = (q->corners[j])->row; + + if( (q->corners[j])->row < min_row) + min_row = (q->corners[j])->row; + + if( (q->corners[j])->column > max_column) + max_column = (q->corners[j])->column; + + if( (q->corners[j])->column < min_column) + min_column = (q->corners[j])->column; + } + } + + // Label all internal corners with "needsNeighbor" = false + // Label all external corners with "needsNeighbor" = true, + // except if in a given dimension the pattern size is reached + for(int i = min_row; i <= max_row; i++) + { + for(int j = min_column; j <= max_column; j++) + { + // A flag that indicates, wheter a row/column combination is + // executed multiple times + bool flagg = false; + + + // Remember corner and quad + int cornerID; + int quadID; + + for(int k = 0; k < count; k++) + { + for(int l = 0; l < 4; l++) + { + if( ((quad_group[k])->corners[l]->row == i) && ((quad_group[k])->corners[l]->column == j) ) + { + + if (flagg == true) + { + // Passed at least twice through here + (quad_group[k])->corners[l]->needsNeighbor = false; + (quad_group[quadID])->corners[cornerID]->needsNeighbor = false; + } + else + { + // Mark with needs a neighbor, but note the + // address + (quad_group[k])->corners[l]->needsNeighbor = true; + cornerID = l; + quadID = k; + } + + + // set the flag to true + flagg = true; + } + } + } + } + } + + + // Complete Linking: + // sometimes not all corners were properly linked in "mrFindQuadNeighbors2", + // but after labeling each corner with its respective row and column, it is + // possible to match them anyway. + for(int i = min_row; i <= max_row; i++) + { + for(int j = min_column; j <= max_column; j++) + { + // the following "number" indicates the number of corners which + // correspond to the given (i,j) value + // 1 is a border corner or a conrer which still needs a neighbor + // 2 is a fully connected internal corner + // >2 something went wrong during labeling, report a warning + int number = 1; + + + // remember corner and quad + int cornerID; + int quadID; + + for(int k = 0; k < count; k++) + { + for(int l = 0; l < 4; l++) + { + if( ((quad_group[k])->corners[l]->row == i) && ((quad_group[k])->corners[l]->column == j) ) + { + + if (number == 1) + { + // First corner, note its ID + cornerID = l; + quadID = k; + } + + else if (number == 2) + { + // Second corner, check wheter this and the + // first one have equal coordinates, else + // interpolate + float delta_x = (quad_group[k])->corners[l]->pt.x - (quad_group[quadID])->corners[cornerID]->pt.x; + float delta_y = (quad_group[k])->corners[l]->pt.y - (quad_group[quadID])->corners[cornerID]->pt.y; + + if (delta_x != 0 || delta_y != 0) + { + // Interpolate + (quad_group[k])->corners[l]->pt.x = (quad_group[k])->corners[l]->pt.x - delta_x/2; + (quad_group[quadID])->corners[cornerID]->pt.x = (quad_group[quadID])->corners[cornerID]->pt.x + delta_x/2; + (quad_group[k])->corners[l]->pt.y = (quad_group[k])->corners[l]->pt.y - delta_y/2; + (quad_group[quadID])->corners[cornerID]->pt.y = (quad_group[quadID])->corners[cornerID]->pt.y + delta_y/2; + } + } + else if (number > 2) + { + // Something went wrong during row/column labeling + // Report a Warning + // ->Implemented in the function "mrWriteCorners" + } + + // increase the number by one + number = number + 1; + } + } + } + } + } + + + // Bordercorners don't need any neighbors, if the pattern size in the + // respective direction is reached + // The only time we can make shure that the target pattern size is reached in a given + // dimension, is when the larger side has reached the target size in the maximal + // direction, or if the larger side is larger than the smaller target size and the + // smaller side equals the smaller target size + int largerDimPattern = max(pattern_size.height,pattern_size.width); + int smallerDimPattern = min(pattern_size.height,pattern_size.width); + bool flagSmallerDim1 = false; + bool flagSmallerDim2 = false; + + if((largerDimPattern + 1) == max_column - min_column) + { + flagSmallerDim1 = true; + // We found out that in the column direction the target pattern size is reached + // Therefore border column corners do not need a neighbor anymore + // Go through all corners + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->column == min_column || (quad_group[k])->corners[l]->column == max_column) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + + if((largerDimPattern + 1) == max_row - min_row) + { + flagSmallerDim2 = true; + // We found out that in the column direction the target pattern size is reached + // Therefore border column corners do not need a neighbor anymore + // Go through all corners + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->row == min_row || (quad_group[k])->corners[l]->row == max_row) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + + + // Check the two flags: + // - If one is true and the other false, then the pattern target + // size was reached in in one direction -> We can check, whether the target + // pattern size is also reached in the other direction + // - If both are set to true, then we deal with a square board -> do nothing + // - If both are set to false -> There is a possibility that the larger side is + // larger than the smaller target size -> Check and if true, then check whether + // the other side has the same size as the smaller target size + if( (flagSmallerDim1 == false && flagSmallerDim2 == true) ) + { + // Larger target pattern size is in row direction, check wheter smaller target + // pattern size is reached in column direction + if((smallerDimPattern + 1) == max_column - min_column) + { + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->column == min_column || (quad_group[k])->corners[l]->column == max_column) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + } + + if( (flagSmallerDim1 == true && flagSmallerDim2 == false) ) + { + // Larger target pattern size is in column direction, check wheter smaller target + // pattern size is reached in row direction + if((smallerDimPattern + 1) == max_row - min_row) + { + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->row == min_row || (quad_group[k])->corners[l]->row == max_row) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + } + + if( (flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_column - min_column ) + { + // Larger target pattern size is in column direction, check wheter smaller target + // pattern size is reached in row direction + if((smallerDimPattern + 1) == max_row - min_row) + { + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->row == min_row || (quad_group[k])->corners[l]->row == max_row) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + } + + if( (flagSmallerDim1 == false && flagSmallerDim2 == false) && smallerDimPattern + 1 < max_row - min_row ) + { + // Larger target pattern size is in row direction, check wheter smaller target + // pattern size is reached in column direction + if((smallerDimPattern + 1) == max_column - min_column) + { + for( int k = 0; k < count; k++ ) + { + for( int l = 0; l < 4; l++ ) + { + if ( (quad_group[k])->corners[l]->column == min_column || (quad_group[k])->corners[l]->column == max_column) + { + // Needs no neighbor anymore + (quad_group[k])->corners[l]->needsNeighbor = false; + } + } + } + } + } + + + +// EVALUATE TIMER +#if TIMER + float time = (float) (clock() - start_time) / CLOCKS_PER_SEC; + LabelQuadGroup.open("timer/LabelQuadGroup.txt", ofstream::app); + LabelQuadGroup << "Time for mrLabelQuadGroup was " << time << " seconds." << endl; + LabelQuadGroup.close(); +#endif + +} + + + +//=========================================================================== +// PRESERVE LARGEST QUAD GROUP +//=========================================================================== +// Copies all necessary information of every quad of the largest found group +// into a new Quad struct array. +// This information is then again needed in PART 2 of the MAIN LOOP +static void mrCopyQuadGroup( CvCBQuad **temp_quad_group, CvCBQuad **for_out_quad_group, int count ) +{ + for (int i = 0; i < count; i++) + { + for_out_quad_group[i] = new CvCBQuad; + for_out_quad_group[i]->count = temp_quad_group[i]->count; + for_out_quad_group[i]->edge_len = temp_quad_group[i]->edge_len; + for_out_quad_group[i]->group_idx = temp_quad_group[i]->group_idx; + for_out_quad_group[i]->labeled = temp_quad_group[i]->labeled; + + for (int j = 0; j < 4; j++) + { + for_out_quad_group[i]->corners[j] = new CvCBCorner; + for_out_quad_group[i]->corners[j]->pt.x = temp_quad_group[i]->corners[j]->pt.x; + for_out_quad_group[i]->corners[j]->pt.y = temp_quad_group[i]->corners[j]->pt.y; + for_out_quad_group[i]->corners[j]->row = temp_quad_group[i]->corners[j]->row; + for_out_quad_group[i]->corners[j]->column = temp_quad_group[i]->corners[j]->column; + for_out_quad_group[i]->corners[j]->needsNeighbor = temp_quad_group[i]->corners[j]->needsNeighbor; + } + } +} + + + +//=========================================================================== +// GIVE A GROUP IDX +//=========================================================================== +// This function replaces mrFindQuadNeighbors, which in turn replaced +// icvFindQuadNeighbors +static void mrFindQuadNeighbors2( CvCBQuad *quads, int quad_count, int dilation) +{ +//START TIMER +#if TIMER + ofstream FindQuadNeighbors2; + time_t start_time = clock(); +#endif + + // Thresh dilation is used to counter the effect of dilation on the + // distance between 2 neighboring corners. Since the distance below is + // computed as its square, we do here the same. Additionally, we take the + // conservative assumption that dilation was performed using the 3x3 CROSS + // kernel, which coresponds to the 4-neighborhood. + const float thresh_dilation = (float)(2*dilation+3)*(2*dilation+3)*2; // the "*2" is for the x and y component + int idx, i, k, j; // the "3" is for initial corner mismatch + float dx, dy, dist; + int cur_quad_group = -1; + + + // Find quad neighbors + for( idx = 0; idx < quad_count; idx++ ) + { + CvCBQuad* cur_quad = &quads[idx]; + + + // Go through all quadrangles and label them in groups + // For each corner of this quadrangle + for( i = 0; i < 4; i++ ) + { + CvPoint2D32f pt; + float min_dist = FLT_MAX; + int closest_corner_idx = -1; + CvCBQuad *closest_quad = 0; + CvCBCorner *closest_corner = 0; + + if( cur_quad->neighbors[i] ) + continue; + + pt = cur_quad->corners[i]->pt; + + + // Find the closest corner in all other quadrangles + for( k = 0; k < quad_count; k++ ) + { + if( k == idx ) + continue; + + for( j = 0; j < 4; j++ ) + { + // If it already has a neighbor + if( quads[k].neighbors[j] ) + continue; + + dx = pt.x - quads[k].corners[j]->pt.x; + dy = pt.y - quads[k].corners[j]->pt.y; + dist = dx * dx + dy * dy; + + + // The following "if" checks, whether "dist" is the + // shortest so far and smaller than the smallest + // edge length of the current and target quads + if( dist < min_dist && + dist <= (cur_quad->edge_len + thresh_dilation) && + dist <= (quads[k].edge_len + thresh_dilation) ) + { + // First Check everything from the viewpoint of the current quad + // compute midpoints of "parallel" quad sides 1 + float x1 = (cur_quad->corners[i]->pt.x + cur_quad->corners[(i+1)%4]->pt.x)/2; + float y1 = (cur_quad->corners[i]->pt.y + cur_quad->corners[(i+1)%4]->pt.y)/2; + float x2 = (cur_quad->corners[(i+2)%4]->pt.x + cur_quad->corners[(i+3)%4]->pt.x)/2; + float y2 = (cur_quad->corners[(i+2)%4]->pt.y + cur_quad->corners[(i+3)%4]->pt.y)/2; + // compute midpoints of "parallel" quad sides 2 + float x3 = (cur_quad->corners[i]->pt.x + cur_quad->corners[(i+3)%4]->pt.x)/2; + float y3 = (cur_quad->corners[i]->pt.y + cur_quad->corners[(i+3)%4]->pt.y)/2; + float x4 = (cur_quad->corners[(i+1)%4]->pt.x + cur_quad->corners[(i+2)%4]->pt.x)/2; + float y4 = (cur_quad->corners[(i+1)%4]->pt.y + cur_quad->corners[(i+2)%4]->pt.y)/2; + + // MARTIN: Heuristic + // For the corner "j" of quad "k" to be considered, + // it needs to be on the same side of the two lines as + // corner "i". This is given, if the cross product has + // the same sign for both computations below: + float a1 = x1 - x2; + float b1 = y1 - y2; + // the current corner + float c11 = cur_quad->corners[i]->pt.x - x2; + float d11 = cur_quad->corners[i]->pt.y - y2; + // the candidate corner + float c12 = quads[k].corners[j]->pt.x - x2; + float d12 = quads[k].corners[j]->pt.y - y2; + float sign11 = a1*d11 - c11*b1; + float sign12 = a1*d12 - c12*b1; + + float a2 = x3 - x4; + float b2 = y3 - y4; + // the current corner + float c21 = cur_quad->corners[i]->pt.x - x4; + float d21 = cur_quad->corners[i]->pt.y - y4; + // the candidate corner + float c22 = quads[k].corners[j]->pt.x - x4; + float d22 = quads[k].corners[j]->pt.y - y4; + float sign21 = a2*d21 - c21*b2; + float sign22 = a2*d22 - c22*b2; + + + // Then make shure that two border quads of the same row or + // column don't link. Check from the current corner's view, + // whether the corner diagonal from the candidate corner + // is also on the same side of the two lines as the current + // corner and the candidate corner. + float c13 = quads[k].corners[(j+2)%4]->pt.x - x2; + float d13 = quads[k].corners[(j+2)%4]->pt.y - y2; + float c23 = quads[k].corners[(j+2)%4]->pt.x - x4; + float d23 = quads[k].corners[(j+2)%4]->pt.y - y4; + float sign13 = a1*d13 - c13*b1; + float sign23 = a2*d23 - c23*b2; + + + // Then check everything from the viewpoint of the candidate quad + // compute midpoints of "parallel" quad sides 1 + float u1 = (quads[k].corners[j]->pt.x + quads[k].corners[(j+1)%4]->pt.x)/2; + float v1 = (quads[k].corners[j]->pt.y + quads[k].corners[(j+1)%4]->pt.y)/2; + float u2 = (quads[k].corners[(j+2)%4]->pt.x + quads[k].corners[(j+3)%4]->pt.x)/2; + float v2 = (quads[k].corners[(j+2)%4]->pt.y + quads[k].corners[(j+3)%4]->pt.y)/2; + // compute midpoints of "parallel" quad sides 2 + float u3 = (quads[k].corners[j]->pt.x + quads[k].corners[(j+3)%4]->pt.x)/2; + float v3 = (quads[k].corners[j]->pt.y + quads[k].corners[(j+3)%4]->pt.y)/2; + float u4 = (quads[k].corners[(j+1)%4]->pt.x + quads[k].corners[(j+2)%4]->pt.x)/2; + float v4 = (quads[k].corners[(j+1)%4]->pt.y + quads[k].corners[(j+2)%4]->pt.y)/2; + + // MARTIN: Heuristic + // for the corner "j" of quad "k" to be considered, it + // needs to be on the same side of the two lines as + // corner "i". This is again given, if the cross + //product has the same sign for both computations below: + float a3 = u1 - u2; + float b3 = v1 - v2; + // the current corner + float c31 = cur_quad->corners[i]->pt.x - u2; + float d31 = cur_quad->corners[i]->pt.y - v2; + // the candidate corner + float c32 = quads[k].corners[j]->pt.x - u2; + float d32 = quads[k].corners[j]->pt.y - v2; + float sign31 = a3*d31-c31*b3; + float sign32 = a3*d32-c32*b3; + + float a4 = u3 - u4; + float b4 = v3 - v4; + // the current corner + float c41 = cur_quad->corners[i]->pt.x - u4; + float d41 = cur_quad->corners[i]->pt.y - v4; + // the candidate corner + float c42 = quads[k].corners[j]->pt.x - u4; + float d42 = quads[k].corners[j]->pt.y - v4; + float sign41 = a4*d41-c41*b4; + float sign42 = a4*d42-c42*b4; + + + // Then make shure that two border quads of the same row or + // column don't link. Check from the candidate corner's view, + // whether the corner diagonal from the current corner + // is also on the same side of the two lines as the current + // corner and the candidate corner. + float c33 = cur_quad->corners[(i+2)%4]->pt.x - u2; + float d33 = cur_quad->corners[(i+2)%4]->pt.y - v2; + float c43 = cur_quad->corners[(i+2)%4]->pt.x - u4; + float d43 = cur_quad->corners[(i+2)%4]->pt.y - v4; + float sign33 = a3*d33-c33*b3; + float sign43 = a4*d43-c43*b4; + + + // Check whether conditions are fulfilled + if ( ((sign11 < 0 && sign12 < 0) || (sign11 > 0 && sign12 > 0)) && + ((sign21 < 0 && sign22 < 0) || (sign21 > 0 && sign22 > 0)) && + ((sign31 < 0 && sign32 < 0) || (sign31 > 0 && sign32 > 0)) && + ((sign41 < 0 && sign42 < 0) || (sign41 > 0 && sign42 > 0)) && + ((sign11 < 0 && sign13 < 0) || (sign11 > 0 && sign13 > 0)) && + ((sign21 < 0 && sign23 < 0) || (sign21 > 0 && sign23 > 0)) && + ((sign31 < 0 && sign33 < 0) || (sign31 > 0 && sign33 > 0)) && + ((sign41 < 0 && sign43 < 0) || (sign41 > 0 && sign43 > 0)) ) + + { + closest_corner_idx = j; + closest_quad = &quads[k]; + min_dist = dist; + } + } + } + } + + // Have we found a matching corner point? + if( closest_corner_idx >= 0 && min_dist < FLT_MAX ) + { + closest_corner = closest_quad->corners[closest_corner_idx]; + + + // Make shure that the closest quad does not have the current + // quad as neighbor already + for( j = 0; j < 4; j++ ) + { + if( closest_quad->neighbors[j] == cur_quad ) + break; + } + if( j < 4 ) + continue; + + + // We've found one more corner - remember it + closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f; + closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f; + + cur_quad->count++; + cur_quad->neighbors[i] = closest_quad; + cur_quad->corners[i] = closest_corner; + + closest_quad->count++; + closest_quad->neighbors[closest_corner_idx] = cur_quad; + closest_quad->corners[closest_corner_idx] = closest_corner; + } + } + } + +// EVALUATE TIMER +#if TIMER + float time = (float) (clock() - start_time) / CLOCKS_PER_SEC; + FindQuadNeighbors2.open("timer/FindQuadNeighbors2.txt", ofstream::app); + FindQuadNeighbors2 << "Time for mrFindQuadNeighbors2 was " << time << " seconds." << endl; + FindQuadNeighbors2.close(); +#endif +} + + + +//=========================================================================== +// AUGMENT PATTERN WITH ADDITIONAL QUADS +//=========================================================================== +// The first part of the function is basically a copy of +// "mrFindQuadNeighbors2" +// The comparisons between two points and two lines could be computed in their +// own function +static int mrAugmentBestRun( CvCBQuad *new_quads, int new_quad_count, int new_dilation, + CvCBQuad **old_quads, int old_quad_count, int old_dilation ) +{ +//START TIMER +#if TIMER + ofstream AugmentBestRun; + time_t start_time = clock(); +#endif + + // thresh dilation is used to counter the effect of dilation on the + // distance between 2 neighboring corners. Since the distance below is + // computed as its square, we do here the same. Additionally, we take the + // conservative assumption that dilation was performed using the 3x3 CROSS + // kernel, which coresponds to the 4-neighborhood. + const float thresh_dilation = (float)(2*new_dilation+3)*(2*old_dilation+3)*2; // the "*2" is for the x and y component + int idx, i, k, j; // the "3" is for initial corner mismatch + float dx, dy, dist; + + + // Search all old quads which have a neighbor that needs to be linked + for( idx = 0; idx < old_quad_count; idx++ ) + { + CvCBQuad* cur_quad = old_quads[idx]; + + + // For each corner of this quadrangle + for( i = 0; i < 4; i++ ) + { + CvPoint2D32f pt; + float min_dist = FLT_MAX; + int closest_corner_idx = -1; + CvCBQuad *closest_quad = 0; + CvCBCorner *closest_corner = 0; + + + // If cur_quad corner[i] is already linked, continue + if( cur_quad->corners[i]->needsNeighbor == false ) + continue; + + pt = cur_quad->corners[i]->pt; + + + // Look for a match in all new_quads' corners + for( k = 0; k < new_quad_count; k++ ) + { + // Only look at unlabeled new quads + if( new_quads[k].labeled == true) + continue; + + for( j = 0; j < 4; j++ ) + { + + // Only proceed if they are less than dist away from each + // other + dx = pt.x - new_quads[k].corners[j]->pt.x; + dy = pt.y - new_quads[k].corners[j]->pt.y; + dist = dx * dx + dy * dy; + + if( (dist < min_dist) && + dist <= (cur_quad->edge_len + thresh_dilation) && + dist <= (new_quads[k].edge_len + thresh_dilation) ) + { + // First Check everything from the viewpoint of the + // current quad compute midpoints of "parallel" quad + // sides 1 + float x1 = (cur_quad->corners[i]->pt.x + cur_quad->corners[(i+1)%4]->pt.x)/2; + float y1 = (cur_quad->corners[i]->pt.y + cur_quad->corners[(i+1)%4]->pt.y)/2; + float x2 = (cur_quad->corners[(i+2)%4]->pt.x + cur_quad->corners[(i+3)%4]->pt.x)/2; + float y2 = (cur_quad->corners[(i+2)%4]->pt.y + cur_quad->corners[(i+3)%4]->pt.y)/2; + // compute midpoints of "parallel" quad sides 2 + float x3 = (cur_quad->corners[i]->pt.x + cur_quad->corners[(i+3)%4]->pt.x)/2; + float y3 = (cur_quad->corners[i]->pt.y + cur_quad->corners[(i+3)%4]->pt.y)/2; + float x4 = (cur_quad->corners[(i+1)%4]->pt.x + cur_quad->corners[(i+2)%4]->pt.x)/2; + float y4 = (cur_quad->corners[(i+1)%4]->pt.y + cur_quad->corners[(i+2)%4]->pt.y)/2; + + // MARTIN: Heuristic + // For the corner "j" of quad "k" to be considered, + // it needs to be on the same side of the two lines as + // corner "i". This is given, if the cross product has + // the same sign for both computations below: + float a1 = x1 - x2; + float b1 = y1 - y2; + // the current corner + float c11 = cur_quad->corners[i]->pt.x - x2; + float d11 = cur_quad->corners[i]->pt.y - y2; + // the candidate corner + float c12 = new_quads[k].corners[j]->pt.x - x2; + float d12 = new_quads[k].corners[j]->pt.y - y2; + float sign11 = a1*d11 - c11*b1; + float sign12 = a1*d12 - c12*b1; + + float a2 = x3 - x4; + float b2 = y3 - y4; + // the current corner + float c21 = cur_quad->corners[i]->pt.x - x4; + float d21 = cur_quad->corners[i]->pt.y - y4; + // the candidate corner + float c22 = new_quads[k].corners[j]->pt.x - x4; + float d22 = new_quads[k].corners[j]->pt.y - y4; + float sign21 = a2*d21 - c21*b2; + float sign22 = a2*d22 - c22*b2; + + // Also make shure that two border quads of the same row or + // column don't link. Check from the current corner's view, + // whether the corner diagonal from the candidate corner + // is also on the same side of the two lines as the current + // corner and the candidate corner. + float c13 = new_quads[k].corners[(j+2)%4]->pt.x - x2; + float d13 = new_quads[k].corners[(j+2)%4]->pt.y - y2; + float c23 = new_quads[k].corners[(j+2)%4]->pt.x - x4; + float d23 = new_quads[k].corners[(j+2)%4]->pt.y - y4; + float sign13 = a1*d13 - c13*b1; + float sign23 = a2*d23 - c23*b2; + + + // Second: Then check everything from the viewpoint of + // the candidate quad. Compute midpoints of "parallel" + // quad sides 1 + float u1 = (new_quads[k].corners[j]->pt.x + new_quads[k].corners[(j+1)%4]->pt.x)/2; + float v1 = (new_quads[k].corners[j]->pt.y + new_quads[k].corners[(j+1)%4]->pt.y)/2; + float u2 = (new_quads[k].corners[(j+2)%4]->pt.x + new_quads[k].corners[(j+3)%4]->pt.x)/2; + float v2 = (new_quads[k].corners[(j+2)%4]->pt.y + new_quads[k].corners[(j+3)%4]->pt.y)/2; + // compute midpoints of "parallel" quad sides 2 + float u3 = (new_quads[k].corners[j]->pt.x + new_quads[k].corners[(j+3)%4]->pt.x)/2; + float v3 = (new_quads[k].corners[j]->pt.y + new_quads[k].corners[(j+3)%4]->pt.y)/2; + float u4 = (new_quads[k].corners[(j+1)%4]->pt.x + new_quads[k].corners[(j+2)%4]->pt.x)/2; + float v4 = (new_quads[k].corners[(j+1)%4]->pt.y + new_quads[k].corners[(j+2)%4]->pt.y)/2; + + // MARTIN: Heuristic + // For the corner "j" of quad "k" to be considered, + // it needs to be on the same side of the two lines as + // corner "i". This is given, if the cross product has + // the same sign for both computations below: + float a3 = u1 - u2; + float b3 = v1 - v2; + // the current corner + float c31 = cur_quad->corners[i]->pt.x - u2; + float d31 = cur_quad->corners[i]->pt.y - v2; + // the candidate corner + float c32 = new_quads[k].corners[j]->pt.x - u2; + float d32 = new_quads[k].corners[j]->pt.y - v2; + float sign31 = a3*d31-c31*b3; + float sign32 = a3*d32-c32*b3; + + float a4 = u3 - u4; + float b4 = v3 - v4; + // the current corner + float c41 = cur_quad->corners[i]->pt.x - u4; + float d41 = cur_quad->corners[i]->pt.y - v4; + // the candidate corner + float c42 = new_quads[k].corners[j]->pt.x - u4; + float d42 = new_quads[k].corners[j]->pt.y - v4; + float sign41 = a4*d41-c41*b4; + float sign42 = a4*d42-c42*b4; + + // Also make shure that two border quads of the same row or + // column don't link. Check from the candidate corner's view, + // whether the corner diagonal from the current corner + // is also on the same side of the two lines as the current + // corner and the candidate corner. + float c33 = cur_quad->corners[(i+2)%4]->pt.x - u2; + float d33 = cur_quad->corners[(i+2)%4]->pt.y - v2; + float c43 = cur_quad->corners[(i+2)%4]->pt.x - u4; + float d43 = cur_quad->corners[(i+2)%4]->pt.y - v4; + float sign33 = a3*d33-c33*b3; + float sign43 = a4*d43-c43*b4; + + + // This time we also need to make shure, that no quad + // is linked to a quad of another dilation run which + // may lie INSIDE it!!! + // Third: Therefore check everything from the viewpoint + // of the current quad compute midpoints of "parallel" + // quad sides 1 + float x5 = cur_quad->corners[i]->pt.x; + float y5 = cur_quad->corners[i]->pt.y; + float x6 = cur_quad->corners[(i+1)%4]->pt.x; + float y6 = cur_quad->corners[(i+1)%4]->pt.y; + // compute midpoints of "parallel" quad sides 2 + float x7 = x5; + float y7 = y5; + float x8 = cur_quad->corners[(i+3)%4]->pt.x; + float y8 = cur_quad->corners[(i+3)%4]->pt.y; + + // MARTIN: Heuristic + // For the corner "j" of quad "k" to be considered, + // it needs to be on the other side of the two lines than + // corner "i". This is given, if the cross product has + // a different sign for both computations below: + float a5 = x6 - x5; + float b5 = y6 - y5; + // the current corner + float c51 = cur_quad->corners[(i+2)%4]->pt.x - x5; + float d51 = cur_quad->corners[(i+2)%4]->pt.y - y5; + // the candidate corner + float c52 = new_quads[k].corners[j]->pt.x - x5; + float d52 = new_quads[k].corners[j]->pt.y - y5; + float sign51 = a5*d51 - c51*b5; + float sign52 = a5*d52 - c52*b5; + + float a6 = x8 - x7; + float b6 = y8 - y7; + // the current corner + float c61 = cur_quad->corners[(i+2)%4]->pt.x - x7; + float d61 = cur_quad->corners[(i+2)%4]->pt.y - y7; + // the candidate corner + float c62 = new_quads[k].corners[j]->pt.x - x7; + float d62 = new_quads[k].corners[j]->pt.y - y7; + float sign61 = a6*d61 - c61*b6; + float sign62 = a6*d62 - c62*b6; + + + // Fourth: Then check everything from the viewpoint of + // the candidate quad compute midpoints of "parallel" + // quad sides 1 + float u5 = new_quads[k].corners[j]->pt.x; + float v5 = new_quads[k].corners[j]->pt.y; + float u6 = new_quads[k].corners[(j+1)%4]->pt.x; + float v6 = new_quads[k].corners[(j+1)%4]->pt.y; + // compute midpoints of "parallel" quad sides 2 + float u7 = u5; + float v7 = v5; + float u8 = new_quads[k].corners[(j+3)%4]->pt.x; + float v8 = new_quads[k].corners[(j+3)%4]->pt.y; + + // MARTIN: Heuristic + // For the corner "j" of quad "k" to be considered, + // it needs to be on the other side of the two lines than + // corner "i". This is given, if the cross product has + // a different sign for both computations below: + float a7 = u6 - u5; + float b7 = v6 - v5; + // the current corner + float c71 = cur_quad->corners[i]->pt.x - u5; + float d71 = cur_quad->corners[i]->pt.y - v5; + // the candidate corner + float c72 = new_quads[k].corners[(j+2)%4]->pt.x - u5; + float d72 = new_quads[k].corners[(j+2)%4]->pt.y - v5; + float sign71 = a7*d71-c71*b7; + float sign72 = a7*d72-c72*b7; + + float a8 = u8 - u7; + float b8 = v8 - v7; + // the current corner + float c81 = cur_quad->corners[i]->pt.x - u7; + float d81 = cur_quad->corners[i]->pt.y - v7; + // the candidate corner + float c82 = new_quads[k].corners[(j+2)%4]->pt.x - u7; + float d82 = new_quads[k].corners[(j+2)%4]->pt.y - v7; + float sign81 = a8*d81-c81*b8; + float sign82 = a8*d82-c82*b8; + + + + + + // Check whether conditions are fulfilled + if ( ((sign11 < 0 && sign12 < 0) || (sign11 > 0 && sign12 > 0)) && + ((sign21 < 0 && sign22 < 0) || (sign21 > 0 && sign22 > 0)) && + ((sign31 < 0 && sign32 < 0) || (sign31 > 0 && sign32 > 0)) && + ((sign41 < 0 && sign42 < 0) || (sign41 > 0 && sign42 > 0)) && + ((sign11 < 0 && sign13 < 0) || (sign11 > 0 && sign13 > 0)) && + ((sign21 < 0 && sign23 < 0) || (sign21 > 0 && sign23 > 0)) && + ((sign31 < 0 && sign33 < 0) || (sign31 > 0 && sign33 > 0)) && + ((sign41 < 0 && sign43 < 0) || (sign41 > 0 && sign43 > 0)) && + ((sign51 < 0 && sign52 > 0) || (sign51 > 0 && sign52 < 0)) && + ((sign61 < 0 && sign62 > 0) || (sign61 > 0 && sign62 < 0)) && + ((sign71 < 0 && sign72 > 0) || (sign71 > 0 && sign72 < 0)) && + ((sign81 < 0 && sign82 > 0) || (sign81 > 0 && sign82 < 0)) ) + { + closest_corner_idx = j; + closest_quad = &new_quads[k]; + min_dist = dist; + } + } + } + } + + // Have we found a matching corner point? + if( closest_corner_idx >= 0 && min_dist < FLT_MAX ) + { + closest_corner = closest_quad->corners[closest_corner_idx]; + closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f; + closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f; + + + // We've found one more corner - remember it + // ATTENTION: write the corner x and y coordinates separately, + // else the crucial row/column entries will be overwritten !!! + cur_quad->corners[i]->pt.x = closest_corner->pt.x; + cur_quad->corners[i]->pt.y = closest_corner->pt.y; + cur_quad->neighbors[i] = closest_quad; + closest_quad->corners[closest_corner_idx]->pt.x = closest_corner->pt.x; + closest_quad->corners[closest_corner_idx]->pt.y = closest_corner->pt.y; + + + // Label closest quad as labeled. In this way we exclude it + // being considered again during the next loop iteration + closest_quad->labeled = true; + + + // We have a new member of the final pattern, copy it over + old_quads[old_quad_count] = new CvCBQuad; + old_quads[old_quad_count]->count = 1; + old_quads[old_quad_count]->edge_len = closest_quad->edge_len; + old_quads[old_quad_count]->group_idx = cur_quad->group_idx; //the same as the current quad + old_quads[old_quad_count]->labeled = false; //do it right afterwards + + + // We only know one neighbor for shure, initialize rest with + // the NULL pointer + old_quads[old_quad_count]->neighbors[closest_corner_idx] = cur_quad; + old_quads[old_quad_count]->neighbors[(closest_corner_idx+1)%4] = NULL; + old_quads[old_quad_count]->neighbors[(closest_corner_idx+2)%4] = NULL; + old_quads[old_quad_count]->neighbors[(closest_corner_idx+3)%4] = NULL; + + for (int j = 0; j < 4; j++) + { + old_quads[old_quad_count]->corners[j] = new CvCBCorner; + old_quads[old_quad_count]->corners[j]->pt.x = closest_quad->corners[j]->pt.x; + old_quads[old_quad_count]->corners[j]->pt.y = closest_quad->corners[j]->pt.y; + } + + cur_quad->neighbors[i] = old_quads[old_quad_count]; + + + // Start the function again + return -1; + } + } + } + +// EVALUATE TIMER +#if TIMER + float time = (float) (clock() - start_time) / CLOCKS_PER_SEC; + AugmentBestRun.open("timer/AugmentBestRun.txt", ofstream::app); + AugmentBestRun << "Time for mrAugmentBestRun was " << time << " seconds." << endl; + AugmentBestRun.close(); +#endif + + // Finished, don't start the function again + return 1; +} + + + +//=========================================================================== +// GENERATE QUADRANGLES +//=========================================================================== +static int +icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners, + CvMemStorage *storage, CvMat *image, int flags, int dilation, bool firstRun ) +{ +//START TIMER +#if TIMER + ofstream GenerateQuads; + time_t start_time = clock(); +#endif + + // Initializations + int quad_count = 0; + CvMemStorage *temp_storage = 0; + + if( out_quads ) + *out_quads = 0; + + if( out_corners ) + *out_corners = 0; + + CV_FUNCNAME( "icvGenerateQuads" ); + + __BEGIN__; + + CvSeq *src_contour = 0; + CvSeq *root; + CvContourEx* board = 0; + CvContourScanner scanner; + int i, idx, min_size; + + CV_ASSERT( out_corners && out_quads ); + + + // Empiric sower bound for the size of allowable quadrangles. + // MARTIN, modified: Added "*0.1" in order to find smaller quads. + min_size = cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 * 0.1); + + + // Create temporary storage for contours and the sequence of pointers to + // found quadrangles + CV_CALL( temp_storage = cvCreateChildMemStorage( storage )); + CV_CALL( root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage )); + + + // Initialize contour retrieving routine + CV_CALL( scanner = cvStartFindContours( image, temp_storage, sizeof(CvContourEx), + CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE )); + + + // Get all the contours one by one + while( (src_contour = cvFindNextContour( scanner )) != 0 ) + { + CvSeq *dst_contour = 0; + CvRect rect = ((CvContour*)src_contour)->rect; + + + + // Reject contours with a too small perimeter and contours which are + // completely surrounded by another contour + // MARTIN: If this function is called during PART 1, then the parameter "first run" + // is set to "true". This guarantees, that only "nice" squares are detected. + // During PART 2, we allow the polygonial approcimation function below to + // approximate more freely, which can result in recognized "squares" that are in + // reality multiple blurred and sticked together squares. + if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size ) + { + int min_approx_level = 2, max_approx_level; + if (firstRun == true) + max_approx_level = 3; + else + max_approx_level = MAX_CONTOUR_APPROX; + int approx_level; + for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ ) + { + dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage, + CV_POLY_APPROX_DP, (float)approx_level ); + + + // We call this again on its own output, because sometimes + // cvApproxPoly() does not simplify as much as it should. + dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage, + CV_POLY_APPROX_DP, (float)approx_level ); + + if( dst_contour->total == 4 ) + break; + } + + + // Reject non-quadrangles + if(dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) ) + { + CvPoint pt[4]; + double d1, d2, p = cvContourPerimeter(dst_contour); + double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ)); + double dx, dy; + + for( i = 0; i < 4; i++ ) + pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i); + + dx = pt[0].x - pt[2].x; + dy = pt[0].y - pt[2].y; + d1 = sqrt(dx*dx + dy*dy); + + dx = pt[1].x - pt[3].x; + dy = pt[1].y - pt[3].y; + d2 = sqrt(dx*dx + dy*dy); + + // PHILIPG: Only accept those quadrangles which are more + // square than rectangular and which are big enough + double d3, d4; + dx = pt[0].x - pt[1].x; + dy = pt[0].y - pt[1].y; + d3 = sqrt(dx*dx + dy*dy); + dx = pt[1].x - pt[2].x; + dy = pt[1].y - pt[2].y; + d4 = sqrt(dx*dx + dy*dy); + if(true)//!(flags & CV_CALIB_CB_FILTER_QUADS) || + //d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size && + //d1 >= 0.15 * p && d2 >= 0.15 * p ) + { + CvContourEx* parent = (CvContourEx*)(src_contour->v_prev); + parent->counter++; + if( !board || board->counter < parent->counter ) + board = parent; + dst_contour->v_prev = (CvSeq*)parent; + cvSeqPush( root, &dst_contour ); + } + } + } + } + + + // Finish contour retrieving + cvEndFindContours( &scanner ); + + + // Allocate quad & corner buffers + CV_CALL( *out_quads = (CvCBQuad*)cvAlloc(root->total * sizeof((*out_quads)[0]))); + CV_CALL( *out_corners = (CvCBCorner*)cvAlloc(root->total * 4 * sizeof((*out_corners)[0]))); + + + // Create array of quads structures + for( idx = 0; idx < root->total; idx++ ) + { + CvCBQuad* q = &(*out_quads)[quad_count]; + src_contour = *(CvSeq**)cvGetSeqElem( root, idx ); + if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board ) + continue; + + + // Reset group ID + memset( q, 0, sizeof(*q) ); + q->group_idx = -1; + assert( src_contour->total == 4 ); + for( i = 0; i < 4; i++ ) + { + CvPoint2D32f pt = cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i)); + CvCBCorner* corner = &(*out_corners)[quad_count*4 + i]; + + memset( corner, 0, sizeof(*corner) ); + corner->pt = pt; + q->corners[i] = corner; + } + q->edge_len = FLT_MAX; + for( i = 0; i < 4; i++ ) + { + float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x; + float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y; + float d = dx*dx + dy*dy; + if( q->edge_len > d ) + q->edge_len = d; + } + quad_count++; + } + + __END__; + + if( cvGetErrStatus() < 0 ) + { + if( out_quads ) + cvFree( out_quads ); + if( out_corners ) + cvFree( out_corners ); + quad_count = 0; + } + + cvReleaseMemStorage( &temp_storage ); + +// EVALUATE TIMER +#if TIMER + float time = (float) (clock() - start_time) / CLOCKS_PER_SEC; + GenerateQuads.open("timer/GenerateQuads.txt", ofstream::app); + GenerateQuads << "Time for icvGenerateQuads was " << time << " seconds." << endl; + GenerateQuads.close(); +#endif + + return quad_count; +} + + + +//=========================================================================== +// WRITE CORNERS TO FILE +//=========================================================================== +static int mrWriteCorners( CvCBQuad **output_quads, int count, CvSize pattern_size, int min_number_of_corners ) +{ + // Initialize + int corner_count = 0; + bool flagRow = false; + bool flagColumn = false; + int maxPattern_sizeRow = -1; + int maxPattern_sizeColumn = -1; + + + // Return variable + int internal_found = 0; + + + // Compute the minimum and maximum row / column ID + // (it is unlikely that more than 8bit checkers are used per dimension) + int min_row = 127; + int max_row = -127; + int min_column = 127; + int max_column = -127; + + for(int i = 0; i < count; i++ ) + { + CvCBQuad* q = output_quads[i]; + + for(int j = 0; j < 4; j++ ) + { + if( (q->corners[j])->row > max_row) + max_row = (q->corners[j])->row; + if( (q->corners[j])->row < min_row) + min_row = (q->corners[j])->row; + if( (q->corners[j])->column > max_column) + max_column = (q->corners[j])->column; + if( (q->corners[j])->column < min_column) + min_column = (q->corners[j])->column; + } + } + + + // If in a given direction the target pattern size is reached, we know exactly how + // the checkerboard is oriented. + // Else we need to prepare enought "dummy" corners for the worst case. + for(int i = 0; i < count; i++ ) + { + CvCBQuad* q = output_quads[i]; + + for(int j = 0; j < 4; j++ ) + { + if( (q->corners[j])->column == max_column && (q->corners[j])->row != min_row && (q->corners[j])->row != max_row ) + { + if( (q->corners[j]->needsNeighbor) == false) + { + // We know, that the target pattern size is reached + // in column direction + flagColumn = true; + } + } + if( (q->corners[j])->row == max_row && (q->corners[j])->column != min_column && (q->corners[j])->column != max_column ) + { + if( (q->corners[j]->needsNeighbor) == false) + { + // We know, that the target pattern size is reached + // in row direction + flagRow = true; + } + } + } + } + + if( flagColumn == true) + { + if( max_column - min_column == pattern_size.width + 1) + { + maxPattern_sizeColumn = pattern_size.width; + maxPattern_sizeRow = pattern_size.height; + } + else + { + maxPattern_sizeColumn = pattern_size.height; + maxPattern_sizeRow = pattern_size.width; + } + } + else if( flagRow == true) + { + if( max_row - min_row == pattern_size.width + 1) + { + maxPattern_sizeRow = pattern_size.width; + maxPattern_sizeColumn = pattern_size.height; + } + else + { + maxPattern_sizeRow = pattern_size.height; + maxPattern_sizeColumn = pattern_size.width; + } + } + else + { + // If target pattern size is not reached in at least one of the two + // directions, then we do not know where the remaining corners are + // located. Account for this. + maxPattern_sizeColumn = max(pattern_size.width, pattern_size.height); + maxPattern_sizeRow = max(pattern_size.width, pattern_size.height); + } + + + // Open the output files + ofstream cornersX("cToMatlab/cornersX.txt"); + ofstream cornersY("cToMatlab/cornersY.txt"); + ofstream cornerInfo("cToMatlab/cornerInfo.txt"); + + + // Write the corners in increasing order to the output file + for(int i = min_row + 1; i < maxPattern_sizeRow + min_row + 1; i++) + { + for(int j = min_column + 1; j < maxPattern_sizeColumn + min_column + 1; j++) + { + // Reset the iterator + int iter = 1; + + for(int k = 0; k < count; k++) + { + for(int l = 0; l < 4; l++) + { + if(((output_quads[k])->corners[l]->row == i) && ((output_quads[k])->corners[l]->column == j) ) + { + // Only write corners to the output file, which are connected + // i.e. only if iter == 2 + if( iter == 2) + { + // The respective row and column have been found, print it to + // the output file, only do this once + cornersX << (output_quads[k])->corners[l]->pt.x; + cornersX << " "; + cornersY << (output_quads[k])->corners[l]->pt.y; + cornersY << " "; + + corner_count++; + } + + + // If the iterator is larger than two, this means that more than + // two corners have the same row / column entries. Then some + // linking errors must have occured and we should not use the found + // pattern + if (iter > 2) + return -1; + + iter++; + } + } + } + + // If the respective row / column is non - existent or is a border corner + if (iter == 1 || iter == 2) + { + cornersX << -1; + cornersX << " "; + cornersY << -1; + cornersY << " "; + } + } + cornersX << endl; + cornersY << endl; + } + + + // Write to the corner matrix size info file + cornerInfo << maxPattern_sizeRow<< " " << maxPattern_sizeColumn << endl; + + + // Close the output files + cornersX.close(); + cornersY.close(); + cornerInfo.close(); + + + // check whether enough corners have been found + if (corner_count >= min_number_of_corners) + internal_found = 1; + else + internal_found = 0; + + + // pattern found, or not found? + return internal_found; +} + +//=========================================================================== +// END OF FILE +//=========================================================================== diff --git a/cvcalibinit3.h b/cvcalibinit3.h new file mode 100644 index 0000000..bbe231e --- /dev/null +++ b/cvcalibinit3.h @@ -0,0 +1,3 @@ +int cvFindChessboardCorners3( const void* arr, CvSize pattern_size, + CvPoint2D32f* out_corners, int* out_corner_count, + int min_number_of_corners ); diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..191022a --- /dev/null +++ b/main.cpp @@ -0,0 +1,242 @@ +/************************************************************************************\ + This is improved variant of chessboard corner detection algorithm that + uses a graph of connected quads. It is based on the code contributed + by Vladimir Vezhnevets and Philip Gruebele. + Here is the copyright notice from the original Vladimir's code: + =============================================================== + + The algorithms developed and implemented by Vezhnevets Vldimir + aka Dead Moroz (vvp@graphics.cs.msu.ru) + See http://graphics.cs.msu.su/en/research/calibration/opencv.html + for detailed information. + + Reliability additions and modifications made by Philip Gruebele. + pgruebele@cox.net + + His code was adapted for use with low resolution and omnidirectional cameras + by Martin Rufli during his Master Thesis under supervision of Davide Scaramuzza, at the ETH Zurich. Further enhancements include: + - Increased chance of correct corner matching. + - Corner matching over all dilation runs. + +If you use this code, please cite the following articles: + +1. Scaramuzza, D., Martinelli, A. and Siegwart, R. (2006), A Toolbox for Easily Calibrating Omnidirectional Cameras, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2006), Beijing, China, October 2006. +2. Scaramuzza, D., Martinelli, A. and Siegwart, R., (2006). "A Flexible Technique for Accurate Omnidirectional Camera Calibration and Structure from Motion", Proceedings of IEEE International Conference of Vision Systems (ICVS'06), New York, January 5-7, 2006. +3. Rufli, M., Scaramuzza, D., and Siegwart, R. (2008), Automatic Detection of Checkerboards on Blurred and Distorted Images, Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS 2008), Nice, France, September 2008. + +\************************************************************************************/ + + +// Includes +#include +#include + +#include + +#include +#include +#include + +#include +using namespace std; +using std::ifstream; + +#include "cvcalibinit3.h" +/* +static int cvFindChessboardCorners3( const void* arr, CvSize pattern_size, + CvPoint2D32f* out_corners, int* out_corner_count, + int min_number_of_corners ); +*/ + +//=========================================================================== +// MAIN LOOP +//=========================================================================== +int main( int argc, char** argv ) +{ + // CHOOSE METHOD (0= Vladimir Vezhnevets, 1= Martin Rufli) + int method = 1; + + + // Initializations + CvSize board_size = {7,6}; + const char* input_filename = 0; + CvCapture* capture = 0; + FILE* f = 0; + char imagename[1024]; + CvMemStorage* storage; + CvSeq* image_points_seq = 0; + int elem_size; + CvPoint2D32f* image_points_buf = 0; + CvSize img_size = {0,0}; + int found = -2; + int min_number_of_corners = 42; + input_filename = "pictures.txt"; + //input_filename = "myVideo2.avi"; + + // Create error message file + ofstream error("outputImages/error.txt"); + + + // Read the "argv" function input arguments + for(int i = 1; i < argc; i++ ) + { + const char* s = argv[i]; + if( strcmp( s, "-w" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &board_size.width ) != 1 || board_size.width <= 0 ) + { + error << "Invalid board width" << endl; + error.close(); + return -1; + } + } + else if( strcmp( s, "-h" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &board_size.height ) != 1 || board_size.height <= 0 ) + { + error << "Invalid board height" << endl; + error.close(); + return -1; + } + } + else if( strcmp( s, "-m" ) == 0 ) + { + if( sscanf( argv[++i], "%u", &min_number_of_corners ) != 1 ) + { + error << "Invalid minimal number of corners" << endl; + error.close(); + return -1; + } + } + else if( s[0] != '-' ) + input_filename = s; + else + { + error << "Unknown option" << endl; + error.close(); + return -1; + } + } + + + // Close error message file + error.close(); + + + // Figure out what kind of image input needs to be prepared + if( input_filename ) + { + // Try to open a video sequence + //capture = cvCreateFileCapture( input_filename ); //OBRAND commented out + if( !capture ) + { + // Try to open an input image + f = fopen( input_filename, "rt" ); + if( !f ) + return fprintf( stderr, "The input file could not be opened\n" ), -1; + } + } + else + // Open a live video stream + capture = cvCreateCameraCapture(0); + + + // Nothing could be opened -> error + if( !capture && !f ) + return fprintf( stderr, "Could not initialize video capture\n" ), -2; + + + // Allocate memory + elem_size = board_size.width*board_size.height*sizeof(image_points_buf[0]); + storage = cvCreateMemStorage( MAX( elem_size*4, 1 << 16 )); + image_points_buf = (CvPoint2D32f*)cvAlloc( elem_size ); + image_points_seq = cvCreateSeq( 0, sizeof(CvSeq), elem_size, storage ); + + + // For loop which goes through all images specified above + for(int j = 1;; j++) + { + // Initializations + IplImage *view = 0, *view_gray = 0; + int count = 0, blink = 0; + CvSize text_size = {0,0}; + int base_line = 0; + // Load the correct image... + if( f && fgets( imagename, sizeof(imagename)-2, f )) + { + + int l = (int) strlen(imagename); + if( l > 0 && imagename[l-1] == '\n' ) + imagename[--l] = '\0'; + if( l > 0 ) + { + if( imagename[0] == '#' ) + continue; + // Load as BGR 3 channel image + view = cvLoadImage( imagename, 1 ); + // Currently the following file formats are supported: + // Windows bitmaps BMP, DIB + // JPEG files JPEG, JPG, JPE + // Portable Network Graphics PNG + // Portable image format PBM, PGM, PPM + // Sun rasters SR, RAS + // TIFF files TIFF, TIF + // NOTABLY: GIF IS NOT SUPPORTED! + } + } + + + // ...Or capture the correct frame from the video + else if( capture ) + { + IplImage* view0 = cvQueryFrame( capture ); + if( view0 ) + { + view = cvCreateImage( cvGetSize(view0), IPL_DEPTH_8U, view0->nChannels ); + if( view0->origin == IPL_ORIGIN_BL ) + cvFlip( view0, view, 0 ); + else + cvCopy( view0, view ); + } + } + + + // If no more images are to be processed -> break + if( !view) + { + break; + } + + // If esc key was pressed -> break + int key = cvWaitKey(10); + if( key == 27) + { + break; + } + + img_size = cvGetSize(view); + + + // Perform the corner finding algorithm + // 0 = old method, 1 = New method by Martin Rufli + if (method == 0) + { + //found = cvFindChessboardCorners1( view, board_size, + // image_points_buf, &count, CV_CALIB_CB_ADAPTIVE_THRESH ); + } + else + { + found = cvFindChessboardCorners3( view, board_size, + image_points_buf, &count, min_number_of_corners ); + } + + if( !view ) + break; + cvReleaseImage( &view ); + } + + if( capture ) + cvReleaseCapture( &capture ); + + return found; +} diff --git a/pictures.txt b/pictures.txt new file mode 100644 index 0000000..5d4f25c --- /dev/null +++ b/pictures.txt @@ -0,0 +1 @@ +./VMRImage0.jpg \ No newline at end of file