Калибровка камеры с использованием маркеров AprilTag

AprilTags широко используются в качестве визуальных маркеров для приложений в обнаружении объектов, локализации и в качестве цели для калибровки камеры [1]. AprilTags аналогичны QR кодам, но разработаны, чтобы кодировать меньше данных, и поэтому могут декодироваться быстрее, что полезно, например, для приложений робототехники в реальном времени.

Этот пример использует readAprilTag функция для обнаружения и локализации AprilTags в калибровочном шаблоне. The readAprilTag функция поддерживает все официальные семейства тегов. Пример также использует дополнительные функции Computer Vision Toolbox™ для выполнения сквозной калибровки камеры. Шаблон шахматной доски по умолчанию заменяется сеткой с равномерными интервалами AprilTags. Пример использования шахматного шаблона для калибровки см. в приложении Single Camera Calibrator.

Преимущества использования AprilTags в качестве калибровочного шаблона: более устойчивое обнаружение функции точек, последовательные и повторяемые обнаружения. Этот пример может также служить шаблоном для использования других пользовательских калибровочных паттеров, таких как поле кругов вместо типового шахматного шаблона.

Шаг 1: Сгенерируйте калибровочный шаблон

Загрузите и подготовьте изображения тегов

Предварительно сгенерированные теги для всех поддерживаемых семейств можно загрузить отсюда с помощью веб-браузера или запустив следующий код:

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...

Функция helperGenerateAprilTagPattern в конце конца exmaple может использоваться, чтобы сгенерировать цель калибровки, используя изображения тегов для определенного расположения тегов. Изображение шаблона содержится в 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 функция на этом шаблоне приводит к обнаружениям с угловыми местоположениями отдельных тегов, сгруппированных вместе. Функция helperAprilTagToCheckerLocations может использоваться, чтобы преобразовать это расположение в основное расположение столбца, подобное шахматной доске.

% 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 изображений дает более точные результаты.

  • Захватывайте различные изображения шаблона таким образом, чтобы шаблон заполнял большую часть изображения, таким образом покрывая все поле зрения. Для примера, чтобы лучше всего захватить искажение объектива, иметь изображения шаблона на всех ребрах системы координат изображения.

  • Убедитесь, что шаблон полностью виден на захваченных изображениях, поскольку изображения с частично видимыми шаблонами будут отклонены.

  • Для получения дополнительной информации о подготовке изображений калибровочного шаблона, Подготовьте шаблон, Камеру и Изображения.

Шаг 2: Обнаружение и локализация AprilTags

Функция helperDetectAprilTagCorners, включенная в конец примера, используется для обнаружения и локализации тегов из захваченных изображений и расположения их в шахматном порядке, который используется в качестве ключевых точек в процедуре калибровки.

% 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);

Шаг 3: Сгенерируйте мировые точки для калибровочного шаблона

Сгенерированный шаблон AprilTag таков, что теги расположены в шахматном порядке, и поэтому мировые координаты для соответствующих координат изображения, определенных выше (в imagePoints) может быть получен с помощью generateCheckerboardPoints функция.

Здесь размер квадрата заменяется размером тега и размер платы получается с предыдущего шага. Измерьте размер тега между внешними чёрными ребрами одной стороны тега.

% Generate world point coordinates for the pattern.
tagSize = 16; % in millimeters
worldPoints = generateCheckerboardPoints(boardSize, tagSize);

Шаг 4: Оценка параметров камеры

С помощью изображений и соответствия мировых точек оцените параметры камеры с помощью 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 в калибровочном шаблоне, тот же рабочий процесс может быть распространен и на другие плоские шаблоны. The estimateCameraParameters используется для получения параметров камеры требует:

  • imagePoints: Ключевые точки в калибровочном шаблоне в координатах изображения, полученных из захваченных изображений.

  • worldPoints: Соответствующие мировые координаты точек ключевых точек в калибровочном шаблоне.

При условии, что существует способ получить эти ключевые точки, остальная часть рабочего процесса калибровки остается неизменной.

Вспомогательные функции

helper Generate April Tag Pattern генерирует калибровочный шаблон на основе April Tag.

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

helperDetectAprilTagCorners обнаруживает калибровочный шаблон AprilTag в изображениях.

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

helperAprilTagToCheckerLocations преобразует углы AprilTag в шахматные углы.

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] E. Olson, «AprilTag: A робастная и гибкая визуальная реперная система», 2011 IEEE International Conference on Robotics and Automation, Shanghai, 2011, pp. 3400-3407, doi: 10.1109/ICRA.2011.5979561.