Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DTU Robot dataset #1

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
81 changes: 27 additions & 54 deletions +benchmarks/RepeatabilityBenchmark.m
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,9 @@
properties
Opts = struct(...
'overlapError', 0.4,...
'normaliseFrames', true,...
'cropFrames', true,...
'magnification', 3,...
'warpMethod', 'linearise',...
'mode', 'repeatability',...
'descriptorsDistanceMetric', 'L2',...
'normalisedScale', 30);
'descriptorsDistanceMetric', 'L2');
end

properties(Constant, Hidden)
Expand Down Expand Up @@ -174,8 +170,8 @@
obj.checkInstall(varargin);
end

function [score numMatches bestMatches reprojFrames] = ...
testFeatureExtractor(obj, featExtractor, tf, imageAPath, imageBPath)
function [score numMatches bestMatches] = ...
testFeatureExtractor(obj, featExtractor, dataset, imgAId, imgBId)
% testFeatureExtractor Image feature extractor repeatability
% REPEATABILITY = obj.testFeatureExtractor(FEAT_EXTRACTOR, TF,
% IMAGEAPATH, IMAGEBPATH) computes the repeatability REP of a image
Expand Down Expand Up @@ -211,6 +207,8 @@
import benchmarks.*;
import helpers.*;

imageAPath = dataset.getImagePath(imgAId);
imageBPath = dataset.getImagePath(imgBId);
obj.info('Comparing frames from det. %s and images %s and %s.',...
featExtractor.Name,getFileName(imageAPath),...
getFileName(imageBPath));
Expand All @@ -228,29 +226,29 @@
if obj.ModesOpts(obj.Opts.mode).matchDescs
[framesA descriptorsA] = featExtractor.extractFeatures(imageAPath);
[framesB descriptorsB] = featExtractor.extractFeatures(imageBPath);
[score numMatches bestMatches reprojFrames] = obj.testFeatures(...
tf, imageASize, imageBSize, framesA, framesB,...
[score numMatches bestMatches] = obj.testFeatures(...
dataset, imgAId, imgBId, imageASize, imageBSize, framesA, framesB,...
descriptorsA, descriptorsB);
else
[framesA] = featExtractor.extractFeatures(imageAPath);
[framesB] = featExtractor.extractFeatures(imageBPath);
[score numMatches bestMatches reprojFrames] = ...
obj.testFeatures(tf,imageASize, imageBSize,framesA, framesB);
[score numMatches bestMatches] = ...
obj.testFeatures(dataset, imgAId, imgBId, imageASize, imageBSize,framesA, framesB);
end
if featExtractor.UseCache
results = {score numMatches bestMatches reprojFrames};
results = {score numMatches bestMatches};
obj.storeResults(results, resultsKey);
end
else
[score numMatches bestMatches reprojFrames] = cachedResults{:};
[score numMatches bestMatches] = cachedResults{:};
obj.debug('Results loaded from cache');
end

end

function [score numMatches matches reprojFrames] = ...
testFeatures(obj, tf, imageASize, imageBSize, framesA, framesB, ...
descriptorsA, descriptorsB)
function [score numMatches matches] = ...
testFeatures(obj, dataset, imgAId, imgBId, imageASize, ...
imageBSize, framesA, framesB, descriptorsA, descriptorsB)
% testFeatures Compute repeatability of given image features
% [SCORE NUM_MATCHES] = obj.testFeatures(TF, IMAGE_A_SIZE,
% IMAGE_B_SIZE, FRAMES_A, FRAMES_B, DESCS_A, DESCS_B) Compute
Expand Down Expand Up @@ -284,8 +282,11 @@
matchDescriptors = obj.ModesOpts(obj.Opts.mode).matchDescs;

if isempty(framesA) || isempty(framesB)
matches = zeros(size(framesA,2)); reprojFrames = {};
matches = zeros(size(framesA,2));
obj.info('Nothing to compute.');
score = 0;
numMatches = 0;
matches = [];
return;
end
if exist('descriptorsA','var') && exist('descriptorsB','var')
Expand All @@ -299,7 +300,7 @@

score = 0; numMatches = 0;
startTime = tic;
normFrames = obj.Opts.normaliseFrames;

overlapError = obj.Opts.overlapError;
overlapThresh = 1 - overlapError;

Expand All @@ -308,57 +309,29 @@
framesA = localFeatures.helpers.frameToEllipse(framesA) ;
framesB = localFeatures.helpers.frameToEllipse(framesB) ;

% map frames from image A to image B and viceversa
reprojFramesA = warpEllipse(tf, framesA,...
'Method',obj.Opts.warpMethod) ;
reprojFramesB = warpEllipse(inv(tf), framesB,...
'Method',obj.Opts.warpMethod) ;

% optionally remove frames that are not fully contained in
% both images
if obj.Opts.cropFrames
% find frames fully visible in both images
bboxA = [1 1 imageASize(2)+1 imageASize(1)+1] ;
bboxB = [1 1 imageBSize(2)+1 imageBSize(1)+1] ;

visibleFramesA = isEllipseInBBox(bboxA, framesA ) & ...
isEllipseInBBox(bboxB, reprojFramesA);

visibleFramesB = isEllipseInBBox(bboxA, reprojFramesB) & ...
isEllipseInBBox(bboxB, framesB );
[validFramesA validFramesB] = dataset.validateFrames(imgAId, imgBId, framesA, framesB);

% Crop frames outside overlap region
framesA = framesA(:,visibleFramesA);
reprojFramesA = reprojFramesA(:,visibleFramesA);
framesB = framesB(:,visibleFramesB);
reprojFramesB = reprojFramesB(:,visibleFramesB);
framesA = framesA(:,validFramesA);
framesB = framesB(:,validFramesB);

if isempty(framesA) || isempty(framesB)
matches = zeros(size(framesA,2)); reprojFrames = {};
return;
end

if matchDescriptors
descriptorsA = descriptorsA(:,visibleFramesA);
descriptorsB = descriptorsB(:,visibleFramesB);
descriptorsA = descriptorsA(:,validFramesA);
descriptorsB = descriptorsB(:,validFramesB);
end
end

if ~normFrames
% When frames are not normalised, account the descriptor region
magFactor = obj.Opts.magnification^2;
framesA = [framesA(1:2,:); framesA(3:5,:).*magFactor];
reprojFramesB = [reprojFramesB(1:2,:); ...
reprojFramesB(3:5,:).*magFactor];
end

reprojFrames = {framesA,framesB,reprojFramesA,reprojFramesB};
frameOverlaps = dataset.scoreFrameOverlaps(imgAId, imgBId, framesA, framesB);
numFramesA = size(framesA,2);
numFramesB = size(reprojFramesB,2);

% Find all ellipse overlaps (in one-to-n array)
frameOverlaps = fastEllipseOverlap(reprojFramesB, framesA, ...
'NormaliseFrames',normFrames,'MinAreaRatio',overlapThresh,...
'NormalisedScale',obj.Opts.normalisedScale);
numFramesB = size(framesB,2);

matches = [];

Expand Down
223 changes: 223 additions & 0 deletions +datasets/DTURobotDataset.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
classdef DTURobotDataset < datasets.GenericCorrespondenceDataset ...
& helpers.Logger & helpers.GenericInstaller


properties (SetAccess=private, GetAccess=public)
Category = 'arc1'; % Dataset category
Viewpoints;
Lightings;
ReconstructionsDir;
CacheDir;
CamerasPath;
CodeDir;
DataDir;
end

properties (Constant)
% All dataset categories
AllCategories = {'arc1', 'arc2', 'arc3', 'linear_path', ...
'lighting_x', 'lighting_y'};
end

properties (Constant, Hidden)
% Names of the image transformations in particular categories
CategoryImageNames = {...
'Arc 1, viewpoint angle',...
'Arc 2, viewpoint angle',...
'Arc 3, viewpoint angle',...
'Linear path, camera distance',...
'Lighting changes, x coordinate',...
'Lighting changes, y coordinate',...
};
% Image labels for particular categories (degree of transf.)
CategoryImageLabels = {...
round([[-40: 40/24: -1.6666666] [1.666666: 40/24: 40]]*10)/10,... % arc1
round([-25: 50/29: 25]*10)/10,... % arc2
round([-20: 40/24: 20]*10)/10,... % arc3
round([.5214: .3/14: .8]*10)/10,... % linear_path
round([0: 180.8/8: 180.8]*10)/10,... % lighting_x
round([0: 80.3/6: 80.3]*10)/10,... % lighting_y
};
% Viewpoint indices for each category.
CategoryViewpoints = {...
[1:24 26:49], ...
[65:94], ...
[95:119], ...
[51:64], ...
[12 25 60 87], ...
[12 25 60 87], ...
};
% Lighting indices for each category.
CategoryLightings = {...
0, ...
0, ...
0, ...
0, ...
[20:28], ...
[29:35], ...
};
% Size of search cells in the structured light grid.
CellRadius = 10;
% Acceptance threshold of point distance in 3D. 3e-3 = 3mm.
StrLBoxPad=3e-3;
% Acceptance threshold of backprojection error in pixels.
BackProjThresh=5;
% Acceptance threshold of scale difference after distance normalization. A
% value of 2 corresponds to an octave.
ScaleMargin = 2;

ImgWidth = 1600;
ImgHeight = 1200;

% Installation directory
RootInstallDir = fullfile('data','datasets','DTURobot');
% URL for dataset tarballs
CodeUrl = 'http://roboimagedata.imm.dtu.dk/data/code.tar.gz';
ReconstructionsUrl = 'http://roboimagedata.imm.dtu.dk/data/ground_truth.tar.gz';
ScenesUrl = 'http://roboimagedata.imm.dtu.dk/data/condensed.tar.gz';
end

methods
function obj = DTURobotDataset(varargin)
import datasets.*;
import helpers.*;
opts.Category = obj.Category;
[opts varargin] = vl_argparse(opts,varargin);
[valid loc] = ismember(opts.Category,obj.AllCategories);
assert(valid,...
sprintf('Invalid category for DTURobot dataset: %s\n',opts.Category));
obj.DatasetName = ['DTURobotDataset-' opts.Category];
obj.Category= opts.Category;
obj.DataDir = fullfile(obj.RootInstallDir,'scenes');

obj.checkInstall(varargin);
obj.ImageNames = obj.CategoryImageLabels{loc};
obj.ImageNamesLabel = obj.CategoryImageNames{loc};
obj.Viewpoints = obj.CategoryViewpoints{loc};
obj.Lightings = obj.CategoryLightings{loc};
obj.ReconstructionsDir = fullfile(obj.RootInstallDir, 'reconstructions');
obj.CamerasPath = fullfile(obj.RootInstallDir, 'cameras.mat');
obj.CacheDir = fullfile(obj.RootInstallDir, 'cache');
obj.CodeDir = fullfile(obj.RootInstallDir, 'code');
obj.NumScenes = 60;
obj.NumLabels = numel(obj.ImageNames);
if strfind(obj.Category,'lighting')
obj.NumImages = obj.NumScenes * obj.NumLabels * numel(obj.CategoryViewpoints{loc});
else
obj.NumImages = obj.NumScenes * obj.NumLabels;
end
addpath(obj.CodeDir)
end

function imgPath = getImagePath(obj, imgId)
if isstruct(imgId)
imgPath = fullfile(obj.DataDir, sprintf('scene%.3d/%.3d_%.2d.png', ...
imgId.scene, imgId.viewpoint, imgId.lighting));
else
labelNo = floor(imgId/obj.NumImages)+1;
sceneNo = mod(imgId,obj.NumImages)+1;
imgId = obj.getImageId(labelNo, sceneNo);
imgPath = obj.getImagePath(imgId);
end
end

function imgId = getReferenceImageId(obj, labelNo, sceneNo)
imgId.scene = sceneNo;
imgId.viewpoint = 25;
imgId.lighting = 0;
end

function imgId = getImageId(obj, labelNo, sceneNo)
if strfind(obj.Category,'lighting')
imgId.viewpoint = obj.Viewpoints(mod(sceneNo, numel(obj.Viewpoints))+1);
imgId.lighting = obj.Lightings(labelNo);
imgId.scene = floor(sceneNo/numel(obj.Viewpoints))+1;
else
imgId.viewpoint = obj.Viewpoints(labelNo);
imgId.lighting = 0;
imgId.scene = sceneNo;
end
end

function [validFramesA validFramesB] = validateFrames(obj, ...
imgAId, imgBId, framesA, framesB)
validFramesA = logical(ones(1, size(framesA,2)));
validFramesB = logical(ones(1, size(framesB,2)));
end

function overlaps = scoreFrameOverlaps(obj, imgAId, imgBId, framesA, framesB)
% Get 3D reconstruction
[Grid3D, Pts] = GenStrLightGrid(imgAId.viewpoint, ...
obj.ReconstructionsDir, obj.ImgHeight, obj.ImgWidth, ...
obj.CellRadius, imgAId.scene);
CamPair = GetCamPair(imgAId.viewpoint, imgBId.viewpoint);

N = size(framesA,2);
neighs = cell(1,N);
scores = cell(1,N);
for f = 1:N
frame_ref = framesA(:, f)';
[neighs{f}, scores{f}] = obj.overlap(Grid3D, Pts, CamPair, frame_ref, framesB');
end
overlaps.neighs = neighs;
overlaps.scores = scores;
end


function [neighs, scores] = overlap(obj, Grid3D, Pts, Cams, frame_ref, frames)
KeyP = frame_ref(1:2);
KeyScale = frame_ref(3);

% Project point onto 3D structure
[Mean,Var,IsEst] = Get3DGridEst(Grid3D,Pts,obj.CellRadius,KeyP(1),KeyP(2));

neighs = [];
scores = [];
if(IsEst)
Var = Var+obj.StrLBoxPad;
Q = Mean*ones(1,8)+[Var(1)*[-1 1 -1 1 -1 1 -1 1];
Var(2)*[-1 -1 1 1 -1 -1 1 1];
Var(3)*[-1 -1 -1 -1 1 1 1 1]];
q = Cams(:,:,2)*[Q;ones(1,8)];
depth = mean(q(3,:));
q(1,:) = q(1,:)./q(3,:);
q(2,:) = q(2,:)./q(3,:);
q(3,:) = q(3,:)./q(3,:);

kq = Cams(:,:,1)*[Q;ones(1,8)];
kDepth = mean(kq(3,:));
Scale = KeyScale*kDepth/depth;

% Find neighbor frames within scale bounds
idx = find(frames(:,1)>min(q(1,:)) & frames(:,1)<max(q(1,:)) & ...
frames(:,2)>min(q(2,:)) & frames(:,2)<max(q(2,:)) & ...
frames(:,3)>Scale/obj.ScaleMargin & ...
frames(:,3)<Scale*obj.ScaleMargin );

for i=1:length(idx),
% Score matches
consistency = PointCamGeoConsistency([KeyP 1]', [frames(idx(i),1:2) 1]', Cams);
if consistency < obj.BackProjThresh
neighs = [neighs idx(i)];
scores = [scores 1/consistency];
end
end
end
end


end

methods (Access = protected)
function [urls dstPaths] = getTarballsList(obj)
import datasets.*;
installDir = DTURobotDataset.RootInstallDir;
dstPaths = {fullfile(installDir),
fullfile(installDir),
fullfile(installDir)};
urls = {DTURobotDataset.CodeUrl,
DTURobotDataset.ReconstructionsUrl,
DTURobotDataset.ScenesUrl};
end
end
end
Loading