Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
43 changed files
with
4,194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
function [score] = ComputeMatchScore(im1, im2, method, Settings) | ||
|
||
% Given two images, return a score based on their match cost. The lower, the better. | ||
% | ||
% Inputs: im1 - Input image 1 | ||
% im2 - Input image 2 | ||
% method - Match method (DSP or SIFT) | ||
% Settings - Application wide settings used to store algorithm parameters, paths etc. | ||
% | ||
% Outputs: score - Match cost, lower score means two images match better. | ||
|
||
score = 0; | ||
|
||
if(strcmp(method, 'DSP')) | ||
% pca_basis: pca basis for dimensionality reduction of sift | ||
%load('dsp/lmo_pca_basis.mat', 'pca_basis'); | ||
pca_basis = []; % if you want to use the sift of original dimension | ||
sift_size = 4; % sift patch size 16 x 16 pixels (i.e., patch_size = 4*sift_size) | ||
% Extract SIFT | ||
[sift1, ~] = ExtractSIFT(im1, pca_basis, sift_size); | ||
[sift2, ~] = ExtractSIFT(im2, pca_basis, sift_size); | ||
[~, ~, match_cost] = DSPMatch(sift1, sift2); | ||
score = mean2(match_cost); % Compute the score based on the match_cost of DSP matrix. | ||
|
||
else if(strcmp(method, 'SIFT')) | ||
im1 = single(rgb2gray(im1)); % Convert to single, since it is recommended | ||
im2 = single(rgb2gray(im2)); | ||
[F1 D1] = vl_sift(im1); % Extract sift features and descriptors | ||
[F2 D2] = vl_sift(im2); % Extract sift features and descriptors | ||
L2Ratio = Settings.L2Ratio4SIFT; % Euclidean distance ratio of NN2/NN1, used to filter matches | ||
[matches score] = vl_ubcmatch(D1, D2, L2Ratio); | ||
score = mean2(score); | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
function [averageEstimationError] = DemoApp(mode, parameters) | ||
|
||
% Geolocalize given query scene(s) using a reference dataset. | ||
% | ||
% Inputs: mode - only test mode for now | ||
% parameters - simply a struct with the same fields as settings. You can set any field as you wish. | ||
% | ||
% Outputs: averageEstimationError - average estimation error for given scene(s) | ||
|
||
addpath(genpath('lib')); % Environment variables. | ||
Settings = LoadSettings('data/settings.ini'); % Load default settings. | ||
|
||
if(strcmp(mode,'test') && exist('parameters','var')) % In test mode, if provided override default settings | ||
parameterFields = fieldnames(parameters); | ||
for loopIndex = 1:numel(parameterFields) | ||
if(isfield(Settings,parameterFields{loopIndex})) % If there is a same field in Settings | ||
Settings.(parameterFields{loopIndex}) = parameters.(parameterFields{loopIndex}); | ||
end | ||
end | ||
end | ||
|
||
Settings.InitialResultPath = Settings.ResultPath; % Used to reset to initial folder | ||
Results = struct('QueryImageName', [], 'QueryImageLatitude', 0, 'QueryImageLongitude', 0, 'EstimatedLatitude', 0, 'EstimatedLongitude', 0, 'EstimationError', 0, 'ComputationTime', 0, 'Success', 0, 'TimesBetterThanChance', 0); | ||
JointResults = struct('QueryImageName', [], 'EstimationError', 0, 'ComputationTime', 0, 'Success', 0, 'ByChanceDistanceError', 0, 'TimesBetterThanChance', 0); | ||
baseFolder = [datestr(now,30) '-' Settings.ExperimentName] ; | ||
mkdir(Settings.ResultPath, baseFolder); % Create a base directory to save the results for the input. | ||
disp('Starting Geolocalization...'); | ||
AllItems = LoadPreRequisites(Settings); | ||
|
||
if(Settings.SizeCap ~= 0 && length(AllItems) > Settings.SizeCap) | ||
r = randi(length(AllItems), Settings.SizeCap, 1); % Get random integers | ||
AllItems = AllItems(r); % Reduce dataset size by selecting random items from dataset, for size effect in the paper. | ||
clear r; | ||
end | ||
|
||
if(strcmp(mode,'test')) % Test mode | ||
inputImageList = dir([Settings.QueryDataPath '*.jpg']); % Get query image list | ||
if isempty(inputImageList) | ||
error('Cannot find the query images.'); | ||
end | ||
[~,order] = sort_nat({inputImageList.name}); % Sort images in asc order. | ||
inputImageList = inputImageList(order); | ||
imageCount = length(inputImageList); | ||
end | ||
|
||
if(Settings.SequenceCap ~= 0 && imageCount > Settings.SequenceCap) | ||
imageCount = Settings.SequenceCap; | ||
end | ||
|
||
for imageIndex=1:imageCount | ||
start = clock; | ||
Settings.ResultPath = [Settings.InitialResultPath baseFolder '/']; % Set path to base folder | ||
Results.QueryImageName = num2str(imageIndex); | ||
|
||
if(strcmp(mode, 'test')) % Test mode | ||
%if(~strcmp(inputImageList(imageIndex).name, '0445.jpg')) % To test specific image. Comment o/w. | ||
% Settings.ResultPath = [Settings.InitialResultPath]; % Reset path | ||
% continue; | ||
%end | ||
disp(['Processing ' inputImageList(imageIndex).name]); | ||
imageFileName = inputImageList(imageIndex).name; | ||
imageFullPath = strcat(Settings.QueryDataPath, imageFileName); % Construct the full path of the image | ||
queryImage = imread(imageFullPath); % Read image | ||
fileName = strrep(imageFileName, '.jpg', ''); % Strip file extension to obtain file name | ||
fid = fopen([Settings.QueryDataPath fileName '.gps'], 'r'); % Read GPS data | ||
|
||
if fid <= 0 | ||
error('Cannot read: %s', fileName); | ||
end | ||
|
||
tline = fgetl(fid); | ||
fileNameParameterArray = textscan(tline, '%s', 'delimiter', ' '); % Split string | ||
lat = fileNameParameterArray{1}{1}; | ||
lon = fileNameParameterArray{1}{2}; | ||
fclose(fid); | ||
Settings.QueryImageLatitude = str2double(lat); | ||
Settings.QueryImageLongitude = str2double(lon); | ||
Results.QueryImageName = fileName; | ||
else | ||
queryImage = keyFrames(imageIndex); | ||
end | ||
|
||
mkdir(Settings.ResultPath, Results.QueryImageName); % Create a directory to save individual results. | ||
Settings.ResultPath = [Settings.ResultPath Results.QueryImageName '/']; | ||
[latitude, longitude, FirstCandidates, SecondCandidates, ThirdCandidates, FirstCandidateOutliers, SecondCandidateOutliers, AllCandidates] = RetrieveAlignPredict(queryImage, AllItems, Settings); | ||
% Display Results | ||
disp('---------RESULTS--------------------------------------------------------------------------------------'); | ||
disp(['Estimated location for image ' Results.QueryImageName]); | ||
disp(['Latitude:' num2str(latitude)]); | ||
disp(['Longitude:' num2str(longitude)]); | ||
computationTime = etime(clock, start); | ||
disp(['The computation took ' num2str(computationTime) ' seconds on the ' num2str(size(queryImage, 2)) 'x' num2str(size(queryImage, 1)) ' image']); | ||
|
||
if(strcmp(mode,'test')) % Test mode | ||
latlongQueryImage = [Settings.QueryImageLatitude Settings.QueryImageLongitude]; | ||
Results.EstimationError = lldistkm(latlongQueryImage, [latitude longitude]); % Calculate distance between estimated location and query location | ||
|
||
if(Results.EstimationError < Settings.SuccessDistance) | ||
Results.Success = 1; | ||
disp('Estimation succeeded...'); | ||
else | ||
Results.Success = 0; | ||
disp('Estimation failed...'); | ||
end | ||
|
||
disp(['Estimation error:' num2str(Results.EstimationError) 'km']); | ||
disp('Please wait, calculating better than chance metric...'); | ||
[RandomCandidates, latitudeRandom, longitudeRandom] = EstimateByChance(AllCandidates, length(ThirdCandidates), Settings); | ||
estimatedDistanceForRandomSelection = lldistkm(latlongQueryImage, [latitudeRandom longitudeRandom]); % Calculate distance between the query and the estimated location | ||
Results.TimesBetterThanChance = estimatedDistanceForRandomSelection / Results.EstimationError; | ||
disp(['Our results are better than chance by ' num2str(Results.TimesBetterThanChance) ' times']); | ||
end | ||
disp('--------------------------------------------------------------------------------------------------------'); | ||
|
||
if(Settings.DisplayResults == 1 || Settings.ExportResults == 1) | ||
disp('Adjusting points..'); | ||
[AllCandidates, FirstCandidates, SecondCandidates] = AdjustCandidateScoresForPlotting(AllCandidates, FirstCandidates, SecondCandidates); | ||
end | ||
|
||
% Plot results | ||
if(Settings.DisplayResults == 1) | ||
disp('Plotting results..'); | ||
DisplayAndSaveResults(queryImage, FirstCandidates, SecondCandidates, ThirdCandidates, RandomCandidates, AllCandidates, FirstCandidateOutliers, SecondCandidateOutliers, Settings); % Display and save results altogether | ||
close all; | ||
end | ||
|
||
Results.QueryImageLatitude = Settings.QueryImageLatitude; % Save results | ||
Results.QueryImageLongitude = Settings.QueryImageLongitude; | ||
Results.EstimatedLatitude = latitude; | ||
Results.EstimatedLongitude = longitude; | ||
Results.ComputationTime = computationTime; | ||
|
||
if(Settings.ExportResults == 1) | ||
save([Settings.ResultPath 'Settings.mat'], 'Settings'); | ||
save([Settings.ResultPath 'Results.mat'], 'Results'); | ||
save([Settings.ResultPath 'FirstCandidates.mat'], 'FirstCandidates'); | ||
save([Settings.ResultPath 'SecondCandidates.mat'], 'SecondCandidates'); | ||
save([Settings.ResultPath 'ThirdCandidates.mat'], 'ThirdCandidates'); | ||
save([Settings.ResultPath 'RandomCandidates.mat'], 'RandomCandidates'); | ||
save([Settings.ResultPath 'FirstCandidateOutliers.mat'], 'FirstCandidateOutliers'); | ||
save([Settings.ResultPath 'SecondCandidateOutliers.mat'], 'SecondCandidateOutliers'); | ||
save([Settings.ResultPath 'AllCandidates.mat'], 'AllCandidates'); | ||
close all; | ||
end | ||
|
||
% Set joint results | ||
JointResults(imageIndex).QueryImageName = Results.QueryImageName; | ||
JointResults(imageIndex).EstimationError = Results.EstimationError; | ||
JointResults(imageIndex).ByChanceDistanceError = estimatedDistanceForRandomSelection; | ||
JointResults(imageIndex).ComputationTime = Results.ComputationTime; | ||
JointResults(imageIndex).TimesBetterThanChance = Results.TimesBetterThanChance; | ||
JointResults(imageIndex).Success = Results.Success; | ||
end | ||
|
||
Settings.ResultPath = [Settings.InitialResultPath baseFolder '/']; % Reset path | ||
save([Settings.ResultPath 'JointResults.mat'], 'JointResults'); | ||
struct2csv(JointResults, [Settings.ResultPath 'joint-recall-results-' Settings.ExperimentName '.csv']); % Save as csv file | ||
averageEstimationError = mean([JointResults.EstimationError]); | ||
disp('Geolocalization completed successfuly!'); | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
function [candidateLocations, outliers] = FurthestNeighbourRemoval(candidateLocations, method, Settings) | ||
|
||
% Remove furthers neighbours in two stages by employing a similarity based and a distance based approach | ||
% | ||
% Inputs: candidateLocations - A set of candidates which contain inliers and outliers | ||
% method - A similarity metric (JointScore or MatchScore) | ||
% Settings - Application wide settings used to store algorithm parameters, paths etc. | ||
% | ||
% Outputs: candidateLocations - Inliers | ||
% outliers - Outliers | ||
|
||
remaining = length(candidateLocations); | ||
|
||
%% PHASE 1: Remove furthest neighbours based on similarity. | ||
minusFrom = 1; | ||
if(strcmp(Settings.NormalizationMethod, 'exp')) | ||
minusFrom = Settings.Sigma; | ||
elseif(strcmp(Settings.NormalizationMethod, 'max')) | ||
minusFrom = 1; | ||
end | ||
|
||
% Subtract scores, so that lower the values better the score. | ||
for k = 1:length(candidateLocations); | ||
if(strcmp(method, 'JointScore')) | ||
candidateLocations(k).AdjustedDistance = minusFrom - candidateLocations(k).JointScore; | ||
elseif(strcmp(method, 'MatchScore')) | ||
candidateLocations(k).AdjustedDistance = minusFrom - candidateLocations(k).MatchScore; | ||
end | ||
candidateLocations(k).AveragePairwiseDistance = 0; % A small fix for first outliers to make them have same fields with second outliers | ||
end | ||
|
||
distMin = min([candidateLocations.AdjustedDistance]); | ||
candidateLocations = nestedSortStruct(candidateLocations, 'AdjustedDistance'); % Sort by AdjustedDistance | ||
|
||
idx = []; | ||
for k = 1:length(candidateLocations) | ||
if((candidateLocations(k).AdjustedDistance > (1 + Settings.Epsilon) * distMin)) | ||
if(remaining > Settings.NearestNeighbourNumber) % Make sure we have at least k items to triangulate. | ||
idx = [idx k]; | ||
remaining = remaining - 1; | ||
else | ||
break; | ||
end | ||
end | ||
end | ||
|
||
outliersSimilarity = candidateLocations(idx); | ||
candidateLocations(idx) = []; % Remove outliers | ||
|
||
%% PHASE 2: Remove furthest neighbours based on 2D distances | ||
positions = [candidateLocations.Latitude; candidateLocations.Longitude]; | ||
distances = dist(positions); | ||
dMax = max(distances(:)); | ||
normalizedDistances = distances / dMax; | ||
|
||
for k = 1:length(candidateLocations); | ||
candidateLocations(k).AveragePairwiseDistance = mean(normalizedDistances(k,:)); | ||
candidateLocations(k).AdjustedDistance = 0; % A small fix for first outliers to make them have same fields with second outliers | ||
end | ||
|
||
candidateLocations = nestedSortStruct(candidateLocations, 'AveragePairwiseDistance'); % Sort by AveragePairwiseDistance | ||
idx = []; | ||
for k = length(candidateLocations) :-1:1 | ||
if(candidateLocations(k).AveragePairwiseDistance > Settings.NearestNeighbourThreshold) | ||
if(remaining > Settings.NearestNeighbourNumber) % Make sure we have at least k items to triangulate. | ||
idx = [idx k]; | ||
remaining = remaining - 1; | ||
else | ||
break; | ||
end | ||
end | ||
end | ||
|
||
outliers2D = candidateLocations(idx); | ||
candidateLocations(idx) = []; % Remove outliers | ||
|
||
outliers = [outliers2D, outliersSimilarity]; % Merge outliers obtained from two stages PHASE 1 and PHASE 2 | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
function [settings] = LoadSettings(fileName) | ||
|
||
% Load settings from ini file. | ||
% Input : filename - name of the file | ||
% | ||
% Output : settings - a struct containing key value pairs | ||
|
||
addpath('lib/ini2struct'); | ||
settings = ini2struct(fileName); | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
function normalizedData = NormalizeData(data, min, max, average, method, sigma) | ||
|
||
% Normalize data | ||
% | ||
% Inputs: data - Input data to be normalized | ||
% min - Minimum of the data range | ||
% max - Maximum of the data range | ||
% average - Average of the data range | ||
% method - Method to use in normalization | ||
% sigma - Sigma for exponential normalization | ||
% | ||
% Outputs: normalizedData - Normalized data | ||
|
||
if(strcmp(method,'EXP')) | ||
normalizedData = exp(-((data)^2 ./ (2 * (sigma * average)^2))); % Subtract from sigma so that higher values are better scores. | ||
elseif(strcmp(method,'MAX')) | ||
normalizedData = 1 - ((data - min) ./ (max - min)); | ||
else | ||
normalizedData = 0; | ||
disp('Error in normalization!!'); | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
function [latitude, longitude] = PredictLocation(candidateLocations, method, Settings) | ||
|
||
% Given a set of candidate locations return the most likely estimated location. | ||
% | ||
% Inputs: candidateLocations- Candidate locations is an array of latitude, longitude | ||
% method - Similarity metric (JointScore or MatchScore) | ||
% Settings - Application wide settings used to store algorithm parameters, paths etc. | ||
% | ||
% Outputs: latitude - Estimated latitude | ||
% longitude - Estimated longitude | ||
|
||
n = length(candidateLocations); | ||
positions = [candidateLocations.Latitude; candidateLocations.Longitude]; | ||
if(strcmp(method, 'JointScore')) | ||
subtractedScores = bsxfun(@minus, 1, [candidateLocations.JointScore]); % Subtract scores, so that lower the values better the score | ||
elseif(strcmp(method, 'MatchScore')) | ||
subtractedScores = bsxfun(@minus, 1, [candidateLocations.MatchScore]); % Subtract scores, so that lower the values better the score | ||
else | ||
return; | ||
end | ||
|
||
weightedPositions = bsxfun(@times, positions, subtractedScores); | ||
sumOfScores = sum(subtractedScores); | ||
latitude = sum(weightedPositions(1, : )) / sumOfScores; | ||
longitude = sum(weightedPositions(2, : )) / sumOfScores; | ||
end |
Oops, something went wrong.