目的:
在研究坐标映射的相关问题时,遇到棋盘坐标匹配出错的问题。其中涉及到一个关键函数FindChessboardCorners。以下将对其做一定的介绍和分析。
函数介绍:
FindChessboardCorners是opencv的一个函数,可以用来寻找棋盘图的内角点位置。
函数形式
int cvFindChessboardCorners( const void* image, CvSize pattern_size, CvPoint2D32f* corners, int* corner_count=NULL, int flags=CV_CALIB_CB_ADAPTIVE_THRESH );
参数说明
Image:
输入的棋盘图,必须是8位的灰度或者彩色图像。
pattern_size:
棋盘图中每行和每列角点的个数。
Corners:
检测到的角点
corner_count:
输出,角点的个数。如果不是NULL,函数将检测到的角点的个数存储于此变量。
Flags:
各种操作标志,可以是0或者下面值的组合:
CV_CALIB_CB_ADAPTIVE_THRESH -使用自适应阈值(通过平均图像亮度计算得到)将图像转换为黑白图,而不是一个固定的阈值。
CV_CALIB_CB_NORMALIZE_IMAGE -在利用固定阈值或者自适应的阈值进行二值化之前,先使用cvNormalizeHist来均衡化图像亮度。
CV_CALIB_CB_FILTER_QUADS -使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块。
补充说明
函数cvFindChessboardCorners试图确定输入图像是否是棋盘模式,并确定角点的位置。如果所有角点都被检测到且它们都被以一定顺序排布,函数返回非零值,否则在函数不能发现所有角点或者记录它们地情况下,函数返回0。例如一个正常地棋盘图右8x8个方块和7x7个内角点,内角点是黑色方块相互联通的位置。这个函数检测到地坐标只是一个大约的值,如果要精确地确定它们的位置,可以使用函数cvFindCornerSubPix。
函数测试:
测试图像(左)和运行结果(右)
图像均为640*360的8*8黑白格棋盘图,7*7个内点。
- 图1.计算机图像,成功检测出所有49个角点,顺序以行从左上到右下
- 图2.正面实拍图像,检测出48个角点,其中正确47个,错误一个,没有标记顺序,只标记位置
- 图3.正面实拍图像,成功检测出所有49个角点,顺序以行从左上到右下
- 图4.侧面实拍图像,成功检测出所有49个角点,顺序以列从右上到坐下
结果分析:
- 1图与3图,角特性良好,正面拍摄,函数顺利找到角点位置
- 2图中,未检测出的右上角两个角点可能是由于胶带干扰,角特征变的不明显。检测到的错误点是因为背景图像中有黑色物体,导致计算机误认为其为黑色方格。
- 4图中,由于拍摄角度倾斜,棋盘图像发生变形,角点查找顺序发生变化。可以通过重新排列矩阵Corners的大小来得到1图与3图同样的效果
反思与函数应用注意事项:
- pattern_size参数传递内点数,8*8的棋盘只有7*7内点。
- 图像选取应注意减少干扰,例如光照与背景等。
- Corners中的角点坐标顺序排列规律不一定是以行从左上到右下。使用坐标计算映射关系时应提高警惕,对坐标进行重新排列。
#include <iostream>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
using namespace std;
int main( )
{
cout<<"Draw Chess OpenCV!"<<endl;
char* filename="..//image5.jpg";
char* filename2="..//5.jpg";
IplImage* imgRGB = cvLoadImage(filename);
IplImage* imgGrey = cvLoadImage(filename,CV_LOAD_IMAGE_GRAYSCALE);
if (imgGrey==NULL){//image validation
cout<< "No valid image input."<<endl;
char c=getchar();
return 1;
}
//-------find chessboard corners--------------
int corner_row=7;//interior number of row corners.(this can be countered by fingers.)
int corner_col=7;//interior number of column corners.
int corner_n=corner_row*corner_col;
CvSize pattern_size=cvSize(corner_row,corner_col);
// CvPoint2D32f* corners=new CvPoint2D32f[corner_n];
CvPoint2D32f corners[49];
int corner_count;
int found=cvFindChessboardCorners(//returning non-zero means sucess.
imgGrey,// 8-bit single channel greyscale image.
pattern_size,//how many INTERIOR corners in each row and column of the chessboard.
corners,//an array where the corner locations can be recorded.
&corner_count,// optional, if non-NULL, its a point to an integer where the nuber of corners found can be recorded.
// CV_CALIB_CB_ADAPTIVE_THRESH|CV_CALIB_CB_FILTER_QUADS// check page 382-383.
0
);
cout<<"corner_count = "<<corner_count;
//-------Draw the corner pattern-------
cvDrawChessboardCorners(
imgRGB,
pattern_size,
corners,
corner_count,
found
);
cvSaveImage(filename2,imgRGB);
//to summary a bit of findings.
cout<<"found="<<found<<endl;
cout<<"x="<<corners[1].x;
cout<<",y="<<corners[1].y<<endl;
cvNamedWindow("Find and Draw ChessBoard", 0 );
cvShowImage( "Find and Draw ChessBoard", imgRGB );
cvWaitKey(0);
cvReleaseImage(&imgGrey);
cvReleaseImage(&imgRGB);
cvDestroyWindow("Find and Draw ChessBoard");
return 0;
}