📄 cvcalibinit.cpp
字号:
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
/************************************************************************************\
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.
<a href="mailto:pgruebele@cox.net">pgruebele@cox.net</a>
\************************************************************************************/
#include "_cv.h"
//=====================================================================================
// Implementation for the enhanced calibration object detection
//=====================================================================================
#define MAX_CONTOUR_APPROX 7
typedef struct CvContourEx
{
CV_CONTOUR_FIELDS()
int counter;
}
CvContourEx;
//=====================================================================================
/// Corner info structure
/** This structure stores information about the chessboard corner.*/
typedef struct CvCBCorner
{
CvPoint2D32f pt; // Coordinates of the corner
int row; // Board row index
int count; // Number of neighbor corners
struct CvCBCorner* neighbors[4]; // Neighbor corners
}
CvCBCorner;
//=====================================================================================
/// Quadrangle contour info structure
/** This structure stores information about the chessboard quadrange.*/
typedef struct CvCBQuad
{
int count; // Number of quad neibors
int group_idx; // quad group ID
float edge_len; // quad size characteristic
CvCBCorner *corners[4]; // Coordinates of quad corners
struct CvCBQuad *neighbors[4]; // Pointers of quad neighbors
}
CvCBQuad;
//=====================================================================================
//static CvMat* debug_img = 0;
static int icvGenerateQuads( CvCBQuad **quads, CvCBCorner **corners,
CvMemStorage *storage, CvMat *image, int flags );
static void icvFindQuadNeighbors( CvCBQuad *quads, int quad_count );
static int icvFindConnectedQuads( CvCBQuad *quads, int quad_count,
CvCBQuad **quad_group, int group_idx,
CvMemStorage* storage );
static int icvCheckQuadGroup( CvCBQuad **quad_group, int count,
CvCBCorner **out_corners, CvSize pattern_size );
static int icvCleanFoundConnectedQuads( int quad_count,
CvCBQuad **quads, CvSize pattern_size );
CV_IMPL
int cvFindChessboardCorners( const void* arr, CvSize pattern_size,
CvPoint2D32f* out_corners, int* out_corner_count,
int flags )
{
const int min_dilations = 1;
const int max_dilations = 3;
int found = 0;
CvMat* norm_img = 0;
CvMat* thresh_img = 0;
CvMemStorage* storage = 0;
CvCBQuad *quads = 0, **quad_group = 0;
CvCBCorner *corners = 0, **corner_group = 0;
if( out_corner_count )
*out_corner_count = 0;
CV_FUNCNAME( "cvFindChessBoardCornerGuesses2" );
__BEGIN__;
int quad_count, group_idx, i, dilations;
CvMat stub, *img = (CvMat*)arr;
CV_CALL( img = cvGetMat( img, &stub ));
//debug_img = img;
if( CV_MAT_DEPTH( img->type ) != CV_8U || CV_MAT_CN( img->type ) == 2 )
CV_ERROR( CV_StsUnsupportedFormat, "Only 8-bit grayscale or color images are supported" );
if( pattern_size.width <= 2 || pattern_size.height <= 2 )
CV_ERROR( CV_StsOutOfRange, "pattern should have at least 2x2 size" );
if( !out_corners )
CV_ERROR( CV_StsNullPtr, "Null pointer to corners" );
CV_CALL( storage = cvCreateMemStorage(0) );
CV_CALL( thresh_img = cvCreateMat( img->rows, img->cols, CV_8UC1 ));
if( CV_MAT_CN(img->type) != 1 || (flags & CV_CALIB_CB_NORMALIZE_IMAGE) )
{
// equalize the input image histogram -
// that should make the contrast between "black" and "white" areas big enough
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( flags & CV_CALIB_CB_NORMALIZE_IMAGE )
{
cvEqualizeHist( img, norm_img );
img = norm_img;
}
}
// Try our standard "1" dilation, but if the pattern is not found, iterate the whole procedure with higher dilations.
// This is necessary because some squares simply do not separate properly with a single dilation. However,
// we want to use the minimum number of dilations possible since dilations cause the squares to become smaller,
// making it difficult to detect smaller squares.
for( dilations = min_dilations; dilations <= max_dilations; dilations++ )
{
// convert the input grayscale image to binary (black-n-white)
if( flags & CV_CALIB_CB_ADAPTIVE_THRESH )
{
int block_size = cvRound(MIN(img->cols,img->rows)*0.2)|1;
cvDilate( img, thresh_img, 0, dilations );
// convert to binary
cvAdaptiveThreshold( img, thresh_img, 255,
CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, block_size, 0 );
cvDilate( thresh_img, thresh_img, 0, dilations-1 );
}
else
{
// Make dilation before the thresholding.
// It splits chessboard corners
//cvDilate( img, thresh_img, 0, 1 );
// empiric threshold level
double mean = cvMean( img );
int thresh_level = cvRound( mean - 10 );
thresh_level = MAX( thresh_level, 10 );
cvThreshold( img, thresh_img, thresh_level, 255, CV_THRESH_BINARY );
cvDilate( thresh_img, thresh_img, 0, dilations );
}
// So we can find rectangles that go to the edge, we draw a white line around the image edge.
// Otherwise FindContours will miss those clipped rectangle contours.
// The border color will be the image mean, because otherwise we risk screwing up filters like cvSmooth()...
cvRectangle( thresh_img, cvPoint(0,0), cvPoint(thresh_img->cols-1,
thresh_img->rows-1), CV_RGB(255,255,255), 3, 8);
CV_CALL( quad_count = icvGenerateQuads( &quads, &corners, storage, thresh_img, flags ));
if( quad_count <= 0 )
continue;
// Find quad's neighbors
CV_CALL( icvFindQuadNeighbors( quads, quad_count ));
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 ));
for( group_idx = 0; ; group_idx++ )
{
int count;
CV_CALL( count = icvFindConnectedQuads( quads, quad_count, quad_group, group_idx, storage ));
if( count == 0 )
break;
// If count is more than it should be, this will remove those quads
// which cause maximum deviation from a nice square pattern.
CV_CALL( count = icvCleanFoundConnectedQuads( count, quad_group, pattern_size ));
CV_CALL( count = icvCheckQuadGroup( quad_group, count, corner_group, pattern_size ));
if( count > 0 || (out_corner_count && -count > *out_corner_count) )
{
int n = count > 0 ? pattern_size.width * pattern_size.height : -count;
n = MIN( n, pattern_size.width * pattern_size.height );
// copy corners to output array
for( i = 0; i < n; i++ )
out_corners[i] = corner_group[i]->pt;
if( out_corner_count )
*out_corner_count = n;
if( count > 0 )
{
found = 1;
EXIT;
}
}
}
cvFree( &quads );
cvFree( &corners );
}
__END__;
cvReleaseMemStorage( &storage );
cvReleaseMat( &norm_img );
cvReleaseMat( &thresh_img );
cvFree( &quads );
cvFree( &corners );
cvFree( &quad_group );
cvFree( &corner_group );
return found;
}
// if we found too many connect 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...
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] = ci;
center.x += ci.x;
center.y += ci.y;
}
center.x /= quad_count;
center.y /= quad_count;
// If we still 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]; // temporarily make index 'skip' the same as
centers[skip] = center; // pattern center (so it is not counted for convex hull)
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++ )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -