主要内容

使用AprilTag标记的相机校准

AprilTags被广泛应用于物体检测、定位和摄像机标定[1]的目标中作为视觉标记。AprilTags类似于QR码,但被设计为编码更少的数据,因此可以更快地解码,这在实时机器人应用中非常有用。

使用AprilTags作为校准模式的优点包括更大的特征点检测,以及一致的、可重复的检测。本例使用readAprilTag功能来检测和定位AprilTags在校准模式。的readAprilTag函数支持所有官方标万博1manbetx签家族。该示例还使用了额外的计算机视觉工具箱™功能来执行端到端摄像机校准。默认的棋盘图案被等距的AprilTags网格所取代。有关使用棋盘图案进行校准的示例,请参阅使用单相机校准器应用程序

这个例子展示了如何使用AprilTags编程校准相机,并使用camera Calibrator应用程序:

使用功能接口的相机校准

步骤1:生成校准图案

下载并准备标签图像

预先生成的标签,所有支持的家庭可以从万博1manbetx在这里使用web浏览器或运行以下代码:

downloadURL =“https://github.com/AprilRobotics/apriltag-imgs/archive/master.zip”;dataFolder = fullfile(tempdir,“apriltag-imgs”, filesep);选项= weboptions(“超时”、正);zipFileName = fullfile(数据文件夹,“apriltag-imgs-master.zip”);folderExists = exist(数据文件夹“dir”);在临时目录中创建一个文件夹来保存下载的文件。如果~ folderExists mkdir (dataFolder);disp ("下载apriltag-imgs-master.zip (60.1 MB)…") websave (zipFileName downloadURL选项);提取下载文件的内容。disp (“提取apriltag-imgs-master.zip……”)解压缩(zipFileName dataFolder);结束
下载apriltag-imgs-master.zip (60.1 MB)…
提取apriltag-imgs-master.zip……

helperGenerateAprilTagPattern函数可用于使用标签图像为标签的特定排列生成校准目标。模式图像包含在calibPattern,可以用来打印图案(来自MATLAB)。该示例使用tag36h11族,它在检测性能和假阳性检测的鲁棒性之间提供了合理的权衡。

设置校准模式的属性。tagArrangement = [5,8];tagFamily =“tag36h11”使用AprilTags生成校准模式。tagImageFolder = fullfile(数据文件夹,“apriltag-imgs-master”, tagFamily);imdsTags =图像数据存储(tagImageFolder);calibPattern = helperGenerateAprilTagPattern(imdsTags,tagArrangement,tagFamily);

使用readAprilTag函数在此模式上执行的检测结果是将各个标记的角位置分组在一起。的helperAprilTagToCheckerLocations函数可用于将此排列转换为列主排列,例如棋盘。

在校准模式中读取和定位标签。[tagIds, tagLocs] = readAprilTag(calibPattern,tagFamily);根据标签的ID值对标签进行排序。[~, sortIdx] = sort(tagIds);tagLocs = tagLocs(:,:,sortIdx);将标签角位置重新塑造为m × 2数组。tagLocs =重塑(permute(tagLocs,[1,3,2]),[],2);将AprilTag角位置转换为棋盘角位置。checkerIdx = helperAprilTagToCheckerLocations(tagArrangement);imagePoints = tagLocs(checkerIdx(:),:);显示角落位置。图;imshow (calibPattern);持有情节(imagePoints (: 1) imagePoints (:, 2),“ro - - - - - -”MarkerSize = 15)

为校准准备图像

在准备校准图像时需要注意以下几点:

  • 在本例中,当图案打印在纸张上时,请考虑将其打印在保持平整的表面上,并且不会因潮湿等原因而变形。

  • 由于校准程序假设图案是平面的,图案中的任何缺陷(例如不均匀的表面)都会降低校准的精度。

  • 校准过程需要至少2张图案的图像,但使用10到20张图像会产生更准确的结果。

  • 捕获模式的各种图像,使模式填充图像的大部分,从而覆盖整个视野。例如,为了最好地捕捉镜头失真,在图像帧的所有边缘都有图案的图像。

  • 确保图案在捕获的图像中是完全可见的,因为部分可见图案的图像将被拒绝。

  • 有关准备校准模式图像的详细信息,请参见准备相机和捕捉图像

calibImages.png

步骤2:检测和本地化AprilTags

