@ -46,28 +46,26 @@
# include <vector>
# include <vector>
# include <algorithm>
# include <algorithm>
//#define DEBUG_WINDOWS
using namespace cv ;
using namespace std ;
# if defined(DEBUG_WINDOWS)
static void icvGetQuadrangleHypotheses ( const std : : vector < std : : vector < cv : : Point > > & contours , const std : : vector < cv : : Vec4i > & hierarchy , std : : vector < std : : pair < float , int > > & quads , int class_id )
# include "opencv2 / opencv_modules.hpp"
# ifdef HAVE_OPENCV_HIGHGUI
# include "opencv2 / highgui.hpp"
# else
# undef DEBUG_WINDOWS
# endif
# endif
int cvCheckChessboardBinary ( IplImage * src , CvSize size ) ;
static void icvGetQuadrangleHypotheses ( CvSeq * contours , std : : vector < std : : pair < float , int > > & quads , int class_id )
{
{
const float min_aspect_ratio = 0.3f ;
const float min_aspect_ratio = 0.3f ;
const float max_aspect_ratio = 3.0f ;
const float max_aspect_ratio = 3.0f ;
const float min_box_size = 10.0f ;
const float min_box_size = 10.0f ;
for ( CvSeq * seq = contours ; seq ! = NULL ; seq = seq - > h_next )
typedef std : : vector < std : : vector < cv : : Point > > : : const_iterator iter_t ;
iter_t i ;
for ( i = contours . begin ( ) ; i ! = contours . end ( ) ; + + i )
{
{
CvBox2D box = cvMinAreaRect2 ( seq ) ;
const iter_t : : difference_type idx = i - contours . begin ( ) ;
if ( hierarchy . at ( idx ) [ 3 ] ! = - 1 )
continue ; // skip holes
const std : : vector < cv : : Point > & c = * i ;
cv : : RotatedRect box = cv : : minAreaRect ( c ) ;
float box_size = MAX ( box . size . width , box . size . height ) ;
float box_size = MAX ( box . size . width , box . size . height ) ;
if ( box_size < min_box_size )
if ( box_size < min_box_size )
{
{
@ -98,113 +96,98 @@ inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, in
return p1 . first < p2 . first ;
return p1 . first < p2 . first ;
}
}
// does a fast check if a chessboard is in the input image. This is a workaround to
static void fillQuads ( Mat & white , Mat & black , double white_thresh , double black_thresh , vector < pair < float , int > > & quads )
// a problem of cvFindChessboardCorners being slow on images with no chessboard
// - src: input image
// - size: chessboard size
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// 0 if there is no chessboard, -1 in case of error
int cvCheckChessboard ( IplImage * src , CvSize size )
{
{
if ( src - > nChannels > 1 )
Mat thresh ;
{
{
cvError ( CV_BadNumChannels , " cvCheckChessboard " , " supports single-channel images only " ,
vector < vector < Point > > contours ;
__FILE__ , __LINE__ ) ;
vector < Vec4i > hierarchy ;
threshold ( white , thresh , white_thresh , 255 , THRESH_BINARY ) ;
findContours ( thresh , contours , hierarchy , RETR_CCOMP , CHAIN_APPROX_SIMPLE ) ;
icvGetQuadrangleHypotheses ( contours , hierarchy , quads , 1 ) ;
}
}
if ( src - > depth ! = 8 )
{
{
cvError ( CV_BadDepth , " cvCheckChessboard " , " supports depth=8 images only " ,
vector < vector < Point > > contours ;
__FILE__ , __LINE__ ) ;
vector < Vec4i > hierarchy ;
threshold ( black , thresh , black_thresh , 255 , THRESH_BINARY_INV ) ;
findContours ( thresh , contours , hierarchy , RETR_CCOMP , CHAIN_APPROX_SIMPLE ) ;
icvGetQuadrangleHypotheses ( contours , hierarchy , quads , 0 ) ;
}
}
}
const int erosion_count = 1 ;
static bool checkQuads ( vector < pair < float , int > > & quads , const cv : : Size & size )
const float black_level = 20.f ;
{
const float white_level = 130.f ;
const size_t min_quads_count = size . width * size . height / 2 ;
const float black_white_gap = 70.f ;
std : : sort ( quads . begin ( ) , quads . end ( ) , less_pred ) ;
# if defined(DEBUG_WINDOWS)
cvNamedWindow ( " 1 " , 1 ) ;
cvShowImage ( " 1 " , src ) ;
cvWaitKey ( 0 ) ;
# endif //DEBUG_WINDOWS
CvMemStorage * storage = cvCreateMemStorage ( ) ;
IplImage * white = cvCloneImage ( src ) ;
IplImage * black = cvCloneImage ( src ) ;
cvErode ( white , white , NULL , erosion_count ) ;
// now check if there are many hypotheses with similar sizes
cvDilate ( black , black , NULL , erosion_count ) ;
// do this by floodfill-style algorithm
IplImage * thresh = cvCreateImage ( cvGetSize ( src ) , IPL_DEPTH_8U , 1 ) ;
const float size_rel_dev = 0.4f ;
int result = 0 ;
for ( size_t i = 0 ; i < quads . size ( ) ; i + + )
for ( float thresh_level = black_level ; thresh_level < white_level & & ! result ; thresh_level + = 20.0f )
{
{
cvThreshold ( white , thresh , thresh_level + black_white_gap , 255 , CV_THRESH_BINARY ) ;
size_t j = i + 1 ;
for ( ; j < quads . size ( ) ; j + + )
# if defined(DEBUG_WINDOWS)
cvShowImage ( " 1 " , thresh ) ;
cvWaitKey ( 0 ) ;
# endif //DEBUG_WINDOWS
CvSeq * first = 0 ;
std : : vector < std : : pair < float , int > > quads ;
cvFindContours ( thresh , storage , & first , sizeof ( CvContour ) , CV_RETR_CCOMP ) ;
icvGetQuadrangleHypotheses ( first , quads , 1 ) ;
cvThreshold ( black , thresh , thresh_level , 255 , CV_THRESH_BINARY_INV ) ;
# if defined(DEBUG_WINDOWS)
cvShowImage ( " 1 " , thresh ) ;
cvWaitKey ( 0 ) ;
# endif //DEBUG_WINDOWS
cvFindContours ( thresh , storage , & first , sizeof ( CvContour ) , CV_RETR_CCOMP ) ;
icvGetQuadrangleHypotheses ( first , quads , 0 ) ;
const size_t min_quads_count = size . width * size . height / 2 ;
std : : sort ( quads . begin ( ) , quads . end ( ) , less_pred ) ;
// now check if there are many hypotheses with similar sizes
// do this by floodfill-style algorithm
const float size_rel_dev = 0.4f ;
for ( size_t i = 0 ; i < quads . size ( ) ; i + + )
{
{
size_t j = i + 1 ;
if ( quads [ j ] . first / quads [ i ] . first > 1.0f + size_rel_dev )
for ( ; j < quads . size ( ) ; j + + )
{
{
if ( quads [ j ] . first / quads [ i ] . first > 1.0f + size_rel_dev )
break ;
{
break ;
}
}
}
}
if ( j + 1 > min_quads_count + i )
if ( j + 1 > min_quads_count + i )
{
// check the number of black and white squares
std : : vector < int > counts ;
countClasses ( quads , i , j , counts ) ;
const int black_count = cvRound ( ceil ( size . width / 2.0 ) * ceil ( size . height / 2.0 ) ) ;
const int white_count = cvRound ( floor ( size . width / 2.0 ) * floor ( size . height / 2.0 ) ) ;
if ( counts [ 0 ] < black_count * 0.75 | |
counts [ 1 ] < white_count * 0.75 )
{
{
// check the number of black and white squares
continue ;
std : : vector < int > counts ;
countClasses ( quads , i , j , counts ) ;
const int black_count = cvRound ( ceil ( size . width / 2.0 ) * ceil ( size . height / 2.0 ) ) ;
const int white_count = cvRound ( floor ( size . width / 2.0 ) * floor ( size . height / 2.0 ) ) ;
if ( counts [ 0 ] < black_count * 0.75 | |
counts [ 1 ] < white_count * 0.75 )
{
continue ;
}
result = 1 ;
break ;
}
}
return true ;
}
}
}
}
return false ;
}
// does a fast check if a chessboard is in the input image. This is a workaround to
// a problem of cvFindChessboardCorners being slow on images with no chessboard
// - src: input image
// - size: chessboard size
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// 0 if there is no chessboard, -1 in case of error
int cvCheckChessboard ( IplImage * src , CvSize size )
{
cv : : Mat img = cv : : cvarrToMat ( src ) ;
return checkChessboard ( img , size ) ;
}
int checkChessboard ( const cv : : Mat & img , const cv : : Size & size )
{
CV_Assert ( img . channels ( ) = = 1 & & img . depth ( ) = = CV_8U ) ;
cvReleaseImage ( & thresh ) ;
const int erosion_count = 1 ;
cvReleaseImage ( & white ) ;
const float black_level = 20.f ;
cvReleaseImage ( & black ) ;
const float white_level = 130.f ;
cvReleaseMemStorage ( & storage ) ;
const float black_white_gap = 70.f ;
Mat white ;
Mat black ;
erode ( img , white , Mat ( ) , Point ( - 1 , - 1 ) , erosion_count ) ;
dilate ( img , black , Mat ( ) , Point ( - 1 , - 1 ) , erosion_count ) ;
int result = 0 ;
for ( float thresh_level = black_level ; thresh_level < white_level & & ! result ; thresh_level + = 20.0f )
{
vector < pair < float , int > > quads ;
fillQuads ( white , black , thresh_level + black_white_gap , thresh_level , quads ) ;
if ( checkQuads ( quads , size ) )
result = 1 ;
}
return result ;
return result ;
}
}
@ -214,90 +197,29 @@ int cvCheckChessboard(IplImage* src, CvSize size)
// - size: chessboard size
// - size: chessboard size
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// 0 if there is no chessboard, -1 in case of error
// 0 if there is no chessboard, -1 in case of error
int cvC heckChessboardBinary ( IplImage * src , CvSize size )
int checkChessboardBinary ( const cv : : Mat & img , const cv : : Size & size )
{
{
if ( src - > nChannels > 1 )
CV_Assert ( img . channels ( ) = = 1 & & img . depth ( ) = = CV_8U ) ;
{
cvError ( CV_BadNumChannels , " cvCheckChessboard " , " supports single-channel images only " ,
__FILE__ , __LINE__ ) ;
}
if ( src - > depth ! = 8 )
{
cvError ( CV_BadDepth , " cvCheckChessboard " , " supports depth=8 images only " ,
__FILE__ , __LINE__ ) ;
}
CvMemStorage * storage = cvCreateMemStorage ( ) ;
Mat white = img . clone ( ) ;
Mat black = img . clone ( ) ;
IplImage * white = cvCloneImage ( src ) ;
IplImage * black = cvCloneImage ( src ) ;
IplImage * thresh = cvCreateImage ( cvGetSize ( src ) , IPL_DEPTH_8U , 1 ) ;
int result = 0 ;
int result = 0 ;
for ( int erosion_count = 0 ; erosion_count < = 3 ; erosion_count + + )
for ( int erosion_count = 0 ; erosion_count < = 3 ; erosion_count + + )
{
{
if ( 1 = = result )
if ( 1 = = result )
break ;
break ;
if ( 0 ! = erosion_count ) // first iteration keeps original images
{
cvErode ( white , white , NULL , 1 ) ;
cvDilate ( black , black , NULL , 1 ) ;
}
cvThreshold ( white , thresh , 128 , 255 , CV_THRESH_BINARY ) ;
CvSeq * first = 0 ;
if ( 0 ! = erosion_count ) // first iteration keeps original images
std : : vector < std : : pair < float , int > > quads ;
{
cvFindContours ( thresh , storage , & first , sizeof ( CvContour ) , CV_RETR_CCOMP ) ;
erode ( white , white , Mat ( ) , Point ( - 1 , - 1 ) , 1 ) ;
icvGetQuadrangleHypotheses ( first , quads , 1 ) ;
dilate ( black , black , Mat ( ) , Point ( - 1 , - 1 ) , 1 ) ;
}
cvThreshold ( black , thresh , 128 , 255 , CV_THRESH_BINARY_INV ) ;
cvFindContours ( thresh , storage , & first , sizeof ( CvContour ) , CV_RETR_CCOMP ) ;
icvGetQuadrangleHypotheses ( first , quads , 0 ) ;
const size_t min_quads_count = size . width * size . height / 2 ;
std : : sort ( quads . begin ( ) , quads . end ( ) , less_pred ) ;
// now check if there are many hypotheses with similar sizes
// do this by floodfill-style algorithm
const float size_rel_dev = 0.4f ;
for ( size_t i = 0 ; i < quads . size ( ) ; i + + )
{
size_t j = i + 1 ;
for ( ; j < quads . size ( ) ; j + + )
{
if ( quads [ j ] . first / quads [ i ] . first > 1.0f + size_rel_dev )
{
break ;
}
}
if ( j + 1 > min_quads_count + i )
vector < pair < float , int > > quads ;
{
fillQuads ( white , black , 128 , 128 , quads ) ;
// check the number of black and white squares
if ( checkQuads ( quads , size ) )
std : : vector < int > counts ;
result = 1 ;
countClasses ( quads , i , j , counts ) ;
const int black_count = cvRound ( ceil ( size . width / 2.0 ) * ceil ( size . height / 2.0 ) ) ;
const int white_count = cvRound ( floor ( size . width / 2.0 ) * floor ( size . height / 2.0 ) ) ;
if ( counts [ 0 ] < black_count * 0.75 | |
counts [ 1 ] < white_count * 0.75 )
{
continue ;
}
result = 1 ;
break ;
}
}
}
}
cvReleaseImage ( & thresh ) ;
cvReleaseImage ( & white ) ;
cvReleaseImage ( & black ) ;
cvReleaseMemStorage ( & storage ) ;
return result ;
return result ;
}
}