Калибровка фотоаппарата Используя маркеры AprilTag

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

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

В этом примере показано, как калибровать использование камеры AprilTags программно, и при помощи приложения Camera Calibrator:

Калибровка фотоаппарата Используя функциональный интерфейс

Шаг 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

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

Подготовьте изображения к калибровке

Несколько моментов, которых необходимо отметить, при подготовке изображений к калибровке:

  • В то время как шаблон распечатан на бумаге в этом примере, рассмотрите печать его на поверхности, которая остается плоской, и не подвергается деформациям из-за влажности и т.д.

  • Поскольку калибровочная процедура принимает, что шаблон является плоским, любые недостатки в шаблоне (e.g. неровная поверхность), может уменьшать точность калибровки.

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

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

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

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

Интеграция калибровочной поддержки шаблона AprilTag в приложение Camera Calibrator

Для удобства использования вышеупомянутый рабочий процесс может также быть интегрирован в приложение Camera Calibrator. Полный рабочий процесс остается, то же самое и шаги:

1. Добавьте изображения с AprilTags.

2. Импортируйте пользовательский класс детектора шаблона для AprilTags. Детектор должен сделать следующее:

  • Обнаружьте и локализуйте AprilTags

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

3. Оцените параметры камеры.

Добавьте изображения с AprilTags

Откройте приложение Camera Calibrator:

  • Панель инструментов MATLAB: На вкладке Apps, в разделе Image Processing и Computer Vision, кликают по значку Camera Calibrator.

  • Командная строка MATLAB: Введите cameraCalibrator

На вкладке Calibration, в Разделе файла, нажимают на изображения Add, и затем выбирают From file. Можно добавить изображения из нескольких папок путем нажимания на изображения Add для каждой папки. Мы снова используем те же изображения как выше. Вам будут нужны по крайней мере 2 изображения для калибровки фотоаппарата. Если вы добавляете изображения, следующий пользовательский интерфейс появится:

Расширьте Пользовательскую панель Шаблона, чтобы видеть больше опций.

Импортируйте пользовательский класс детектора шаблона

Вышеупомянутый пользовательский интерфейс показывает выпадающий список для выбора шаблона. По умолчанию приложение не включает детектор шаблона для AprilTags. Можно создать пользовательский класс детектора шаблона и затем добавить его в список, чтобы использовать в приложении. Для получения дополнительной информации о том, как создать пользовательский шаблон, нажмите на информационный значок (). Пользовательский класс детектора шаблона для AprilTags был обеспечен в файле MyCustomAprilTagPatternDetector.m. Этот класс содержит код пользовательского интерфейса для параметров, необходимых детектору и функциям для обнаружения и обработки пользовательского калибровочного шаблона AprilTags.

Пример использует configureUIComponents () функция, чтобы сконфигурировать компонент пользовательского интерфейса и initializePropertyValues (), чтобы инициализировать его. Функция helperDrawImageAxesLabels, включенная в конце примера, используется, чтобы представить источник, Ось X и метки Оси Y в калибровочных изображениях, отображенных в диалоговом окне приложения Camera Calibrator.

Основные калибровочные функции:

  • detectPatternPoints () - Обнаруживает и локализует AprilTags от полученных изображений и сортирует их для использования в качестве ключевых пунктов в калибровочной процедуре. Эта функция реализована с помощью helperDetectAprilTagCorners функцию, данную в конце примера.

  • generateWorldPoints () - Вычисляет мировые координаты для соответствующих координат изображений в шаблоне AprilTag. Эта функция реализована с помощью helperGenerateAprilTagPattern функцию, данную в конце примера.

Импортируйте пользовательский класс детектора шаблона путем нажатия на кнопку Import Pattern Detector под Пользовательской панелью Шаблона. Выберите файл класса MyCustomAprilTagPatternDetector.m. Если не будет никаких ошибок в классе, то вы будете видеть следующее представление:

В данном примере все поля в панели Свойств имеют правильные значения. Но можно настроить эти значения, чтобы соответствовать потребностям. Обратите внимание на то, что Размер квадратов представляет ширину тега в мировых единицах измерения и также принят, чтобы быть равным интервалу между каждым тегом в изображении.

Нажмите ОК и панель Браузера Данных отображает список изображений с идентификаторами, как показано ниже:

Эти изображения будут содержать обнаруженный шаблон. Чтобы просмотреть изображение, выберите его из панели Браузера Данных.

