目录

  • 问题概览
  • 函数定义
  • 问题排查
  • 解决方案
  • 1.人为添加空白或者将背景变为与图案背景一致
  • 2.使用OpenCV4中的findChessboardCornersSB() 函数
  • 参考引用



问题概览

近期新买的棋盘格标定板到了,开始尝试用之前写的标定程序对相机进行标定,程序中使用的棋盘格角点提取的函数为OpenCV库函数findChessboardCorners(),但在运行过程中发现其中几张拍摄的图片会出现耗时很长的问题,好的图片一两秒算完,异常图片可能就是几百秒开外,典型图片如下图所示,大家有兴趣可以直接搭例子进行测试

opencv 通过 distanceTransform 找中线 opencv findcirclesgrid_特征点

函数定义

bool cv::findChessboardCorners	(	InputArray 	image,
									Size 	patternSize,
									OutputArray 	corners,
									int 	flags = CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE 
								)
  • image:输入图像(格式:cv::Mat)
  • patternSize:特征点的横纵向数量(格式:cv::Size)
  • corners:输出提取到的特征点像素坐标集合(格式:vector< vector< cv::Point2f > >)
  • flags :(默认缺省)标识符,用于附加一些滤波步骤,以帮助找到棋盘上的角点,缺省量为CALIB_CB_ADAPTIVE_THRESH(自适应阈值法)CALIB_CB_NORMALIZE_IMAGE(图像归一化)

问题排查

从图片上看,这张图像标定区域清晰,且角点并不存在遮挡问题,理论上不应该会出现提取不到特征点的问题出现,其次及时我把标识符中所有提升算法鲁棒性和快速性的标志加上,也没有产生很大的作用。直到我开始仔细查阅OpenCV的Manuel的时候,发现其有关标定特征点提取的函数下面都有这么一段话:

The function requires white space (like a square-thick border, the wider the better) around the board to make the detection more robust in various environments.

翻译一下就是说,这几个特征点提取的函数希望被摄的标定图案周围能有较多的白色空白,这样才能保证特征提取函数的鲁棒性。而回看刚刚的图片其实就存在着问题,首先标定板制作本身比较紧凑,图案周边的空白较少;其次该图片图案的左下角贴近图像边缘位置。而这两个因素很大程度上造成了findChessboardCorners()无法从图像上识别到正确的标定图案位置。

对于findChessboardCorners()的这种鲁棒性较差问题,stackoverflow上的大佬做过一个解释,这里我直接搬运了原文,这种空白较少实际上就是造成了第一步出现了很大的计算耗时导致。网上还有一种说法是,findChessboardCorners()中有关特征点搜索的Blob算法比较旧,鲁棒性不是很好,不像findCirclesGrid()圆点特征点提取函数那样有很好的鲁棒性。

Can findChessboardCorners be speeded up?OpenCV findChessboardCorners very slow

Most of the time is spent in checkerboard extraction, which consists (roughly speaking) of three steps: detecting checkerboard corners in the images, refining them to subpixel resolution, and matching them to the nominal checkerboard.
Of the three, the first step is the most expensive, because it involves processing the entire image. So you can gain some time by detecting corners and matching checkerboard at lower resolution (using cv2.findChessboardCorners). You can then rescale their coordinates to full resolution and subpixel-refine them (cv2.find4QuadCornersSubpix) for greater accuracy. The refinement routine works on small regions around the detected corners, so it needs much less memory - which is helpful when working on CPU’s with limited cache size.

解决方案

由于findChessboardCorners()是封装好的函数,我没办法对其进行超时跳出的设计(除非放线程运算,然后对线程做超时处理),因此考虑方案有以下两个:

1.人为添加空白或者将背景变为与图案背景一致

我在实践中采用的人为添加空白这种方式,当然选择删掉跑的慢的标定图片是更好的选择啦。最后实测ok,修改后的标定板长这样

opencv 通过 distanceTransform 找中线 opencv findcirclesgrid_OpenCV_02

2.使用OpenCV4中的findChessboardCornersSB() 函数

这个函数只在OpenCV 4以后的版本出现,对于这个函数的介绍,建议大家阅读这位前辈的opencv4 findChessboardCornersSB 棋盘格角点提取。从Manuel上来看,这个函数在一篇"Accurate Detection and Localization of Checkerboard Corners for Calibration" 的论文基础上,提高了标定算法鲁棒性以及精度,并且直接集成了亚像素提取的步骤(不再需要单独再加一个cornerSubPix()了)

其函数定义如下:

bool cv::findChessboardCornersSB	(	InputArray 		image,
										Size 			patternSize,
										OutputArray 	corners,
										int 			flags,
										OutputArray 	meta 
									)

我实测这个函数确实可以解决刚刚的问题,但这个函数会占用很多算力资源,同时耗时也不算短,利用VS监控运行这个函数期间的内存占用非常惊人,所以还是建议视情况而定。

opencv 通过 distanceTransform 找中线 opencv findcirclesgrid_opencv_03

参考引用

OpenCV Manuel:Camera Calibration and 3D Reconstruction