opencv角点检测、棋盘格检测、亚像素cvFindCornerSubPix()
主要问题:
- 函数参数设置
- 针对图像的对比度光照模糊等问题
- 最终检测的角点的顺序
角点检测:
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; int main(int argc, char **argv) { IplImage* image; IplImage* colorImg; const char *filename ="test.bmp"; if (argc == 2) { image = cvLoadImage(argv[1], 0); }else { image = cvLoadImage(filename,0); } colorImg = cvCreateImage(cvGetSize(image),8,3); cvCvtColor(image, colorImg, CV_GRAY2BGR); IplImage* eig = cvCreateImage( cvGetSize(image), 32, 1 ); IplImage* temp = cvCreateImage( cvGetSize(image), 32, 1 ); double quality = 0.0009;//严格程度,1最严格 0最不严格 默认0.1 double min_distance = 5;//点太近则.. CvPoint2D32f corners[400];//根据实际需要设置 int count=400;//检测点的上限? cvGoodFeaturesToTrack( image, eig, temp, corners, &count, quality, min_distance, 0, 3, 0, 0.04 ); for (int i=0; i<count; i++) { //cvCircle(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y)),0,CV_RGB(255,0,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x-7), cvRound(corners[i].y)),cvPoint(cvRound(corners[i].x+7), cvRound(corners[i].y)),CV_RGB(255,0,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y-7)),cvPoint(cvRound(corners[i].x), cvRound(corners[i].y+7)),CV_RGB(255,0,0), 1,8,0); } cvShowImage("rst", colorImg); cvWaitKey(); cvReleaseImage( &eig ); cvReleaseImage( &temp ); cvReleaseImage( &colorImg); cvReleaseImage( &image); return 0; }
需要调参
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; int main(int argc, char **argv) { IplImage* image; IplImage* colorImg; const char *filename ="lines.bmp"; if (argc == 2) { image = cvLoadImage(argv[1], 0); }else { image = cvLoadImage(filename,0); } colorImg = cvCreateImage(cvGetSize(image),8,3); cvCvtColor(image, colorImg, CV_GRAY2BGR); CvSize pattern_size; pattern_size.height = 6; pattern_size.width = 8; CvPoint2D32f *corners = new CvPoint2D32f[pattern_size.height*pattern_size.width]; int corner_count; cvFindChessboardCorners(image, pattern_size, corners, &corner_count, 0); for (int i=0; i<corner_count; i++) { //cvCircle(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y)),0,CV_RGB(255,0,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x-7), cvRound(corners[i].y)),cvPoint(cvRound(corners[i].x+7), cvRound(corners[i].y)),CV_RGB(0,255,0), 2,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y-7)),cvPoint(cvRound(corners[i].x), cvRound(corners[i].y+7)),CV_RGB(0,255,0), 2,8,0); } //cvSaveImage("1.bmp",colorImg); //cout<<corner_count<<endl; cvShowImage("detect", colorImg); cvWaitKey(); cvReleaseImage(&colorImg); cvReleaseImage(&image); return 0; }
直接检测出棋盘格的角点,但是精度上仍有待提高
使用子像素进一步优化检测结果:
#include <iostream> #include <opencv2/opencv.hpp> using namespace std; int main(int argc, char **argv) { IplImage* image; IplImage* colorImg; const char *filename ="E:\\3D APPLICATION\\LINES\\lines061_1.bmp"; if (argc == 2) { image = cvLoadImage(argv[1], 0); }else { image = cvLoadImage(filename,0); } colorImg = cvCreateImage(cvGetSize(image),8,3); cvCvtColor(image, colorImg, CV_GRAY2BGR); CvSize pattern_size; pattern_size.height = 6; pattern_size.width = 8; int win_size = 10; CvPoint2D32f *corners = new CvPoint2D32f[pattern_size.height*pattern_size.width]; int corner_count; cvFindChessboardCorners(image, pattern_size, corners, &corner_count, 0); for (int i=0; i<corner_count; i++) { //cvCircle(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y)),0,CV_RGB(0,255,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x-7), cvRound(corners[i].y)),cvPoint(cvRound(corners[i].x+7), cvRound(corners[i].y)),CV_RGB(0,255,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y-7)),cvPoint(cvRound(corners[i].x), cvRound(corners[i].y+7)),CV_RGB(0,255,0), 1,8,0); } cvFindCornerSubPix( image, corners, corner_count, cvSize(win_size,win_size), cvSize(-1,-1), cvTermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS,20,0.03)); for (int i=0; i<corner_count; i++) { //cvCircle(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y)),0,CV_RGB(255,0,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x-5), cvRound(corners[i].y)),cvPoint(cvRound(corners[i].x+5), cvRound(corners[i].y)),CV_RGB(255,0,0), 1,8,0); cvLine(colorImg, cvPoint(cvRound(corners[i].x), cvRound(corners[i].y-5)),cvPoint(cvRound(corners[i].x), cvRound(corners[i].y+5)),CV_RGB(255,0,0), 1,8,0); } cvSaveImage("1.bmp",colorImg); cout<<corner_count<<endl; cvShowImage("detect", colorImg); cvWaitKey(); cvReleaseImage(&colorImg); cvReleaseImage(&image); return 0; }
绿色是cvFindChessboardCorners() 红色是cvFindCornerSubPix()
ps:对于不同的棋盘格图像(对比度、离焦模糊等)、拍摄环境的光照等因素会导致角点检测的成功率,通过修改控制参数进行优化
参数设置参考:
http://blog.csdn.net/will7451/article/details/6711070
http://blog.csdn.net/hx1298234467/article/details/50791007
引用如下:
---------------------------------------------------------------------------------------
主要函数介绍:
findChessboardCorners()
- 功能:寻找棋盘图的内角点位置
- 函数形式:
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 - 使用其他的准则(如轮廓面积,周长,方形形状)来去除在轮廓检测阶段检测到的错误方块
- 返回值:如果找到角点返回1,没有找到返回0;
cornerSubPix
- 功能:在角点检测中精确化角点位置
- 函数原型:
void cornerSubPix(InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria);
C: void cvFindCornerSubPix(const CvArr* image, CvPoint2D32f* corners, int count, CvSize win, CvSize zero_zone, CvTermCriteria criteria); - 参数:
- image:输入图像
- corners:输入角点的初始坐标以及精准化后的坐标用于输出
- winSize:搜索窗口边长的一半,例如如果winSize=Size(5,5),则一个大小为:的搜索窗口将被使用。
- zeroZone:搜索区域中间的dead region边长的一半,有时用于避免自相关矩阵的奇异性。如果值设为(-1,-1)则表示没有这个区域。
- criteria:角点精准化迭代过程的终止条件。也就是当迭代次数超过criteria.maxCount,或者角点位置变化小于criteria.epsilon时,停止迭代过程。
-
CvTermCriteria 类:迭代算法的终止准则
- 原型:
- `typedef struct CvTermCriteria
{
int type; /* CV_TERMCRIT_ITER 和CV_TERMCRIT_EPS二值之一,或者二者的组合 */
int max_iter; /* 最大迭代次数 */
double epsilon; /* 结果的精确性 */
- `typedef struct CvTermCriteria
- 宏定义:
- CV_TERMCRIT_ITER:代终止条件为达到最大迭代次数终止
- CV_TERMCRIT_EPS:迭代到阈值终止
- 原型:
---------------------------------------------------------------------------------------------------
详细的chessboard检测过程分析:
(icvOrderFoundConnectedQuads)
http://blog.csdn.net/b5w2p0/article/details/18446961
角点相互之间顺序的问题,“corner order”问题:
https://stackoverflow.com/questions/19024695/opencv-2d-3d-point-correspondence-using-chessboard
摘抄:
|
How is OpenCV supposed to know that? If you rotate your board by 90 degrees, the chessboard looks exactly the same. You could try to use a non-square chessboard. That would be less ambiguous. If that is not an option, you need to come up with your own heuristic, which of the 4 possible rotations is the correct one and reorder the points accordingly. – sietschie Sep 26 '13 at 13:21
|
||
|
Thank you for your reply but the problem is that I'm not even rotating my board by 90°, I'm just moving my chessboard horizontally and it seems the green point is changing. – Gusta Sep 26 '13 at 14:32
|
||
|
I did not assume that. But in your two images you are tilting it a little bit. And with a square board OpenCV cannot distinguish between a 10 degrees rotation in one direction or an 80 degree rotation in the other direction. One could of course assume that the rotation change between images should be minimal. But OpenCV processes each image independently and therefore cannot take such things into account. But you could by doing some postprocessing of the results. Or you could try a non-square board first. Just to check. – sietschie Sep 26 '13 at 15:38
|
||
|
You were right, I changed my chessboard for a non-square one and it fixed my problem :) Thank you very much ! – Gusta Sep 26 '13 at 17:37
|
要么换成非刚好是正方形的棋盘格,要么自己对检测到的角点添加后处理。