helperDetectAprilTagCorners函数,包含在示例的末尾,用于检测和本地化捕获图像中的标记,并将它们以棋盘式方式排列,以用作校准过程中的关键点。

创建一个imageDatastore对象来存储捕获的图像。imdsCalib = imageDatastore(“aprilTagCalibImages /”);从图像中检测校准模式。[imagePoints,boardSize] = helperDetectAprilTagCorners(imdsCalib,tagArrangement,tagFamily);

步骤3:为校准模式生成世界点

生成的AprilTag模式是这样的,标签是棋盘式的,因此上面确定的对应图像坐标的世界坐标(inimagePoints)可使用generateCheckerboardPoints函数。

在这里,正方形的大小被标签的大小所取代,而板子的大小则是从上一步得到的。在标签一侧的外黑边之间测量标签大小。

AprilTagSize.png

为模式生成世界点坐标。tagSize = 16;%(毫米)worldPoints = generateCheckerboardPoints(boardSize, tagSize);

第四步:估计相机参数

利用图像与世界点的对应关系,利用函数估计相机参数estimateCameraParameters函数。

确定图像的大小。I = readimage(imdsCalib,1);imageSize = size(I,1:2);估计相机参数。params = estimatecamerparameters (imagePoints,worldPoints,ImageSize= ImageSize);

可视化校准的精度和外部相机参数。在捕获的图像中显示校准模式的平面。

显示重投影错误。图showReprojectionErrors (params)

显示外部信息。图showExtrinsics (params)

检测检测到的图像点和重投影点的位置,这些点是用估计的摄像机参数得到的。

读取校准图像。I = readimage(imdsCalib,10);为检测到的点和重新投影的点插入标记。I = insertMarker(I,imagePoints(:,:,10),“o”颜色=“g”、大小= 5);I = insertMarker(I,params.ReprojectedPoints(:,:,10),“x”颜色=“r”、大小= 5);显示图像。图imshow(我)

使用其他校准模式

虽然本例在校准模式中使用AprilTags标记,但同样的工作流程也可以扩展到其他平面模式。的estimateCameraParameters用于获取摄像机参数要求:

  • imagePoints:从捕获的图像中获得的图像坐标中的校准模式的关键点。

  • worldPoints:校准图中关键点对应的世界点坐标。

如果有一种方法可以获得这些关键点,那么校准工作流程的其余部分保持不变。

集成AprilTag校准模式支持到相机校准器应用程序万博1manbetx

为了使用方便,以上工作流程也可以集成到Camera Calibrator app中。整体工作流程不变,步骤如下:

1.使用AprilTags添加图像。

2.为AprilTags导入一个自定义模式检测器类。探测器必须做到以下几点:

  • 检测并本地化AprilTags

  • 为校准模式生成世界点

3.估计相机参数。

使用AprilTags添加图像

打开相机校准器应用程序:

  • MATLAB工具条:在应用程序选项卡,在图像处理与计算机视觉“,部分,单击相机校准器图标。

  • MATLAB命令提示符:输入使用单相机校准器应用程序

校准选项卡,在文件部分中,点击添加图片,然后选择从文件.您可以通过单击从多个文件夹中添加图像添加图片对于每个文件夹。我们将重复使用相同的图像以上.您将需要至少2张图像进行相机校准。添加图像后,将出现以下UI:

UI1.png

扩大定制模式面板,以查看更多选项。

UI2.png

导入自定义模式检测器类

上面的UI显示了一个用于模式选择的下拉列表。默认情况下,应用程序不包括AprilTags的模式检测器。您可以创建一个自定义模式检测器类,然后将其添加到列表中,以便在应用程序中使用。有关如何创建自定义模式的更多信息,请单击信息图标().中提供了AprilTags的自定义模式检测器类MyCustomAprilTagPatternDetector.m文件。该类包含用于检测器所需参数的UI代码,以及用于检测和处理自定义AprilTags校准模式的函数。

该示例使用configureUIComponents ()函数来配置UI组件和initializePropertyValues ()来初始化它。的helperDrawImageAxesLabels函数,包含在示例的末尾,用于渲染相机校准器应用程序对话框中显示的校准图像中的原点、x轴和y轴标签。

