Tags широко используются в качестве визуальных маркеров для приложений при обнаружении объектов, локализации и в качестве мишени для калибровки камеры [1]. Tags аналогичны QR кодам, но предназначены для кодирования меньшего количества данных, и поэтому могут быть декодированы быстрее, что полезно, например, для приложений робототехники в реальном времени.
В этом примере используется readAprilTag функция обнаружения и локализации тегов в шаблоне калибровки. readAprilTag поддерживает все официальные семейства марок. В примере также используются дополнительные функции Computer Vision Toolbox™ для выполнения сквозной калибровки камеры. Шаблон шахматной доски по умолчанию заменяется сеткой равномерно разнесенных тегов. Пример использования шаблона шашки для калибровки см. в приложении Single Camera Calibrator App.
Преимущества использования Tags в качестве шаблона калибровки: более надежное обнаружение точек признаков, последовательное и повторяющееся обнаружение. Этот пример может также служить шаблоном для использования других пользовательских калибровочных шаблонов, таких как поле окружностей вместо типичного шаблона шашки.
Предварительно созданные теги для всех поддерживаемых семейств можно загрузить отсюда с помощью веб-браузера или с помощью следующего кода:
downloadURL = 'https://github.com/AprilRobotics/apriltag-imgs/archive/master.zip'; dataFolder = fullfile(tempdir, 'apriltag-imgs', filesep); options = weboptions('Timeout', Inf); zipFileName = [dataFolder, 'apriltag-imgs-master.zip']; folderExists = exist(dataFolder, 'dir'); % Create a folder in a temporary directory to save the downloaded file if ~folderExists mkdir(dataFolder); disp('Downloading apriltag-imgs-master.zip (60.1 MB)...') websave(zipFileName, downloadURL, options); % Extract contents of the downloaded file disp('Extracting apriltag-imgs-master.zip...') unzip(zipFileName, dataFolder); end
Downloading apriltag-imgs-master.zip (60.1 MB)...
Extracting apriltag-imgs-master.zip...
Функцию helperGenerateTagPattern в конце примера можно использовать для создания цели калибровки с использованием изображений тегов для определенного расположения тегов. Изображение узора содержится в calibPattern, который может использоваться для печати шаблона (из MATLAB). В примере используется семейство tag36h11, которое обеспечивает разумный компромисс между производительностью обнаружения и устойчивостью к ложноположительным обнаружениям.
% Set the properties of the calibration pattern. tagArrangement = [5,8]; tagFamily = 'tag36h11'; % Generate the calibration pattern using AprilTags. tagImageFolder = [dataFolder 'apriltag-imgs-master/' tagFamily]; imdsTags = imageDatastore(tagImageFolder); calibPattern = helperGenerateAprilTagPattern(imdsTags, tagArrangement, tagFamily);

Использование readAprilTag функция на этом шаблоне приводит к обнаружениям с расположениями углов отдельных тегов, сгруппированных вместе. Функцию helperTagToChecureLocations можно использовать для преобразования этой компоновки в компоновку типа «основной столбец», аналогичную шахматной доске.
% Read and localize the tags in the calibration pattern. [tagIds, tagLocs] = readAprilTag(calibPattern, tagFamily); % Sort the tags based on their ID values. [~, sortIdx] = sort(tagIds); tagLocs = tagLocs(:,:,sortIdx); % Reshape the tag corner locations into an M-by-2 array. tagLocs = reshape(permute(tagLocs,[1,3,2]), [], 2); % Convert the AprilTag corner locations to checkerboard corner locations. checkerIdx = helperAprilTagToCheckerLocations(tagArrangement); imagePoints = tagLocs(checkerIdx(:),:); % Display corner locations. figure; imshow(calibPattern); hold on plot(imagePoints(:,1), imagePoints(:,2), 'ro-', 'MarkerSize',15)