Оцените параметры камеры

На данном этапе процесс калибровки фотоаппарата эквивалентен данный в Использовании Одного Приложения Camera Calibrator.

С калибровочными настройками по умолчанию нажмите кнопку Calibrate на вкладке Calibration. Визуализируйте точность калибровки путем осмотра Ошибок Перепроекции, разделяют на области и затем визуализируют оценки внешних параметров камеры в центральной Камерой панели, которая показывает шаблоны, расположенные относительно камеры.

Поддерживание функций и классов

helperGenerateAprilTagPattern генерирует основанный на AprilTag калибровочный шаблон.

function calibPattern = helperGenerateAprilTagPattern(imdsTags, tagArragement, tagFamily)

numTags = tagArragement(1)*tagArragement(2);
tagIds = zeros(1,numTags);

% Read the first image.
I = readimage(imdsTags, 3);
Igray = rgb2gray(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 = rgb2gray(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

helperDrawImageAxesLabels представляет источник, Ось X и метки Оси Y в калибровочных изображениях, отображенных в приложении калибратора.

function [originLabel, xLabel, yLabel] = helperDrawImageAxesLabels(boardSize, imagePoints)

    numBoardRows = boardSize(1)-1;
    numBoardCols = boardSize(2)-1;
    
    % Reshape checkerboard corners to boardSize shaped array
    boardCoordsX = reshape(imagePoints(:,1), [numBoardRows, numBoardCols]);
    boardCoordsY = reshape(imagePoints(:,2), [numBoardRows, numBoardCols]);
    boardCoords = cat(3, boardCoordsX, boardCoordsY);
    
    % Origin label (check if origin location is inside the image)
    if ~isnan(boardCoordsX(1,1))
        p1 = boardCoords(1,1,:);
        
        refPointIdx = find(~isnan(boardCoordsX(:,1)),2);
        p2 = boardCoords(refPointIdx(2),1,:);
        
        refPointIdx = find(~isnan(boardCoordsX(1,:)),2);
        p3 = boardCoords(1,refPointIdx(2),:);
        
        [loc, theta] = getAxesLabelPosition(p1, p2, p3);
        
        originLabel.Location    = loc;
        originLabel.Orientation = theta;
    else
        originLabel = struct;
    end
    
    % X-axis label
    firstRowIdx = numBoardCols:-1:1;
    refPointIdx13 = find(~isnan(boardCoordsX(1,firstRowIdx)), 2);
    refPointIdx13 = firstRowIdx(refPointIdx13);
    
    p1 = boardCoords(1,refPointIdx13(1),:);
    p3 = boardCoords(1,refPointIdx13(2),:);
    
    refPointIdx2 = find(~isnan(boardCoordsX(:,refPointIdx13(1))), 2);
    p2 = boardCoords(refPointIdx2(2),refPointIdx13(1),:);
    
    [loc, theta] = getAxesLabelPosition(p1, p2, p3);
    theta = 180 + theta;
    
    xLabel.Location    = loc;
    xLabel.Orientation = theta;
    
    % Y-axis label
    firstColIdx = numBoardRows:-1:1;
    refPointIdx12 = find(~isnan(boardCoordsX(firstColIdx,1)),2);
    refPointIdx12 = firstColIdx(refPointIdx12);
    
    p1 = boardCoords(refPointIdx12(1),1,:);
    p2 = boardCoords(refPointIdx12(2),1,:);
    
    refPointIdx3 = find(~isnan(boardCoordsX(refPointIdx12(1),:)), 2);
    p3 = boardCoords(refPointIdx12(1),refPointIdx3(2),:);
    
    [loc, theta] = getAxesLabelPosition(p1, p2, p3);
    
    yLabel.Location    = loc;
    yLabel.Orientation = theta;
    
    %--------------------------------------------------------------
    % p1+v
    %  \
    %   \     v1
    %    p1 ------ p2
    %    |
    % v2 |
    %    |
    %    p3
    function [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;
        if d < minDist
            v = (v / d) * minDist;
        end
        loc = p1 + v;
    end
    %--------------------------------------------------------------
    
end

Ссылка

[1] Э. Олсон, "AprilTag: устойчивая и гибкая визуальная основанная на вере система", 2 011 Международных конференций IEEE по вопросам Робототехники и Автоматизации, Шанхая, 2011, стр 3400-3407, doi: 10.1109/ICRA.2011.5979561.