主要校准功能有:

  • detectPatternPoints ()-从捕获的图像中检测和定位AprilTags,并将其分类,以用作校准过程中的关键点。此函数是使用helperDetectAprilTagCorners函数,在示例的末尾给出。

  • generateWorldPoints ()-计算AprilTag模式中对应图像坐标的世界坐标。此函数是使用helperGenerateAprilTagPattern函数,在示例的末尾给出。

方法导入自定义模式检测程序类导入模式检测器按钮下定制模式面板。选择类文件MyCustomAprilTagPatternDetector.m.如果类中没有错误,那么你会看到下面的视图:

1. png

属性中的所有字段属性面板有正确的值。但是您可以自定义这些值以满足您的需要。请注意,广场的大小以世界单位表示标签的宽度,也被假设为等于图像中每个标签之间的间距。

点击好吧数据浏览器窗格显示带有id的图像列表,如下所示:

这些图像将包含检测到的模式。要查看图像,请从数据浏览器窗格。

2. png

预估相机参数

此时,摄像机标定过程与图中所示相同使用单相机校准器应用程序

使用默认的校准设置,单击校准按钮。校准选项卡。通过检查,可见校准的准确性Reprojection错误窗格,然后可视化外部相机参数的估计值Camera-centric窗格,显示相对于相机的模式定位。

校准images.PNG

万博1manbetx支持函数和类

helperGenerateAprilTagPattern生成基于AprilTag的校准模式。

函数calibPattern = helperGenerateAprilTagPattern(imdsTags, tagarrage,tagFamily) numTags = tagarrage (1)* tagarrage (2);tagIds = 0 (1,numTags);读取第一个图像。I = readimage(imdsTags,3);灰度= rgb2gray(I);放大缩略图标签图像。Ires = imresize(灰色,15,“最近的”);检测标签ID和位置(在图像坐标中)。[tagIds(1), tagLoc] = readAprilTag(Ires,tagFamily);带有白色边界的填充图像(确保标签取代黑色%棋盘的部分)。tagSize =圆(max (tagLoc (:, 2)) - min (tagLoc (:, 2)));padSize = round(tagSize/2 - (size(Ires,2) - tagSize)/2);Ires = padarray(Ires,[padSize,padSize],255);初始化tagImages数组以保存缩放的标签。tagImages = 0(大小(Ires,1),大小(Ires,2),numTags);tagImages(:,:,1) = Ires;idx = 2:numTags I = readimage(imdsTags,idx + 2);灰度= rgb2gray(I);Ires = imresize(灰色,15,“最近的”);Ires = padarray(Ires,[padSize,padSize],255);tagIds(idx) = readAprilTag(Ires,tagFamily);存储标签图像。tagImages(:,:,idx) = Ires;结束根据标签图像的id对其进行排序。[~, sortIdx] = sort(tagIds);tagImages = tagImages(:,:,sortIdx);重塑标签图像,以确保它们以列-主顺序出现%(蒙太奇函数将图像按行序排列)。columnMajIdx =重塑(1:numTags, tagarrangement)';tagImages = tagImages(:,:,columnMajIdx(:));使用“蒙太奇”创建图案。imgData =蒙太奇(tagImages,大小= tagarrangement);calibPattern = imgData.CData;结束

helperDetectAprilTagCorners检测图像中的AprilTag校准模式。

函数[imagePoints,boardSize,imagesUsed] = helperDetectAprilTagCorners(imdsCalib,tagArrangement,tagFamily)从tagArrangement中获取图案大小。boardSize = tagArrangement*2 + 1;初始化图像和标签的数量。numImages = length(imdscalb . files);numTags = tagArrangement(1)*tagArrangement(2);初始化AprilTag模式的角数。imagePoints = 0 (numTags*4,2,numImages);imagesUsed = 0 (1,numImages);从AprilTag角中获取棋盘角索引。checkerIdx = helperAprilTagToCheckerLocations(tagArrangement);idx = 1:numImages读取和检测图像中的AprilTags。I = readimage(imdsCalib,idx);[tagIds,tagLocs] = readAprilTag(I,tagFamily);%如果检测到所有标签,则接受图像。如果numel(tagIds) == numTags使用ID值对检测到的标记进行排序。[~,sortIdx] = sort(tagIds);tagLocs = tagLocs(:,:,sortIdx);将标签角位置重塑为m × 2数组。tagLocs =重塑(permute(tagLocs,[1,3,2]),[],2);使用棋盘角索引填充imagePoints。imagePoints(:,:,idx) = tagLocs(checkerIdx(:),:);imagesUsed(idx) = true;其他的imagePoints(:,:,idx) = [];结束结束结束