Сгенерированная калибровочная схема должна быть напечатана на плоской поверхности, и камера, которая должна быть откалибрована, используется для захвата изображений модели. Несколько моментов, которые следует отметить при подготовке изображений к калибровке:
В то время как рисунок напечатан на бумаге в этом примере, рассмотрите возможность печати его на поверхности, которая остается плоской и не подвержена деформациям из-за влаги и т.д.
Поскольку процедура калибровки предполагает, что шаблон является плоским, любые дефекты в шаблоне (например, неровная поверхность) может снизить точность калибровки.
Процедура калибровки требует, по меньшей мере, 2 изображения шаблона, но использование от 10 до 20 изображений дает более точные результаты.
Захватывайте различные изображения узора таким образом, чтобы узор заполнял большую часть изображения, охватывая таким образом все поле зрения. Например, чтобы наилучшим образом зафиксировать искажение линзы, иметь изображения узора на всех краях кадра изображения.
Убедитесь, что узор полностью виден на захваченных изображениях, поскольку изображения с частично видимыми узорами будут отклонены.
Дополнительные сведения о подготовке изображений калибровочного шаблона см. в разделе Подготовка шаблона, камеры и изображений.

Функция helperDetectThreadTagCorners, включенная в конце примера, используется для обнаружения и локализации тегов из отснятых изображений и размещения их в шахматном порядке для использования в качестве ключевых точек в процедуре калибровки.
% Create an imageDatastore object to store the captured images. imdsCalib = imageDatastore("aprilTagCalibImages/"); % Detect the calibration pattern from the images. [imagePoints, boardSize] = helperDetectAprilTagCorners(imdsCalib, tagArrangement, tagFamily);
Сгенерированный шаблон TakingTag таков, что теги расположены в шахматном порядке, и поэтому мировые координаты для соответствующих координат изображения определены выше (в imagePoints) может быть получен с использованием generateCheckerboardPoints функция.
Здесь размер квадрата заменяется размером ярлыка и размер доски получается из предыдущего шага. Измерьте размер марки между внешними черными краями одной стороны марки.

% Generate world point coordinates for the pattern. tagSize = 16; % in millimeters worldPoints = generateCheckerboardPoints(boardSize, tagSize);
Оцените параметры камеры с помощью параметров изображения и точки мира. estimateCameraParameters функция.
% Determine the size of the images. I = readimage(imdsCalib, 1); imageSize = [size(I,1), size(I,2)]; % Estimate the camera parameters. params = estimateCameraParameters(imagePoints, worldPoints, 'ImageSize', imageSize);
Визуализация точности калибровки и параметров внешней камеры, показывающих плоскости шаблона калибровки на снятых изображениях.
% Display the reprojection errors.
figure
showReprojectionErrors(params)
% Display the extrinsics.
figure
showExtrinsics(params)
Проверьте местоположения обнаруженных точек изображения и повторно спроецированных точек, полученных с помощью расчетных параметров камеры.
% Read a calibration image. I = readimage(imdsCalib, 10); % Insert markers for the detected and reprojected points. I = insertMarker(I, imagePoints(:,:,10), 'o', 'Color', 'g', 'Size', 5); I = insertMarker(I, params.ReprojectedPoints(:,:,10), 'x', 'Color', 'r', 'Size', 5); % Display the image. figure imshow(I)

