Move the sources to trunk
[opencv] / cv / src / cvcalibinit.cpp
1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 //  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 //  By downloading, copying, installing or using the software you agree to this license.
6 //  If you do not agree to this license, do not download, install,
7 //  copy or use the software.
8 //
9 //
10 //                        Intel License Agreement
11 //                For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 //   * Redistribution's of source code must retain the above copyright notice,
20 //     this list of conditions and the following disclaimer.
21 //
22 //   * Redistribution's in binary form must reproduce the above copyright notice,
23 //     this list of conditions and the following disclaimer in the documentation
24 //     and/or other materials provided with the distribution.
25 //
26 //   * The name of Intel Corporation may not be used to endorse or promote products
27 //     derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 /************************************************************************************\
43     This is improved variant of chessboard corner detection algorithm that
44     uses a graph of connected quads. It is based on the code contributed
45     by Vladimir Vezhnevets and Philip Gruebele.
46     Here is the copyright notice from the original Vladimir's code:
47     ===============================================================
48
49     The algorithms developed and implemented by Vezhnevets Vldimir
50     aka Dead Moroz (vvp@graphics.cs.msu.ru)
51     See http://graphics.cs.msu.su/en/research/calibration/opencv.html
52     for detailed information.
53
54     Reliability additions and modifications made by Philip Gruebele.
55     <a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
56
57 \************************************************************************************/
58
59 #include "_cv.h"
60
61 //=====================================================================================
62 // Implementation for the enhanced calibration object detection
63 //=====================================================================================
64
65 #define MAX_CONTOUR_APPROX  7
66
67 typedef struct CvContourEx
68 {
69     CV_CONTOUR_FIELDS()
70     int counter;
71 }
72 CvContourEx;
73
74 //=====================================================================================
75
76 /// Corner info structure
77 /** This structure stores information about the chessboard corner.*/
78 typedef struct CvCBCorner
79 {
80     CvPoint2D32f pt; // Coordinates of the corner
81     int row;         // Board row index
82     int count;       // Number of neighbor corners
83     struct CvCBCorner* neighbors[4]; // Neighbor corners
84 }
85 CvCBCorner;
86
87 //=====================================================================================
88 /// Quadrangle contour info structure
89 /** This structure stores information about the chessboard quadrange.*/
90 typedef struct CvCBQuad
91 {
92     int count;      // Number of quad neibors
93     int group_idx;  // quad group ID
94     float edge_len; // quad size characteristic
95     CvCBCorner *corners[4]; // Coordinates of quad corners
96     struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors
97 }
98 CvCBQuad;
99
100 //=====================================================================================
101
102 //static CvMat* debug_img = 0;
103
104 static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners,
105                              CvMemStorage *storage, CvMat *image, int flags );
106
107 static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count );
108
109 static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count,
110                                   CvCBQuad **quad_group, int group_idx,
111                                   CvMemStorage* storage );
112
113 static int icvCheckQuadGroup( CvCBQuad **quad_group, int count,
114                               CvCBCorner **out_corners, CvSize pattern_size );
115
116 static int icvCleanFoundConnectedQuads( int quad_count,
117                 CvCBQuad **quads, CvSize pattern_size );
118
119 CV_IMPL
120 int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
121                              CvPoint2D32f* out_corners, int* out_corner_count,
122                              int flags )
123 {
124     const int min_dilations = 1;
125     const int max_dilations = 3;
126     int found = 0;
127     CvMat* norm_img = 0;
128     CvMat* thresh_img = 0;
129     CvMemStorage* storage = 0;
130
131     CvCBQuad *quads = 0, **quad_group = 0;
132     CvCBCorner *corners = 0, **corner_group = 0;
133
134     if( out_corner_count )
135         *out_corner_count = 0;
136
137     CV_FUNCNAME( "cvFindChessBoardCornerGuesses2" );
138
139     __BEGIN__;
140
141     int quad_count, group_idx, i, dilations;
142     CvMat stub, *img = (CvMat*)arr;
143
144     CV_CALL( img = cvGetMat( img, &stub ));
145     //debug_img = img;
146
147     if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 )
148         CV_ERROR( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" );
149
150     if( pattern_size.width <= 2 || pattern_size.height <= 2 )
151         CV_ERROR( CV_StsOutOfRange, "pattern should have at least 2x2 size" );
152
153     if( !out_corners )
154         CV_ERROR( CV_StsNullPtr, "Null pointer to corners" );
155
156     CV_CALL( storage = cvCreateMemStorage(0) );
157     CV_CALL( thresh_img = cvCreateMat( img->rows, img->cols, CV_8UC1 ));
158
159     if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) )
160     {
161         // equalize the input image histogram -
162         // that should make the contrast between "black" and "white" areas big enough
163         CV_CALL( norm_img = cvCreateMat( img->rows, img->cols, CV_8UC1 ));
164
165         if( CV_MAT_CN(img->type) != 1 )
166         {
167             CV_CALL( cvCvtColor( img, norm_img, CV_BGR2GRAY ));
168             img = norm_img;
169         }
170
171         if( flags & CV_CALIB_CB_NORMALIZE_IMAGE )
172         {
173             cvEqualizeHist( img, norm_img );
174             img = norm_img;
175         }
176     }
177
178     // Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
179     // This is necessary because some squares simply do not separate properly with a single dilation.  However,
180     // we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
181     // making it difficult to detect smaller squares.
182     for( dilations = min_dilations; dilations <= max_dilations; dilations++ )
183     {
184         // convert the input grayscale image to binary (black-n-white)
185         if( flags & CV_CALIB_CB_ADAPTIVE_THRESH )
186         {
187             int block_size = cvRound(MIN(img->cols,img->rows)*0.2)|1;
188             cvDilate( img, thresh_img, 0, dilations );
189
190             // convert to binary
191             cvAdaptiveThreshold( img, thresh_img, 255,
192                 CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, 0 );
193             cvDilate( thresh_img, thresh_img, 0, dilations-1 );
194         }
195         else
196         {
197             // Make dilation before the thresholding.
198             // It splits chessboard corners
199             //cvDilate( img, thresh_img, 0, 1 );
200
201             // empiric threshold level
202             double mean = cvMean( img );
203             int thresh_level = cvRound( mean - 10 );
204             thresh_level = MAX( thresh_level, 10 );
205
206             cvThreshold( img, thresh_img, thresh_level, 255, CV_THRESH_BINARY );
207             cvDilate( thresh_img, thresh_img, 0, dilations );
208         }
209
210         // So we can find rectangles that go to the edge, we draw a white line around the image edge.
211         // Otherwise FindContours will miss those clipped rectangle contours.
212         // The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
213         cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1,
214                      thresh_img->rows-1), CV_RGB(255,255,255), 3, 8);
215
216         CV_CALL( quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags ));
217         if( quad_count <= 0 )
218             continue;
219
220         // Find quad's neighbors
221         CV_CALL( icvFindQuadNeighbors( quads, quad_count ));
222
223         CV_CALL( quad_group = (CvCBQuad**)cvAlloc( sizeof(quad_group[0]) * quad_count));
224         CV_CALL( corner_group = (CvCBCorner**)cvAlloc( sizeof(corner_group[0]) * quad_count*4 ));
225
226         for( group_idx = 0; ; group_idx++ )
227         {
228             int count;
229             CV_CALL( count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage ));
230
231             if( count == 0 )
232                 break;
233
234             // If count is more than it should be, this will remove those quads
235             // which cause maximum deviation from a nice square pattern.
236             CV_CALL( count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size ));
237             CV_CALL( count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size ));
238
239             if( count > 0 || (out_corner_count && -count > *out_corner_count) )
240             {
241                 int n = count > 0 ? pattern_size.width * pattern_size.height : -count;
242                 n = MIN( n, pattern_size.width * pattern_size.height );
243
244                 // copy corners to output array
245                 for( i = 0; i < n; i++ )
246                     out_corners[i] = corner_group[i]->pt;
247
248                 if( out_corner_count )
249                     *out_corner_count = n;
250
251                 if( count > 0 )
252                 {
253                     found = 1;
254                     EXIT;
255                 }
256             }
257         }
258
259         cvFree( &quads );
260         cvFree( &corners );
261     }
262
263     __END__;
264
265     cvReleaseMemStorage( &storage );
266     cvReleaseMat( &norm_img );
267     cvReleaseMat( &thresh_img );
268     cvFree( &quads );
269     cvFree( &corners );
270     cvFree( &quad_group );
271     cvFree( &corner_group );
272
273     return found;
274 }
275
276
277 // if we found too many connect quads, remove those which probably do not belong.
278 static int
279 icvCleanFoundConnectedQuads( int quad_count, CvCBQuad **quad_group, CvSize pattern_size )
280 {
281     CvMemStorage *temp_storage = 0;
282     CvPoint2D32f *centers = 0;
283
284     CV_FUNCNAME( "icvCleanFoundConnectedQuads" );
285
286     __BEGIN__;
287
288     CvPoint2D32f center = {0,0};
289     int i, j, k;
290     // number of quads this pattern should contain
291     int count = ((pattern_size.width + 1)*(pattern_size.height + 1) + 1)/2;
292
293     // if we have more quadrangles than we should,
294     // try to eliminate duplicates or ones which don't belong to the pattern rectangle...
295     if( quad_count <= count )
296         EXIT;
297
298     // create an array of quadrangle centers
299     CV_CALL( centers = (CvPoint2D32f *)cvAlloc( sizeof(centers[0])*quad_count ));
300     CV_CALL( temp_storage = cvCreateMemStorage(0));
301
302     for( i = 0; i < quad_count; i++ )
303     {
304         CvPoint2D32f ci = {0,0};
305         CvCBQuad* q = quad_group[i];
306
307         for( j = 0; j < 4; j++ )
308         {
309             CvPoint2D32f pt = q->corners[j]->pt;
310             ci.x += pt.x;
311             ci.y += pt.y;
312         }
313
314         ci.x *= 0.25f;
315         ci.y *= 0.25f;
316
317         centers[i] = ci;
318         center.x += ci.x;
319         center.y += ci.y;
320     }
321     center.x /= quad_count;
322     center.y /= quad_count;
323
324     // If we still have more quadrangles than we should,
325     // we try to eliminate bad ones based on minimizing the bounding box.
326     // We iteratively remove the point which reduces the size of
327     // the bounding box of the blobs the most
328     // (since we want the rectangle to be as small as possible)
329     // remove the quadrange that causes the biggest reduction
330     // in pattern size until we have the correct number
331     for( ; quad_count > count; quad_count-- )
332     {
333         double min_box_area = DBL_MAX;
334         int skip, min_box_area_index = -1;
335         CvCBQuad *q0, *q;
336
337         // For each point, calculate box area without that point
338         for( skip = 0; skip < quad_count; skip++ )
339         {
340             // get bounding rectangle
341             CvPoint2D32f temp = centers[skip]; // temporarily make index 'skip' the same as
342             centers[skip] = center;            // pattern center (so it is not counted for convex hull)
343             CvMat pointMat = cvMat(1, quad_count, CV_32FC2, centers);
344             CvSeq *hull = cvConvexHull2( &pointMat, temp_storage, CV_CLOCKWISE, 1 );
345             centers[skip] = temp;
346             double hull_area = fabs(cvContourArea(hull, CV_WHOLE_SEQ));
347
348             // remember smallest box area
349             if( hull_area < min_box_area )
350             {
351                 min_box_area = hull_area;
352                 min_box_area_index = skip;
353             }
354             cvClearMemStorage( temp_storage );
355         }
356
357         q0 = quad_group[min_box_area_index];
358
359         // remove any references to this quad as a neighbor
360         for( i = 0; i < quad_count; i++ )
361         {
362             q = quad_group[i];
363             for( j = 0; j < 4; j++ )
364             {
365                 if( q->neighbors[j] == q0 )
366                 {
367                     q->neighbors[j] = 0;
368                     q->count--;
369                     for( k = 0; k < 4; k++ )
370                         if( q0->neighbors[k] == q )
371                         {
372                             q0->neighbors[k] = 0;
373                             q0->count--;
374                             break;
375                         }
376                     break;
377                 }
378             }
379         }
380
381         // remove the quad
382         quad_count--;
383         quad_group[min_box_area_index] = quad_group[quad_count];
384         centers[min_box_area_index] = centers[quad_count];
385     }
386
387     __END__;
388
389     cvReleaseMemStorage( &temp_storage );
390     cvFree( &centers );
391
392     return quad_count;
393 }
394
395 //=====================================================================================
396
397 static int
398 icvFindConnectedQuads( CvCBQuad *quad, int quad_count, CvCBQuad **out_group,
399                        int group_idx, CvMemStorage* storage )
400 {
401     CvMemStorage* temp_storage = cvCreateChildMemStorage( storage );
402     CvSeq* stack = cvCreateSeq( 0, sizeof(*stack), sizeof(void*), temp_storage );
403     int i, count = 0;
404
405     // Scan the array for a first unlabeled quad
406     for( i = 0; i < quad_count; i++ )
407     {
408         if( quad[i].count > 0 && quad[i].group_idx < 0)
409             break;
410     }
411
412     // Recursively find a group of connected quads starting from the seed quad[i]
413     if( i < quad_count )
414     {
415         CvCBQuad* q = &quad[i];
416         cvSeqPush( stack, &q );
417         out_group[count++] = q;
418         q->group_idx = group_idx;
419
420         while( stack->total )
421         {
422             cvSeqPop( stack, &q );
423             for( i = 0; i < 4; i++ )
424             {
425                 CvCBQuad *neighbor = q->neighbors[i];
426                 if( neighbor && neighbor->count > 0 && neighbor->group_idx < 0 )
427                 {
428                     cvSeqPush( stack, &neighbor );
429                     out_group[count++] = neighbor;
430                     neighbor->group_idx = group_idx;
431                 }
432             }
433         }
434     }
435
436     cvReleaseMemStorage( &temp_storage );
437     return count;
438 }
439
440
441 //=====================================================================================
442
443 static int
444 icvCheckQuadGroup( CvCBQuad **quad_group, int quad_count,
445                    CvCBCorner **out_corners, CvSize pattern_size )
446 {
447     const int ROW1 = 1000000;
448     const int ROW2 = 2000000;
449     const int ROW_ = 3000000;
450     int result = 0;
451     int i, out_corner_count = 0, corner_count = 0;
452     CvCBCorner** corners = 0;
453
454     CV_FUNCNAME( "icvCheckQuadGroup" );
455
456     __BEGIN__;
457
458     int j, k, kk;
459     int width = 0, height = 0;
460     int hist[5] = {0,0,0,0,0};
461     CvCBCorner* first = 0, *first2 = 0, *right, *cur, *below, *c;
462     CV_CALL( corners = (CvCBCorner**)cvAlloc( quad_count*4*sizeof(corners[0]) ));
463
464     // build dual graph, which vertices are internal quad corners
465     // and two vertices are connected iff they lie on the same quad edge
466     for( i = 0; i < quad_count; i++ )
467     {
468         CvCBQuad* q = quad_group[i];
469         /*CvScalar color = q->count == 0 ? cvScalar(0,255,255) :
470                          q->count == 1 ? cvScalar(0,0,255) :
471                          q->count == 2 ? cvScalar(0,255,0) :
472                          q->count == 3 ? cvScalar(255,255,0) :
473                                          cvScalar(255,0,0);*/
474
475         for( j = 0; j < 4; j++ )
476         {
477             //cvLine( debug_img, cvPointFrom32f(q->corners[j]->pt), cvPointFrom32f(q->corners[(j+1)&3]->pt), color, 1, CV_AA, 0 );
478             if( q->neighbors[j] )
479             {
480                 CvCBCorner *a = q->corners[j], *b = q->corners[(j+1)&3];
481                 // mark internal corners that belong to:
482                 //   - a quad with a single neighbor - with ROW1,
483                 //   - a quad with two neighbors     - with ROW2
484                 // make the rest of internal corners with ROW_
485                 int row_flag = q->count == 1 ? ROW1 : q->count == 2 ? ROW2 : ROW_;
486
487                 if( a->row == 0 )
488                 {
489                     corners[corner_count++] = a;
490                     a->row = row_flag;
491                 }
492                 else if( a->row > row_flag )
493                     a->row = row_flag;
494
495                 if( q->neighbors[(j+1)&3] )
496                 {
497                     if( a->count >= 4 || b->count >= 4 )
498                         EXIT;
499                     for( k = 0; k < 4; k++ )
500                     {
501                         if( a->neighbors[k] == b )
502                             EXIT;
503                         if( b->neighbors[k] == a )
504                             EXIT;
505                     }
506                     a->neighbors[a->count++] = b;
507                     b->neighbors[b->count++] = a;
508                 }
509             }
510         }
511     }
512
513     if( corner_count != pattern_size.width*pattern_size.height )
514         EXIT;
515
516     for( i = 0; i < corner_count; i++ )
517     {
518         int n = corners[i]->count;
519         assert( 0 <= n && n <= 4 );
520         hist[n]++;
521         if( !first && n == 2 )
522         {
523             if( corners[i]->row == ROW1 )
524                 first = corners[i];
525             else if( !first2 && corners[i]->row == ROW2 )
526                 first2 = corners[i];
527         }
528     }
529
530     // start with a corner that belongs to a quad with a signle neighbor.
531     // if we do not have such, start with a corner of a quad with two neighbors.
532     if( !first )
533         first = first2;
534
535     if( !first || hist[0] != 0 || hist[1] != 0 || hist[2] != 4 ||
536         hist[3] != (pattern_size.width + pattern_size.height)*2 - 8 )
537         EXIT;
538
539     cur = first;
540     right = below = 0;
541     out_corners[out_corner_count++] = cur;
542
543     for( k = 0; k < 4; k++ )
544     {
545         c = cur->neighbors[k];
546         if( c )
547         {
548             if( !right )
549                 right = c;
550             else if( !below )
551                 below = c;
552         }
553     }
554
555     if( !right || right->count != 2 && right->count != 3 ||
556         !below || below->count != 2 && below->count != 3 )
557         EXIT;
558
559     cur->row = 0;
560     //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,255,0), -1, 8, 0 );
561
562     first = below; // remember the first corner in the next row
563     // find and store the first row (or column)
564     for(j=1;;j++)
565     {
566         right->row = 0;
567         out_corners[out_corner_count++] = right;
568         //cvCircle( debug_img, cvPointFrom32f(right->pt), 3, cvScalar(0,255-j*10,0), -1, 8, 0 );
569         if( right->count == 2 )
570             break;
571         if( right->count != 3 || out_corner_count >= MAX(pattern_size.width,pattern_size.height) )
572             EXIT;
573         cur = right;
574         for( k = 0; k < 4; k++ )
575         {
576             c = cur->neighbors[k];
577             if( c && c->row > 0 )
578             {
579                 for( kk = 0; kk < 4; kk++ )
580                 {
581                     if( c->neighbors[kk] == below )
582                         break;
583                 }
584                 if( kk < 4 )
585                     below = c;
586                 else
587                     right = c;
588             }
589         }
590     }
591
592     width = out_corner_count;
593     if( width == pattern_size.width )
594         height = pattern_size.height;
595     else if( width == pattern_size.height )
596         height = pattern_size.width;
597     else
598         EXIT;
599
600     // find and store all the other rows
601     for( i = 1; ; i++ )
602     {
603         if( !first )
604             break;
605         cur = first;
606         first = 0;
607         for( j = 0;; j++ )
608         {
609             cur->row = i;
610             out_corners[out_corner_count++] = cur;
611             //cvCircle( debug_img, cvPointFrom32f(cur->pt), 3, cvScalar(0,0,255-j*10), -1, 8, 0 );
612             if( cur->count == 2 + (i < height-1) && j > 0 )
613                 break;
614
615             right = 0;
616
617             // find a neighbor that has not been processed yet
618             // and that has a neighbor from the previous row
619             for( k = 0; k < 4; k++ )
620             {
621                 c = cur->neighbors[k];
622                 if( c && c->row > i )
623                 {
624                     for( kk = 0; kk < 4; kk++ )
625                     {
626                         if( c->neighbors[kk] && c->neighbors[kk]->row == i-1 )
627                             break;
628                     }
629                     if( kk < 4 )
630                     {
631                         right = c;
632                         if( j > 0 )
633                             break;
634                     }
635                     else if( j == 0 )
636                         first = c;
637                 }
638             }
639             if( !right )
640                 EXIT;
641             cur = right;
642         }
643
644         if( j != width - 1 )
645             EXIT;
646     }
647
648     if( out_corner_count != corner_count )
649         EXIT;
650
651     // check if we need to transpose the board
652     if( width != pattern_size.width )
653     {
654         CV_SWAP( width, height, k );
655
656         memcpy( corners, out_corners, corner_count*sizeof(corners[0]) );
657         for( i = 0; i < height; i++ )
658             for( j = 0; j < width; j++ )
659                 out_corners[i*width + j] = corners[j*height + i];
660     }
661
662     // check if we need to revert the order in each row
663     {
664         CvPoint2D32f p0 = out_corners[0]->pt, p1 = out_corners[pattern_size.width-1]->pt,
665                      p2 = out_corners[pattern_size.width]->pt;
666         if( (p1.x - p0.x)*(p2.y - p1.y) - (p1.y - p0.y)*(p2.x - p1.x) < 0 )
667         {
668             if( width % 2 == 0 )
669             {
670                 for( i = 0; i < height; i++ )
671                     for( j = 0; j < width/2; j++ )
672                         CV_SWAP( out_corners[i*width+j], out_corners[i*width+width-j-1], c );
673             }
674             else
675             {
676                 for( j = 0; j < width; j++ )
677                     for( i = 0; i < height/2; i++ )
678                         CV_SWAP( out_corners[i*width+j], out_corners[(height - i - 1)*width+j], c );
679             }
680         }
681     }
682
683     result = corner_count;
684
685     __END__;
686
687     if( result <= 0 && corners )
688     {
689         corner_count = MIN( corner_count, pattern_size.width*pattern_size.height );
690         for( i = 0; i < corner_count; i++ )
691             out_corners[i] = corners[i];
692         result = -corner_count;
693     }
694
695     cvFree( &corners );
696
697     return result;
698 }
699
700 //=====================================================================================
701
702 static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count )
703 {
704     const float thresh_scale = 1.f;
705     int idx, i, k, j;
706     float dx, dy, dist;
707
708     // find quad neighbors
709     for( idx = 0; idx < quad_count; idx++ )
710     {
711         CvCBQuad* cur_quad = &quads[idx];
712
713         // choose the points of the current quadrangle that are close to
714         // some points of the other quadrangles
715         // (it can happen for split corners (due to dilation) of the
716         // checker board). Search only in other quadrangles!
717
718         // for each corner of this quadrangle
719         for( i = 0; i < 4; i++ )
720         {
721             CvPoint2D32f pt;
722             float min_dist = FLT_MAX;
723             int closest_corner_idx = -1;
724             CvCBQuad *closest_quad = 0;
725             CvCBCorner *closest_corner = 0;
726
727             if( cur_quad->neighbors[i] )
728                 continue;
729
730             pt = cur_quad->corners[i]->pt;
731
732             // find the closest corner in all other quadrangles
733             for( k = 0; k < quad_count; k++ )
734             {
735                 if( k == idx )
736                     continue;
737
738                 for( j = 0; j < 4; j++ )
739                 {
740                     if( quads[k].neighbors[j] )
741                         continue;
742
743                     dx = pt.x - quads[k].corners[j]->pt.x;
744                     dy = pt.y - quads[k].corners[j]->pt.y;
745                     dist = dx * dx + dy * dy;
746
747                     if( dist < min_dist &&
748                         dist <= cur_quad->edge_len*thresh_scale &&
749                         dist <= quads[k].edge_len*thresh_scale )
750                     {
751                         closest_corner_idx = j;
752                         closest_quad = &quads[k];
753                         min_dist = dist;
754                     }
755                 }
756             }
757
758             // we found a matching corner point?
759             if( closest_corner_idx >= 0 && min_dist < FLT_MAX )
760             {
761                 // If another point from our current quad is closer to the found corner
762                 // than the current one, then we don't count this one after all.
763                 // This is necessary to support small squares where otherwise the wrong
764                 // corner will get matched to closest_quad;
765                 closest_corner = closest_quad->corners[closest_corner_idx];
766
767                 for( j = 0; j < 4; j++ )
768                 {
769                     if( cur_quad->neighbors[j] == closest_quad )
770                         break;
771
772                     dx = closest_corner->pt.x - cur_quad->corners[j]->pt.x;
773                     dy = closest_corner->pt.y - cur_quad->corners[j]->pt.y;
774
775                     if( dx * dx + dy * dy < min_dist )
776                         break;
777                 }
778
779                 if( j < 4 || cur_quad->count >= 4 || closest_quad->count >= 4 )
780                     continue;
781
782                 // Check that each corner is a neighbor of different quads
783                 for( j = 0; j < closest_quad->count; j++ )
784                 {
785                     if( closest_quad->neighbors[j] == cur_quad )
786                         break;
787                 }
788                 if( j < closest_quad->count )
789                     continue;
790
791                 // check whether the closest corner to closest_corner
792                 // is different from cur_quad->corners[i]->pt
793                 for( k = 0; k < quad_count; k++ )
794                 {
795                     CvCBQuad* q = &quads[k];
796                     if( k == idx || q == closest_quad )
797                         continue;
798
799                     for( j = 0; j < 4; j++ )
800                         if( !q->neighbors[j] )
801                         {
802                             dx = closest_corner->pt.x - q->corners[j]->pt.x;
803                             dy = closest_corner->pt.y - q->corners[j]->pt.y;
804                             dist = dx*dx + dy*dy;
805                             if( dist < min_dist )
806                                 break;
807                         }
808                     if( j < 4 )
809                         break;
810                 }
811
812                 if( k < quad_count )
813                     continue;
814
815                 closest_corner->pt.x = (pt.x + closest_corner->pt.x) * 0.5f;
816                 closest_corner->pt.y = (pt.y + closest_corner->pt.y) * 0.5f;
817
818                 // We've found one more corner - remember it
819                 cur_quad->count++;
820                 cur_quad->neighbors[i] = closest_quad;
821                 cur_quad->corners[i] = closest_corner;
822
823                 closest_quad->count++;
824                 closest_quad->neighbors[closest_corner_idx] = cur_quad;
825             }
826         }
827     }
828 }
829
830 //=====================================================================================
831
832 static int
833 icvGenerateQuads( CvCBQuad **out_quads, CvCBCorner **out_corners,
834                   CvMemStorage *storage, CvMat *image, int flags )
835 {
836     int quad_count = 0;
837     CvMemStorage *temp_storage = 0;
838
839     if( out_quads )
840         *out_quads = 0;
841
842     if( out_corners )
843         *out_corners = 0;
844
845     CV_FUNCNAME( "icvGenerateQuads" );
846
847     __BEGIN__;
848
849     CvSeq *src_contour = 0;
850     CvSeq *root;
851     CvContourEx* board = 0;
852     CvContourScanner scanner;
853     int i, idx, min_size;
854
855     CV_ASSERT( out_corners && out_quads );
856
857     // empiric bound for minimal allowed perimeter for squares
858     min_size = cvRound( image->cols * image->rows * .03 * 0.01 * 0.92 );
859
860     // create temporary storage for contours and the sequence of pointers to found quadrangles
861     CV_CALL( temp_storage = cvCreateChildMemStorage( storage ));
862     CV_CALL( root = cvCreateSeq( 0, sizeof(CvSeq), sizeof(CvSeq*), temp_storage ));
863
864     // initialize contour retrieving routine
865     CV_CALL( scanner = cvStartFindContours( image, temp_storage, sizeof(CvContourEx),
866                                             CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ));
867
868     // get all the contours one by one
869     while( (src_contour = cvFindNextContour( scanner )) != 0 )
870     {
871         CvSeq *dst_contour = 0;
872         CvRect rect = ((CvContour*)src_contour)->rect;
873
874         // reject contours with too small perimeter
875         if( CV_IS_SEQ_HOLE(src_contour) && rect.width*rect.height >= min_size )
876         {
877             const int min_approx_level = 2, max_approx_level = MAX_CONTOUR_APPROX;
878             int approx_level;
879             for( approx_level = min_approx_level; approx_level <= max_approx_level; approx_level++ )
880             {
881                 dst_contour = cvApproxPoly( src_contour, sizeof(CvContour), temp_storage,
882                                             CV_POLY_APPROX_DP, (float)approx_level );
883                 // we call this again on its own output, because sometimes
884                 // cvApproxPoly() does not simplify as much as it should.
885                 dst_contour = cvApproxPoly( dst_contour, sizeof(CvContour), temp_storage,
886                                             CV_POLY_APPROX_DP, (float)approx_level );
887
888                 if( dst_contour->total == 4 )
889                     break;
890             }
891
892             // reject non-quadrangles
893             if( dst_contour->total == 4 && cvCheckContourConvexity(dst_contour) )
894             {
895                 CvPoint pt[4];
896                 double d1, d2, p = cvContourPerimeter(dst_contour);
897                 double area = fabs(cvContourArea(dst_contour, CV_WHOLE_SEQ));
898                 double dx, dy;
899
900                 for( i = 0; i < 4; i++ )
901                     pt[i] = *(CvPoint*)cvGetSeqElem(dst_contour, i);
902
903                 dx = pt[0].x - pt[2].x;
904                 dy = pt[0].y - pt[2].y;
905                 d1 = sqrt(dx*dx + dy*dy);
906
907                 dx = pt[1].x - pt[3].x;
908                 dy = pt[1].y - pt[3].y;
909                 d2 = sqrt(dx*dx + dy*dy);
910
911                 // philipg.  Only accept those quadrangles which are more square
912                 // than rectangular and which are big enough
913                 double d3, d4;
914                 dx = pt[0].x - pt[1].x;
915                 dy = pt[0].y - pt[1].y;
916                 d3 = sqrt(dx*dx + dy*dy);
917                 dx = pt[1].x - pt[2].x;
918                 dy = pt[1].y - pt[2].y;
919                 d4 = sqrt(dx*dx + dy*dy);
920                 if( !(flags & CV_CALIB_CB_FILTER_QUADS) ||
921                     d3*4 > d4 && d4*4 > d3 && d3*d4 < area*1.5 && area > min_size &&
922                     d1 >= 0.15 * p && d2 >= 0.15 * p )
923                 {
924                     CvContourEx* parent = (CvContourEx*)(src_contour->v_prev);
925                     parent->counter++;
926                     if( !board || board->counter < parent->counter )
927                         board = parent;
928                     dst_contour->v_prev = (CvSeq*)parent;
929                     //for( i = 0; i < 4; i++ ) cvLine( debug_img, pt[i], pt[(i+1)&3], cvScalar(200,255,255), 1, CV_AA, 0 );
930                     cvSeqPush( root, &dst_contour );
931                 }
932             }
933         }
934     }
935
936     // finish contour retrieving
937     cvEndFindContours( &scanner );
938
939     // allocate quad & corner buffers
940     CV_CALL( *out_quads = (CvCBQuad*)cvAlloc(root->total * sizeof((*out_quads)[0])));
941     CV_CALL( *out_corners = (CvCBCorner*)cvAlloc(root->total * 4 * sizeof((*out_corners)[0])));
942
943     // Create array of quads structures
944     for( idx = 0; idx < root->total; idx++ )
945     {
946         CvCBQuad* q = &(*out_quads)[quad_count];
947         src_contour = *(CvSeq**)cvGetSeqElem( root, idx );
948         if( (flags & CV_CALIB_CB_FILTER_QUADS) && src_contour->v_prev != (CvSeq*)board )
949             continue;
950
951         // reset group ID
952         memset( q, 0, sizeof(*q) );
953         q->group_idx = -1;
954         assert( src_contour->total == 4 );
955         for( i = 0; i < 4; i++ )
956         {
957             CvPoint2D32f pt = cvPointTo32f(*(CvPoint*)cvGetSeqElem(src_contour, i));
958             CvCBCorner* corner = &(*out_corners)[quad_count*4 + i];
959
960             memset( corner, 0, sizeof(*corner) );
961             corner->pt = pt;
962             q->corners[i] = corner;
963         }
964         q->edge_len = FLT_MAX;
965         for( i = 0; i < 4; i++ )
966         {
967             float dx = q->corners[i]->pt.x - q->corners[(i+1)&3]->pt.x;
968             float dy = q->corners[i]->pt.y - q->corners[(i+1)&3]->pt.y;
969             float d = dx*dx + dy*dy;
970             if( q->edge_len > d )
971                 q->edge_len = d;
972         }
973         quad_count++;
974     }
975
976     __END__;
977
978     if( cvGetErrStatus() < 0 )
979     {
980         if( out_quads )
981             cvFree( out_quads );
982         if( out_corners )
983             cvFree( out_corners );
984         quad_count = 0;
985     }
986
987     cvReleaseMemStorage( &temp_storage );
988     return quad_count;
989 }
990
991
992 CV_IMPL void
993 cvDrawChessboardCorners( CvArr* _image, CvSize pattern_size,
994                          CvPoint2D32f* corners, int count, int found )
995 {
996     CV_FUNCNAME( "cvDrawChessboardCorners" );
997
998     __BEGIN__;
999
1000     const int shift = 0;
1001     const int radius = 4;
1002     const int r = radius*(1 << shift);
1003     int i;
1004     CvMat stub, *image;
1005     double scale = 1;
1006     int type, cn, line_type;
1007
1008     CV_CALL( image = cvGetMat( _image, &stub ));
1009
1010     type = CV_MAT_TYPE(image->type);
1011     cn = CV_MAT_CN(type);
1012     if( cn != 1 && cn != 3 && cn != 4 )
1013         CV_ERROR( CV_StsUnsupportedFormat, "Number of channels must be 1, 3 or 4" );
1014
1015     switch( CV_MAT_DEPTH(image->type) )
1016     {
1017     case CV_8U:
1018         scale = 1;
1019         break;
1020     case CV_16U:
1021         scale = 256;
1022         break;
1023     case CV_32F:
1024         scale = 1./255;
1025         break;
1026     default:
1027         CV_ERROR( CV_StsUnsupportedFormat,
1028             "Only 8-bit, 16-bit or floating-point 32-bit images are supported" );
1029     }
1030
1031     line_type = type == CV_8UC1 || type == CV_8UC3 ? CV_AA : 8;
1032
1033     if( !found )
1034     {
1035         CvScalar color = {{0,0,255}};
1036         if( cn == 1 )
1037             color = cvScalarAll(200);
1038         color.val[0] *= scale;
1039         color.val[1] *= scale;
1040         color.val[2] *= scale;
1041         color.val[3] *= scale;
1042
1043         for( i = 0; i < count; i++ )
1044         {
1045             CvPoint pt;
1046             pt.x = cvRound(corners[i].x*(1 << shift));
1047             pt.y = cvRound(corners[i].y*(1 << shift));
1048             cvLine( image, cvPoint( pt.x - r, pt.y - r ),
1049                     cvPoint( pt.x + r, pt.y + r ), color, 1, line_type, shift );
1050             cvLine( image, cvPoint( pt.x - r, pt.y + r),
1051                     cvPoint( pt.x + r, pt.y - r), color, 1, line_type, shift );
1052             cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift );
1053         }
1054     }
1055     else
1056     {
1057         int x, y;
1058         CvPoint prev_pt = {0, 0};
1059         const int line_max = 7;
1060         static const CvScalar line_colors[line_max] =
1061         {
1062             {{0,0,255}},
1063             {{0,128,255}},
1064             {{0,200,200}},
1065             {{0,255,0}},
1066             {{200,200,0}},
1067             {{255,0,0}},
1068             {{255,0,255}}
1069         };
1070
1071         for( y = 0, i = 0; y < pattern_size.height; y++ )
1072         {
1073             CvScalar color = line_colors[y % line_max];
1074             if( cn == 1 )
1075                 color = cvScalarAll(200);
1076             color.val[0] *= scale;
1077             color.val[1] *= scale;
1078             color.val[2] *= scale;
1079             color.val[3] *= scale;
1080
1081             for( x = 0; x < pattern_size.width; x++, i++ )
1082             {
1083                 CvPoint pt;
1084                 pt.x = cvRound(corners[i].x*(1 << shift));
1085                 pt.y = cvRound(corners[i].y*(1 << shift));
1086
1087                 if( i != 0 )
1088                     cvLine( image, prev_pt, pt, color, 1, line_type, shift );
1089
1090                 cvLine( image, cvPoint(pt.x - r, pt.y - r),
1091                         cvPoint(pt.x + r, pt.y + r), color, 1, line_type, shift );
1092                 cvLine( image, cvPoint(pt.x - r, pt.y + r),
1093                         cvPoint(pt.x + r, pt.y - r), color, 1, line_type, shift );
1094                 cvCircle( image, pt, r+(1<<shift), color, 1, line_type, shift );
1095                 prev_pt = pt;
1096             }
1097         }
1098     }
1099
1100     __END__;
1101 }
1102
1103
1104 /* End of file. */