AprilTag视觉定位Python实现
AprilTag是一个视觉基准库,在AR,机器人,相机校准领域广泛使用。通过特定的标志(与二维码相似,但是降低了复杂度以满足实时性要求),可以快速地检测标志,并计算相对位置。
官网:https://april.eecs.umich.edu/software/apriltag.html
AprilTag过程
AprilTag内容主要包含三个步骤。
- 第一步是如何根据梯度检测出图像中的各种边缘。
- 第二步即如何在边缘图像中找出需要的四边形图案并进行筛选,AprilTag尽可能的对检测出的边缘检测,首先剔除非直线边缘,在直线边缘进行邻接边缘查找,最终若形成闭环则为检测到一个四边形。
- 最后一个步便是如何进行二维码编码和二维码解码,编码方式分为三种,其黑边色块长度分别为8,7,6三个色块长度,对于解码内容,要在检测到的四边形内生成点阵列用于计算每色块的值,再根据局部二值模式(Local Binary Patterns)构造简单分类器对四边形内的色块进行分类,将正例色块编码为1将负例色块编码为0,就可以得到该二维码的编码。得到编码以后再与已知库内的编码进行匹配,确定解码出的二维码是否为正确。
AprilTag边缘检测
对于第一步来说,最主要的功能就是寻找图像中的边缘,原文章使用的是局部二值模式,但是实际使用中,我建议可以使用Canny算子方法,虽然检测出的边缘会减少,但是相应的也降低了图像中的噪点。
四边形检测
对于得到的二值化边缘图像,需要对其进行多边形分析,首先运用
边缘结构分析(Topological structural analysis of digitized binary images)查找多边形,该方法用于确定二值图像边缘的围绕关系,即确定外边缘,内边缘以及它们之间嵌套关系,确定的边缘都与原图都有相对应的关系,因此完全可以用边缘来表示原图。该算法采用编码的思想,首先赋予不同的边缘以不同的编码值,这样方便确认多边形的层次关系。算法从起点开始,编辑边缘的像素,寻找该起始点同类的边缘点,当扫描至起始点时,该多边形闭环形成,切换至下一个起始点重复该操作,直到所有的二值点都被遍历,排除边数小于4的,将满足条件的进行进一步处理。
利用多边形凸包寻找算法(Finding the convex hull of a simple polygon),计算每一个多边形本身的凸包,并求凸包及其多边形的面积,将两个面积进行比较,当多边形面积比凸包大时将该多边形排除,这样子可以有效的排除非凸多边形,以及不满足多边形面积与凸包面积之比大于0.8的多边形,保留满足条件的其余多边形。
对于最终满足条件的多边形,使用道格拉斯-普克算法(Douglas-Peucker algorithm)进行四边形逼近。
1 寻找曲线的两个端点A,B,作曲线间的弦即线段AB。
2 求线段AB到曲线上的距离最远的点C,并求其间的距离d。
3 将该距离d与提前设定的阈值进行比较,如果小于阈值,就可直接将该直线近似为曲线,该段曲线处理完毕。
4 如果距离d大于给定的阈值,用点C将曲线分割成为AC与BC,并分别对两段取线进行1至3的处理。
5 当所有曲线都处理完毕时,依次连接各个分割点形成的折线,这样就可将处理前的曲线近似成为直线,排除该情况下顶点数不为4的多边形。
图中表示的就是经过筛选后得到四边形的整个过程。
编码与解码
在上一步检测出的四边形并不一定满足我们的要求,所以需要对上一步得到的四边形进行编码,匹配,检查。对于得到的四边形,首先要确定其内部的点阵,上一步的四边形是以四个顶点的形式进行存储的,根据这四个点可以确定其四边形内的点阵的具体坐标,不同编码方式其点阵的个数不相同(根据选择编码方式不同有可选),其编码方式由使用者对物体进行标定时确定,根据编码方式的不同,生成不同的内点坐标。以图\ref{矩形编码}所示举例,该二维码的边长为6个黑边,其中的有效编码区域是除去外部黑边的的部分,该部分区域有黑有白,用于二维码的编码。
具体的编码方式如下:
在确定的四边形中明确点阵坐标,在灰度图像中提取点阵的最外围一圈的像素的平均值Value1,再提取点阵次外外围一圈的像素的平均值Value2,根据AprilTag本身的二维码库的设计,四边形最外一层的虽有点的灰度值都是黑色的,而此外层的灰度值是黑色与白色混合,因此在同一光照环境下,Value1和Value2有明显的阈值分界线,确定阈值为Value1与Value2的均值,重新遍历整个点阵所有点坐标的像素值高于阈值的部分编码为0,低于阈值的部分编码为1,见图\ref{图片点阵}。如此以来,从第一行开始进行编码,直至整个点阵被编码完成,将编码排列起来会得到一串二进制码,该二进制码的长度由具体的编码方式决定(分为36,25,16三种),每个四边形都能得到一串二进制码,该码表示该状态下二维码的编码,错误的四边形往往会生成错误的编码,错误编码无法在阈值范围内匹配到相应的ID,故可以轻松的排除错误编码的四边形,对得到的四边形进行编码并进行匹配校验是重要的一个过程。
进一步确定编码是否可靠,要与已知的编码库进行匹配,由于观察到的编码存在旋转的情况,所以应先将得到的编码进行三次90°旋转共得到四个方向上的编码,再逐一与编码库进行比较,求解编码之间的汉明距离(Hamming Distance)\cite{Hamming},汉明距离是表示两个二进制编码之间的相似程度的一种距离,汉明距离是两个长二进制串之间位数不相同的个数。当观察编码与已知库里的某一编码的汉明距离小于给定的阈值时(一般为2),就确定该观察编码的ID为与之匹配的编码库里的ID,并记录下该汉明距离,若编码库并没有一个与之匹配就确定该观察编码有误,则舍弃该编码对应的四边形。
经过上述的筛选与验证,至此观察编码的ID以及编码的旋转已经可以确定,便可计算该二维码的其他参数,包括二维码相对于原二维码的旋转方位,观察二维码与匹配二维码的相似程度,其中主要为二维码的单线性变换矩阵(Homography Matrix)\cite{SukthankarStockton},在计算机领域中的针孔相机模型下,同一个张图形在不同空间内都有相关性,单线性变换就是将这个图片在该空间下映射至另一个与之对应的空间下,单线性变化矩阵就是这种映射矩阵且可分解出寻转矩阵与平移矩阵,根据上一个步骤求出的四个顶点的坐标即可求出相对于原始坐标的单线性变换矩阵,利用单线性变换矩阵结合多相机的信息,可以得到二维码的相关姿势信息。分解该矩阵就可求出旋转变换向量rvec以及平移变换向量tvec,对于得到的旋转向量,可以根据该向量求得二维码的正确姿势。
实现代码
这个算法是一个开源算法。
原始C代码地址为:https://april.eecs.umich.edu/software/apriltag
我个人将它重构为C#代码:https://github.com/BlackJocker1995/Apriltagcsharp
python代码:https://github.com/BlackJocker1995/Apriltag_python