В то время как этот пример использует маркеры AprilTags в образце калибровки, тот же технологический процесс может быть расширен на другие плоские образцы также. estimateCameraParameters для получения параметров камеры требуется:
imagePoints: ключевые точки в шаблоне калибровки в координатах изображения, полученных из отснятых изображений.
worldPoints: Соответствующие координаты мировых точек ключевых точек в схеме калибровки.
При условии, что есть способ получить эти ключевые моменты, остальная часть процесса калибровки остается прежней.
helperGenerateTagPattern генерирует шаблон калибровки на основе тега.
function calibPattern = helperGenerateAprilTagPattern(imdsTags, tagArragement, tagFamily) numTags = tagArragement(1)*tagArragement(2); tagIds = zeros(1,numTags); % Read the first image. I = readimage(imdsTags, 3); Igray = im2gray(I); % Scale up the thumbnail tag image. Ires = imresize(Igray, 15, 'nearest'); % Detect the tag ID and location (in image coordinates). [tagIds(1), tagLoc] = readAprilTag(Ires, tagFamily); % Pad image with white boundaries (ensures the tags replace the black % portions of the checkerboard). tagSize = round(max(tagLoc(:,2)) - min(tagLoc(:,2))); padSize = round(tagSize/2 - (size(Ires,2) - tagSize)/2); Ires = padarray(Ires, [padSize,padSize], 255); % Initialize tagImages array to hold the scaled tags. tagImages = zeros(size(Ires,1), size(Ires,2), numTags); tagImages(:,:,1) = Ires; for idx = 2:numTags I = readimage(imdsTags, idx + 2); Igray = im2gray(I); Ires = imresize(Igray, 15, 'nearest'); Ires = padarray(Ires, [padSize,padSize], 255); tagIds(idx) = readAprilTag(Ires, tagFamily); % Store the tag images. tagImages(:,:,idx) = Ires; end % Sort the tag images based on their IDs. [~, sortIdx] = sort(tagIds); tagImages = tagImages(:,:,sortIdx); % Reshape the tag images to ensure that they appear in column-major order % (montage function places image in row-major order). columnMajIdx = reshape(1:numTags, tagArragement)'; tagImages = tagImages(:,:,columnMajIdx(:)); % Create the pattern using 'montage'. imgData = montage(tagImages, 'Size', tagArragement); calibPattern = imgData.CData; end
helperDetectIntegTrigCorners обнаруживает на изображениях шаблон калибровки HellingTag.
function [imagePoints, boardSize, imagesUsed] = helperDetectAprilTagCorners(imdsCalib, tagArrangement, tagFamily) % Get the pattern size from tagArrangement. boardSize = tagArrangement*2 + 1; % Initialize number of images and tags. numImages = length(imdsCalib.Files); numTags = tagArrangement(1)*tagArrangement(2); % Initialize number of corners in AprilTag pattern. imagePoints = zeros(numTags*4,2,numImages); imagesUsed = zeros(1, numImages); % Get checkerboard corner indices from AprilTag corners. checkerIdx = helperAprilTagToCheckerLocations(tagArrangement); for idx = 1:numImages % Read and detect AprilTags in image. I = readimage(imdsCalib, idx); [tagIds, tagLocs] = readAprilTag(I, tagFamily); % Accept images if all tags are detected. if numel(tagIds) == numTags % Sort detected tags using ID values. [~, sortIdx] = sort(tagIds); tagLocs = tagLocs(:,:,sortIdx); % Reshape tag corner locations into a M-by-2 array. tagLocs = reshape(permute(tagLocs,[1,3,2]), [], 2); % Populate imagePoints using checkerboard corner indices. imagePoints(:,:,idx) = tagLocs(checkerIdx(:),:); imagesUsed(idx) = true; else imagePoints(:,:,idx) = []; end end end
helperTagToChecterLocations преобразует углы тегов в шашечные углы.
function checkerIdx = helperAprilTagToCheckerLocations(tagArrangement) numTagRows = tagArrangement(1); numTagCols = tagArrangement(2); numTags = numTagRows * numTagCols; % Row index offsets. rowIdxOffset = [0:numTagRows - 1; 0:numTagRows - 1]; % Row indices for first and second columns in board. col1Idx = repmat([4 1]', numTagRows, 1); col2Idx = repmat([3 2]', numTagRows, 1); col1Idx = col1Idx + rowIdxOffset(:)*4; col2Idx = col2Idx + rowIdxOffset(:)*4; % Column index offsets colIdxOffset = 0:4*numTagRows:numTags*4 - 1; % Implicit expansion to get all indices in order. checkerIdx = [col1Idx;col2Idx] + colIdxOffset; end
[1] Э. Олсон, «Тег: надежная и гибкая визуальная реперная система», IEEE International Conference on Robotics and Automation 2011, Shanghai, 2011, pp. 3400-3407, doi: 10.1109/ICRA.2011.5979561.