helperAprilTagToCheckerLocations将AprilTag角转换为棋盘角。

函数checkerIdx = helperAprilTagToCheckerLocations(tagArrangement) numTagRows = tagArrangement(1);numTagCols = tagArrangement(2);numTags = numTagRows * numTagCols;行索引偏移量。rowIdxOffset = [0:numTagRows - 1;0:numTagRows - 1];板中第一和第二列的行索引。col1Idx = repmat([4 1]',numTagRows,1);col2Idx = repmat([3 2]',numTagRows,1);col1Idx = col1Idx + rowIdxOffset(:)*4;col2Idx = col2Idx + rowIdxOffset(:)*4;列索引偏移量colIdxOffset = 0:4*numTagRows:numTags*4 - 1;隐式展开,使所有索引有序。checkerIdx = [col1Idx;col2Idx] + colIdxOffset;结束

helperDrawImageAxesLabels在校准器应用程序中显示校准图像中的原点、x轴和y轴标签。

函数[originLabel,xLabel,yLabel] = helperDrawImageAxesLabels(boardSize,imagePoints) num董事会ws = boardSize(1)-1;numBoardCols = boardSize(2)-1;将棋盘角重塑为boardSize形状的数组boardCoordsX =重塑(imagePoints(:,1), [num董事会ws,numBoardCols]);boardCoordsY =重塑(imagePoints(:,2), [num董事会ws,numBoardCols]);boardcoordds = cat(3, boardCoordsX,boardCoordsY);%原点标签(检查原点位置是否在图像内)如果~isnan(boardCoordsX(1,1)) p1 = boardCoordsX(1,1,:);refPointIdx = find(~isnan(boardCoordsX(:,1)),2);p2 = boardcods (refPointIdx(2),1,:);refPointIdx = find(~isnan(boardCoordsX(1,:)),2);p3 = boardcoordinates (1,refPointIdx(2),:);[loc, theta] = getAxesLabelPosition(p1,p2,p3);originLabel。Location = loc;originLabel。方向= theta;其他的originLabel = struct;结束% x轴标签firstRowIdx = numBoardCols:-1:1;refPointIdx13 = find(~isnan(boardCoordsX(1,firstRowIdx)),2);refPointIdx13 = firstRowIdx(refPointIdx13);p1 = boardcods (1,refPointIdx13(1),:);p3 = boardcoordinates (1,refPointIdx13(2),:);refPointIdx2 = find(~isnan(boardCoordsX(:,refPointIdx13(1))),2);p2 = boardcods (refPointIdx2(2),refPointIdx13(1),:);[loc, theta] = getAxesLabelPosition(p1,p2,p3);Theta = 180 + Theta;包含。Location = loc; xLabel.Orientation = theta;% y轴标签firstColIdx = numboardws:-1:1;refPointIdx12 = find(~isnan(boardCoordsX(firstColIdx,1)),2);refPointIdx12 = firstColIdx(refPointIdx12);p1 = boardcods (refPointIdx12(1),1,:);p2 = boardcods (refPointIdx12(2),1,:);refPointIdx3 = find(~isnan(boardCoordsX(refPointIdx12(1),:)), 2);p3 = boardcods (refPointIdx12(1),refPointIdx3(2),:);[loc,theta] = getAxesLabelPosition(p1,p2,p3);yLabel。Location = loc; yLabel.Orientation = theta;%--------------------------------------------------------------% p1 + v% \% \ v1% p1 ------ p2% |% v2 |% |% p3函数[loc,theta] = getAxesLabelPosition(p1,p2,p3) v1 = p3 - p1;Theta = -atan2d(v1(2),v1(1));V2 = p2 - p1;V = -v1 - v2;D = hypot(v(1),v(2));minDist = 40;如果d < minDist v = (v / d) * minDist;结束Loc = p1 + v;结束%--------------------------------------------------------------结束

参考

[1] E. Olson,“AprilTag:一个强大而灵活的视觉基准系统,”2011年IEEE机器人与自动化国际会议中国科学院学报,2011,pp. 3400-3407, doi: 10.1109/ICRA.2011.5979561。