├── CMC.m ├── CUHK01_PCA.m ├── Dataset ├── custom_gal.mat └── custom_probe.mat ├── Demo_LOMO.m ├── Demo_XQDA.m ├── EvalCMC.m ├── Feature Extraction ├── Demo_LOMO.m └── LOMO.m ├── Images ├── a.md ├── fig1.png ├── stages.png └── subjects.png ├── LOMO.m ├── MahDist.m ├── README.md ├── SILTP.m ├── XQDA.m └── report.m /CMC.m: -------------------------------------------------------------------------------- 1 | function acc = CMC(dist,Rank) 2 | 3 | [~,index] = sort(dist,2); 4 | rank = zeros(size(dist,1),1); 5 | 6 | for i = 1:size(index,1) 7 | rank(i) = find(index(i,:) == i); 8 | end 9 | 10 | acc = zeros(1,Rank); 11 | for i = 1:Rank 12 | acc(i) = 100*mean(rank<=i); 13 | end 14 | 15 | plot(1:Rank, acc,'LineWidth',3); 16 | grid on; 17 | xlabel('Rank') 18 | ylabel('Accuracy') 19 | title('CMC curve') 20 | 21 | end 22 | -------------------------------------------------------------------------------- /CUHK01_PCA.m: -------------------------------------------------------------------------------- 1 | %CUHK01 + LOMO 2 | %Neelabhro Roy 3 | %IIIT-Delhi 4 | 5 | clear; 6 | clc; 7 | close all; 8 | 9 | feaFile = 'cuhk01_lomo.mat'; 10 | %pcaFile = 'CUHK01_LOMO_XQDA.mat'; 11 | 12 | %feaFile1 = 'custom_probe2.mat'; 13 | %feaFile2 = 'custom_gal2.mat'; 14 | 15 | numClass = 40; 16 | numFolds = 20; 17 | numRanks = 20; 18 | 19 | %% load the extracted LOMO features 20 | %load(feaFile1, 'probe'); 21 | %load(feaFile2, 'gallery'); 22 | galFea = gallery(:,1 : numClass); 23 | probFea = probe(:,1 : numClass); 24 | galFea = galFea'; 25 | probFea = probFea'; 26 | %p = randperm(numClass); 27 | 28 | galFea1 = galFea( (1:numClass/2),: ); 29 | probFea1 = probFea( (1:numClass/2), : ); 30 | galFea2 = galFea((numClass/2+1 : end), : ); 31 | probFea2 = probFea((numClass/2+1 : end), : ); 32 | 33 | TrainSet = zeros(40,26960); 34 | TrainSet(1:20 ,:) = galFea1; 35 | TrainSet(21: end,:) = probFea1; 36 | 37 | t0 = tic; 38 | 39 | trainTime = toc(t0); 40 | 41 | TestSet = zeros(40,26960); 42 | TestSet(1:20 ,:) = galFea2; 43 | TestSet(21: end,:) = probFea2; 44 | [X ,W] = matlabPCA(TrainSet',20); -------------------------------------------------------------------------------- /Dataset/custom_gal.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/Dataset/custom_gal.mat -------------------------------------------------------------------------------- /Dataset/custom_probe.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/Dataset/custom_probe.mat -------------------------------------------------------------------------------- /Demo_LOMO.m: -------------------------------------------------------------------------------- 1 | %Neelabhro Roy 2 | %IIIT, Delhi 3 | 4 | %% This is a demo for the LOMO feature extraction 5 | 6 | clear; 7 | clc; 8 | close all; 9 | 10 | %imgDir = 'PRID450S/cam_a/'; 11 | imgDir = 'ncamA/'; 12 | %addpath('../bin/'); 13 | 14 | %% Get image list 15 | %list = dir(['PRID450S/cam_a/*.png']); 16 | list = dir(['ncamA/*.png']); 17 | n = length(list); 18 | 19 | %% Allocate memory 20 | %info = imfinfo([imgDir, list(1).name]); 21 | %images = zeros(info.Height, info.Width, 3, n, 'uint8'); 22 | 23 | %% read images 24 | for i = 1 : n 25 | info = imfinfo([imgDir, list(i).name]); 26 | images = zeros(info.Height, info.Width, 3,'uint8'); 27 | images(:,:,:,i) = imread([imgDir, list(i).name]); 28 | images1(:,:,:,i) = imresize(images(:,:,:,i),[128 48]); 29 | end 30 | 31 | %% extract features. Run with a set of images is usually faster than that one by one, but requires more memory. 32 | imshow(images1(:,:,:,i)); 33 | gallery = LOMO(images1); 34 | 35 | %% if you need to set different parameters other than the defaults, set them accordingly 36 | %{ 37 | options.numScales = 3; 38 | options.blockSize = 10; 39 | options.blockStep = 5; 40 | options.hsvBins = [8,8,8]; 41 | options.tau = 0.3; 42 | options.R = [3, 5]; 43 | options.numPoints = 4; 44 | 45 | descriptors = LOMO(images, options); 46 | %} 47 | 48 | %rmpath('../bin/'); -------------------------------------------------------------------------------- /Demo_XQDA.m: -------------------------------------------------------------------------------- 1 | %% This is a demo for the XQDA metric learning, as well as the evaluation on the VIPeR database. You can run this script to reproduce our CVPR 2015 results. 2 | % Note: this demo requires about 1.0-1.4GB of memory. 3 | 4 | close all; clear; clc; 5 | 6 | feaFile1 = 'custom_probe2.mat'; 7 | feaFile2 = 'custom_gal2.mat'; 8 | 9 | numClass = 40; 10 | numFolds = 10; 11 | numRanks = 20; 12 | 13 | %% load the extracted LOMO features 14 | load(feaFile1, 'probe'); 15 | load(feaFile2, 'gallery'); 16 | galFea = gallery(:,1 : numClass); 17 | probFea = probe(:,1 : numClass); 18 | galFea = galFea'; 19 | probFea = probFea'; 20 | clear descriptors 21 | 22 | %% set the seed of the random stream. The results reported in our CVPR 2015 paper are achieved by setting seed = 0. 23 | seed = 0; 24 | rng(seed); 25 | 26 | %% evaluate 27 | cms = zeros(numFolds, numRanks); 28 | 29 | for nf = 1 : numFolds 30 | p = randperm(numClass); 31 | 32 | galFea1 = galFea( p(1:numClass/2), : ); 33 | probFea1 = probFea( p(1:numClass/2), : ); 34 | 35 | t0 = tic; 36 | [W, M] = XQDA(galFea1, probFea1, (1:numClass/2)', (1:numClass/2)'); 37 | 38 | %{ 39 | %% if you need to set different parameters other than the defaults, set them accordingly 40 | options.lambda = 0.001; 41 | options.qdaDims = -1; 42 | options.verbose = true; 43 | [W, M] = XQDA(galFea1, probFea1, (1:numClass/2)', (1:numClass/2)', options); 44 | %} 45 | 46 | 47 | trainTime = toc(t0); 48 | galFea2 = galFea(p(numClass/2+1 : end), : ); 49 | probFea2 = probFea(p(numClass/2+1 : end), : ); 50 | 51 | 52 | 53 | t0 = tic; 54 | dist = MahDist(M, galFea2 * W, probFea2 * W); 55 | clear galFea2 probFea2 M W 56 | matchTime = toc(t0); 57 | 58 | fprintf('Fold %d: ', nf); 59 | fprintf('Training time: %.3g seconds. ', trainTime); 60 | fprintf('Matching time: %.3g seconds.\n', matchTime); 61 | grid on; 62 | cms(nf,:) = EvalCMC( -dist,1 : numClass / 2, 1 : numClass / 2, numRanks ); 63 | clear dist 64 | 65 | fprintf(' Rank1, Rank5, Rank10, Rank15, Rank20\n'); 66 | fprintf('%5.2f%%, %5.2f%%, %5.2f%%, %5.2f%%, %5.2f%%\n\n', cms(nf,[1,5,10]) * 100); 67 | end 68 | 69 | meanCms = mean(cms); 70 | plot(1 : numRanks, meanCms); 71 | 72 | -------------------------------------------------------------------------------- /EvalCMC.m: -------------------------------------------------------------------------------- 1 | function cms = EvalCMC( score, galLabels, probLabels, numRanks ) 2 | %% cms = EvalCMC( score, galLabels, probLabels, numRanks ) 3 | % 4 | % A function for the CMC curve evaluation. 5 | % 6 | % Inputs: 7 | % score: score matrix with rows corresponding to gallery and columns probe. 8 | % galLabels: a vector containing class labels of each gallery sample, 9 | % corresponding to rows of the score matrix. 10 | % probLabels: a vector containing class labels of each probe sample, 11 | % corresponding to columns of the score matrix. 12 | % numRanks: the maximal number of the matching ranks. Optional. 13 | % 14 | % Outputs: 15 | % cms: the cumulative matching scores. 16 | % 17 | % Version: 1.0 18 | % Date: 2014-07-22 19 | % 20 | % Author: Shengcai Liao 21 | % Institute: National Laboratory of Pattern Recognition, 22 | % Institute of Automation, Chinese Academy of Sciences 23 | % Email: scliao@nlpr.ia.ac.cn 24 | 25 | 26 | %% preprocess 27 | if nargin >= 4 28 | numRanks = min(100, min(size(score))); 29 | end 30 | 31 | if ~iscolumn(galLabels) 32 | galLabels = galLabels'; 33 | end 34 | 35 | if ~isrow(probLabels) 36 | probLabels = probLabels'; 37 | end 38 | 39 | binaryLabels = bsxfun(@eq, galLabels, probLabels); % match / non-match labels corresponding to the score matrix 40 | 41 | if any( all(binaryLabels == false, 1) ); % check whether all probe samples belong to the gallery 42 | error('This is not a closed-set identification experiment.'); 43 | end 44 | 45 | %% get the matching rank of each probe 46 | [~, sortedIndex] = sort(score, 'descend'); % rank the score 47 | score(binaryLabels == false) = -Inf; % set scores of non-matches to -Inf 48 | clear binaryLabels 49 | [~, maxIndex] = max(score); % get the location of the maximum genuine score 50 | [probRanks, ~] = find( bsxfun(@eq, sortedIndex, maxIndex) ); % get the matching rank of each probe, by finding the location of the matches in the sorted index 51 | clear sortedIndex maxIndex 52 | 53 | %% evaluate 54 | if ~iscolumn(probRanks) 55 | probRanks = probRanks'; 56 | end 57 | 58 | T = bsxfun(@le, probRanks, 1 : numRanks); % compare the probe matching ranks to the number of retrievals 59 | cms = squeeze( mean(T) ); % average over all probes 60 | -------------------------------------------------------------------------------- /Feature Extraction/Demo_LOMO.m: -------------------------------------------------------------------------------- 1 | %Neelabhro Roy 2 | %IIIT, Delhi 3 | 4 | %% This is a demo for the LOMO feature extraction 5 | 6 | clear; 7 | clc; 8 | close all; 9 | 10 | %imgDir = 'PRID450S/cam_a/'; 11 | imgDir = 'ncamA/'; 12 | %addpath('../bin/'); 13 | 14 | %% Get image list 15 | %list = dir(['PRID450S/cam_a/*.png']); 16 | list = dir(['ncamA/*.png']); 17 | n = length(list); 18 | 19 | %% Allocate memory 20 | %info = imfinfo([imgDir, list(1).name]); 21 | %images = zeros(info.Height, info.Width, 3, n, 'uint8'); 22 | 23 | %% read images 24 | for i = 1 : n 25 | info = imfinfo([imgDir, list(i).name]); 26 | images = zeros(info.Height, info.Width, 3,'uint8'); 27 | images(:,:,:,i) = imread([imgDir, list(i).name]); 28 | images1(:,:,:,i) = imresize(images(:,:,:,i),[128 48]); 29 | end 30 | 31 | %% extract features. Run with a set of images is usually faster than that one by one, but requires more memory. 32 | imshow(images1(:,:,:,i)); 33 | gallery = LOMO(images1); 34 | 35 | %% if you need to set different parameters other than the defaults, set them accordingly 36 | %{ 37 | options.numScales = 3; 38 | options.blockSize = 10; 39 | options.blockStep = 5; 40 | options.hsvBins = [8,8,8]; 41 | options.tau = 0.3; 42 | options.R = [3, 5]; 43 | options.numPoints = 4; 44 | 45 | descriptors = LOMO(images, options); 46 | %} 47 | 48 | %rmpath('../bin/'); 49 | -------------------------------------------------------------------------------- /Feature Extraction/LOMO.m: -------------------------------------------------------------------------------- 1 | function descriptors = LOMO(images, options) 2 | %% function Descriptors = LOMO(images, options) 3 | % Function for the Local Maximal Occurrence (LOMO) feature extraction 4 | % 5 | % Input: 6 | % : a set of n RGB color images. Size: [h, w, 3, n] 7 | % [optioins]: optional parameters. A structure containing any of the 8 | % following fields: 9 | % numScales: number of pyramid scales in feature extraction. Default: 3 10 | % blockSize: size of the sub-window for histogram counting. Default: 10 11 | % blockStep: sliding step for the sub-windows. Default: 5 12 | % hsvBins: number of bins for HSV channels. Default: [8,8,8] 13 | % tau: the tau parameter in SILTP. Default: 0.3 14 | % R: the radius paramter in SILTP. Specify multiple values for multiscale SILTP. Default: [3, 5] 15 | % numPoints: number of neiborhood points for SILTP encoding. Default: 4 16 | % The above default parameters are good for 128x48 and 160x60 person 17 | % images. You may need to adjust the numScales, blockSize, and R parameters 18 | % for other smaller or higher resolutions. 19 | % 20 | % Output: 21 | % descriptors: the extracted LOMO descriptors. Size: [d, n] 22 | % 23 | % Example: 24 | % I = imread('../images/000_45_a.bmp'); 25 | % descriptor = LOMO(I); 26 | % 27 | % Reference: 28 | % Shengcai Liao, Yang Hu, Xiangyu Zhu, and Stan Z. Li. Person 29 | % re-identification by local maximal occurrence representation and metric 30 | % learning. In IEEE Conference on Computer Vision and Pattern Recognition, 2015. 31 | % 32 | % Version: 1.0 33 | % Date: 2015-04-29 34 | % 35 | % Author: Shengcai Liao 36 | % Institute: National Laboratory of Pattern Recognition, 37 | % Institute of Automation, Chinese Academy of Sciences 38 | % Email: scliao@nlpr.ia.ac.cn 39 | 40 | %% set parameters 41 | numScales = 3; 42 | blockSize = 10; 43 | blockStep = 5; 44 | 45 | hsvBins = [8,8,8]; 46 | tau = 0.3; 47 | R = [3, 5]; 48 | numPoints = 4; 49 | 50 | if nargin >= 2 51 | if isfield(options,'numScales') && ~isempty(options.numScales) && isscalar(options.numScales) && isnumeric(options.numScales) && options.numScales > 0 52 | numScales = options.numScales; 53 | fprintf('numScales = %d.\n', numScales); 54 | end 55 | if isfield(options,'blockSize') && ~isempty(options.blockSize) && isscalar(options.blockSize) && isnumeric(options.blockSize) && options.blockSize > 0 56 | blockSize = options.blockSize; 57 | fprintf('blockSize = %d.\n', blockSize); 58 | end 59 | if isfield(options,'blockStep') && ~isempty(options.blockStep) && isscalar(options.blockStep) && isnumeric(options.blockStep) && options.blockStep > 0 60 | blockStep = options.blockStep; 61 | fprintf('blockStep = %d.\n', blockStep); 62 | end 63 | if isfield(options,'hsvBins') && ~isempty(options.hsvBins) && isvector(options.blockStep) && isnumeric(options.hsvBins) && length(options.hsvBins) == 3 && all(options.hsvBins > 0) 64 | hsvBins = options.hsvBins; 65 | fprintf('hsvBins = [%d, %d, %d].\n', hsvBins); 66 | end 67 | if isfield(options,'tau') && ~isempty(options.tau) && isscalar(options.tau) && isnumeric(options.tau) && options.tau > 0 68 | tau = options.tau; 69 | fprintf('tau = %g.\n', tau); 70 | end 71 | if isfield(options,'R') && ~isempty(options.R) && isnumeric(options.R) && all(options.R > 0) 72 | R = options.R; 73 | fprintf('R = %d.\n', R); 74 | end 75 | if isfield(options,'numPoints') && ~isempty(options.numPoints) && isscalar(options.numPoints) && isnumeric(options.numPoints) && options.numPoints > 0 76 | numPoints = options.numPoints; 77 | fprintf('numPoints = %d.\n', numPoints); 78 | end 79 | end 80 | 81 | t0 = tic; 82 | 83 | %% extract Joint HSV based LOMO descriptors 84 | fea1 = PyramidMaxJointHist( images, numScales, blockSize, blockStep, hsvBins ); 85 | 86 | %% extract SILTP based LOMO descriptors 87 | fea2 = []; 88 | 89 | for i = 1 : length(R) 90 | fea2 = [fea2; PyramidMaxSILTPHist( images, numScales, blockSize, blockStep, tau, R(i), numPoints )]; %#ok 91 | end 92 | 93 | %% finishing 94 | descriptors = [fea1; fea2]; 95 | clear Fea1 Fea2 96 | 97 | feaTime = toc(t0); 98 | meanTime = feaTime / size(images, 4); 99 | fprintf('LOMO feature extraction finished. Running time: %.3f seconds in total, %.3f seconds per image.\n', feaTime, meanTime); 100 | end 101 | 102 | 103 | function descriptors = PyramidMaxJointHist( oriImgs, numScales, blockSize, blockStep, colorBins ) 104 | %% PyramidMaxJointHist: HSV based LOMO representation 105 | 106 | if nargin == 1 107 | numScales = 3; 108 | blockSize = 10; 109 | blockStep = 5; 110 | colorBins = [8,8,8]; 111 | end 112 | 113 | totalBins = prod(colorBins); 114 | numImgs = size(oriImgs, 4); 115 | images = zeros(size(oriImgs)); 116 | 117 | % color transformation 118 | for i = 1 : numImgs 119 | I = oriImgs(:,:,:,i); 120 | I = Retinex(I); 121 | 122 | I = rgb2hsv(I); 123 | I(:,:,1) = min( floor( I(:,:,1) * colorBins(1) ), colorBins(1)-1 ); 124 | I(:,:,2) = min( floor( I(:,:,2) * colorBins(2) ), colorBins(2)-1 ); 125 | I(:,:,3) = min( floor( I(:,:,3) * colorBins(3) ), colorBins(3)-1 ); 126 | images(:,:,:,i) = I; % HSV 127 | end 128 | 129 | minRow = 1; 130 | minCol = 1; 131 | descriptors = []; 132 | 133 | % Scan multi-scale blocks and compute histograms 134 | for i = 1 : numScales 135 | patterns = images(:,:,3,:) * colorBins(2) * colorBins(1) + images(:,:,2,:)*colorBins(1) + images(:,:,1,:); % HSV 136 | patterns = reshape(patterns, [], numImgs); 137 | 138 | height = size(images, 1); 139 | width = size(images, 2); 140 | maxRow = height - blockSize + 1; 141 | maxCol = width - blockSize + 1; 142 | 143 | [cols,rows] = meshgrid(minCol:blockStep:maxCol, minRow:blockStep:maxRow); % top-left positions 144 | cols = cols(:); 145 | rows = rows(:); 146 | numBlocks = length(cols); 147 | numBlocksCol = length(minCol:blockStep:maxCol); 148 | 149 | if numBlocks == 0 150 | break; 151 | end 152 | 153 | offset = bsxfun(@plus, (0 : blockSize-1)', (0 : blockSize-1) * height); % offset to the top-left positions. blockSize-by-blockSize 154 | index = sub2ind([height, width], rows, cols); 155 | index = bsxfun(@plus, offset(:), index'); % (blockSize*blockSize)-by-numBlocks 156 | patches = patterns(index(:), :); % (blockSize * blockSize * numBlocks)-by-numImgs 157 | patches = reshape(patches, [], numBlocks * numImgs); % (blockSize * blockSize)-by-(numBlocks * numChannels * numImgs) 158 | 159 | fea = hist(patches, 0 : totalBins-1); % totalBins-by-(numBlocks * numImgs) 160 | fea = reshape(fea, [totalBins, numBlocks / numBlocksCol, numBlocksCol, numImgs]); 161 | fea = max(fea, [], 3); 162 | fea = reshape(fea, [], numImgs); 163 | 164 | descriptors = [descriptors; fea]; %#ok 165 | 166 | if i < numScales 167 | images = ColorPooling(images, 'average'); 168 | end 169 | end 170 | 171 | descriptors = log(descriptors + 1); 172 | descriptors = normc(descriptors); 173 | end 174 | 175 | 176 | function outImages = ColorPooling(images, method) 177 | [height, width, numChannels, numImgs] = size(images); 178 | outImages = images; 179 | 180 | if mod(height, 2) == 1 181 | outImages(end, :, :, :) = []; 182 | height = height - 1; 183 | end 184 | 185 | if mod(width, 2) == 1 186 | outImages(:, end, :, :) = []; 187 | width = width - 1; 188 | end 189 | 190 | if height == 0 || width == 0 191 | error('Over scaled image: height=%d, width=%d.', height, width); 192 | end 193 | 194 | height = height / 2; 195 | width = width / 2; 196 | 197 | outImages = reshape(outImages, 2, height, 2, width, numChannels, numImgs); 198 | outImages = permute(outImages, [2, 4, 5, 6, 1, 3]); 199 | outImages = reshape(outImages, height, width, numChannels, numImgs, 2*2); 200 | 201 | if strcmp(method, 'average') 202 | outImages = floor(mean(outImages, 5)); 203 | else if strcmp(method, 'max') 204 | outImages = max(outImages, [], 5); 205 | else 206 | error('Error pooling method: %s.', method); 207 | end 208 | end 209 | end 210 | 211 | function descriptors = PyramidMaxSILTPHist( oriImgs, numScales, blockSize, blockStep, tau, R, numPoints ) 212 | %% PyramidMaxSILTPHist: SILTP based LOMO representation 213 | 214 | if nargin == 1 215 | numScales = 3; 216 | blockSize = 10; 217 | blockStep = 5; 218 | tau = 0.3; 219 | R = 5; 220 | numPoints = 4; 221 | end 222 | 223 | totalBins = 3^numPoints; 224 | 225 | [imgHeight, imgWidth, ~, numImgs] = size(oriImgs); 226 | images = zeros(imgHeight,imgWidth, numImgs); 227 | 228 | % Convert gray images 229 | for i = 1 : numImgs 230 | I = oriImgs(:,:,:,i); 231 | I = rgb2gray(I); 232 | images(:,:,i) = double(I) / 255; 233 | end 234 | 235 | minRow = 1; 236 | minCol = 1; 237 | descriptors = []; 238 | 239 | % Scan multi-scale blocks and compute histograms 240 | for i = 1 : numScales 241 | height = size(images, 1); 242 | width = size(images, 2); 243 | 244 | if width < R * 2 + 1 245 | fprintf('Skip scale R = %d, width = %d.\n', R, width); 246 | continue; 247 | end 248 | 249 | patterns = SILTP(images, tau, R, numPoints); 250 | patterns = reshape(patterns, [], numImgs); 251 | 252 | maxRow = height - blockSize + 1; 253 | maxCol = width - blockSize + 1; 254 | 255 | [cols,rows] = meshgrid(minCol:blockStep:maxCol, minRow:blockStep:maxRow); % top-left positions 256 | cols = cols(:); 257 | rows = rows(:); 258 | numBlocks = length(cols); 259 | numBlocksCol = length(minCol:blockStep:maxCol); 260 | 261 | if numBlocks == 0 262 | break; 263 | end 264 | 265 | offset = bsxfun(@plus, (0 : blockSize-1)', (0 : blockSize-1) * height); % offset to the top-left positions. blockSize-by-blockSize 266 | index = sub2ind([height, width], rows, cols); 267 | index = bsxfun(@plus, offset(:), index'); % (blockSize*blockSize)-by-numBlocks 268 | patches = patterns(index(:), :); % (blockSize * blockSize * numBlocks)-by-numImgs 269 | patches = reshape(patches, [], numBlocks * numImgs); % (blockSize * blockSize)-by-(numBlocks * numChannels * numImgs) 270 | 271 | fea = hist(patches, 0:totalBins-1); % totalBins-by-(numBlocks * numImgs) 272 | fea = reshape(fea, [totalBins, numBlocks / numBlocksCol, numBlocksCol, numImgs]); 273 | fea = max(fea, [], 3); 274 | fea = reshape(fea, [], numImgs); 275 | 276 | descriptors = [descriptors; fea]; %#ok 277 | 278 | if i < numScales 279 | images = Pooling(images, 'average'); 280 | end 281 | end 282 | 283 | descriptors = log(descriptors + 1); 284 | descriptors = normc(descriptors); 285 | end 286 | 287 | 288 | function outImages = Pooling(images, method) 289 | [height, width, numImgs] = size(images); 290 | outImages = images; 291 | 292 | if mod(height, 2) == 1 293 | outImages(end, :, :) = []; 294 | height = height - 1; 295 | end 296 | 297 | if mod(width, 2) == 1 298 | outImages(:, end, :) = []; 299 | width = width - 1; 300 | end 301 | 302 | if height == 0 || width == 0 303 | error('Over scaled image: height=%d, width=%d.', height, width); 304 | end 305 | 306 | height = height / 2; 307 | width = width / 2; 308 | 309 | outImages = reshape(outImages, 2, height, 2, width, numImgs); 310 | outImages = permute(outImages, [2, 4, 5, 1, 3]); 311 | outImages = reshape(outImages, height, width, numImgs, 2*2); 312 | 313 | if strcmp(method, 'average') 314 | outImages = mean(outImages, 4); 315 | else if strcmp(method, 'max') 316 | outImages = max(outImages, [], 4); 317 | else 318 | error('Error pooling method: %s.', method); 319 | end 320 | end 321 | end 322 | -------------------------------------------------------------------------------- /Images/a.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Images/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/Images/fig1.png -------------------------------------------------------------------------------- /Images/stages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/Images/stages.png -------------------------------------------------------------------------------- /Images/subjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/Images/subjects.png -------------------------------------------------------------------------------- /LOMO.m: -------------------------------------------------------------------------------- 1 | function descriptors = LOMO(images, options) 2 | %% function Descriptors = LOMO(images, options) 3 | % Function for the Local Maximal Occurrence (LOMO) feature extraction 4 | % 5 | % Input: 6 | % : a set of n RGB color images. Size: [h, w, 3, n] 7 | % [optioins]: optional parameters. A structure containing any of the 8 | % following fields: 9 | % numScales: number of pyramid scales in feature extraction. Default: 3 10 | % blockSize: size of the sub-window for histogram counting. Default: 10 11 | % blockStep: sliding step for the sub-windows. Default: 5 12 | % hsvBins: number of bins for HSV channels. Default: [8,8,8] 13 | % tau: the tau parameter in SILTP. Default: 0.3 14 | % R: the radius paramter in SILTP. Specify multiple values for multiscale SILTP. Default: [3, 5] 15 | % numPoints: number of neiborhood points for SILTP encoding. Default: 4 16 | % The above default parameters are good for 128x48 and 160x60 person 17 | % images. You may need to adjust the numScales, blockSize, and R parameters 18 | % for other smaller or higher resolutions. 19 | % 20 | % Output: 21 | % descriptors: the extracted LOMO descriptors. Size: [d, n] 22 | % 23 | % Example: 24 | % I = imread('../images/000_45_a.bmp'); 25 | % descriptor = LOMO(I); 26 | % 27 | % Reference: 28 | % Shengcai Liao, Yang Hu, Xiangyu Zhu, and Stan Z. Li. Person 29 | % re-identification by local maximal occurrence representation and metric 30 | % learning. In IEEE Conference on Computer Vision and Pattern Recognition, 2015. 31 | % 32 | % Version: 1.0 33 | % Date: 2015-04-29 34 | % 35 | % Author: Shengcai Liao 36 | % Institute: National Laboratory of Pattern Recognition, 37 | % Institute of Automation, Chinese Academy of Sciences 38 | % Email: scliao@nlpr.ia.ac.cn 39 | 40 | %% set parameters 41 | numScales = 3; 42 | blockSize = 10; 43 | blockStep = 5; 44 | 45 | hsvBins = [8,8,8]; 46 | tau = 0.3; 47 | R = [3, 5]; 48 | numPoints = 4; 49 | 50 | if nargin >= 2 51 | if isfield(options,'numScales') && ~isempty(options.numScales) && isscalar(options.numScales) && isnumeric(options.numScales) && options.numScales > 0 52 | numScales = options.numScales; 53 | fprintf('numScales = %d.\n', numScales); 54 | end 55 | if isfield(options,'blockSize') && ~isempty(options.blockSize) && isscalar(options.blockSize) && isnumeric(options.blockSize) && options.blockSize > 0 56 | blockSize = options.blockSize; 57 | fprintf('blockSize = %d.\n', blockSize); 58 | end 59 | if isfield(options,'blockStep') && ~isempty(options.blockStep) && isscalar(options.blockStep) && isnumeric(options.blockStep) && options.blockStep > 0 60 | blockStep = options.blockStep; 61 | fprintf('blockStep = %d.\n', blockStep); 62 | end 63 | if isfield(options,'hsvBins') && ~isempty(options.hsvBins) && isvector(options.blockStep) && isnumeric(options.hsvBins) && length(options.hsvBins) == 3 && all(options.hsvBins > 0) 64 | hsvBins = options.hsvBins; 65 | fprintf('hsvBins = [%d, %d, %d].\n', hsvBins); 66 | end 67 | if isfield(options,'tau') && ~isempty(options.tau) && isscalar(options.tau) && isnumeric(options.tau) && options.tau > 0 68 | tau = options.tau; 69 | fprintf('tau = %g.\n', tau); 70 | end 71 | if isfield(options,'R') && ~isempty(options.R) && isnumeric(options.R) && all(options.R > 0) 72 | R = options.R; 73 | fprintf('R = %d.\n', R); 74 | end 75 | if isfield(options,'numPoints') && ~isempty(options.numPoints) && isscalar(options.numPoints) && isnumeric(options.numPoints) && options.numPoints > 0 76 | numPoints = options.numPoints; 77 | fprintf('numPoints = %d.\n', numPoints); 78 | end 79 | end 80 | 81 | t0 = tic; 82 | 83 | %% extract Joint HSV based LOMO descriptors 84 | fea1 = PyramidMaxJointHist( images, numScales, blockSize, blockStep, hsvBins ); 85 | 86 | %% extract SILTP based LOMO descriptors 87 | fea2 = []; 88 | 89 | for i = 1 : length(R) 90 | fea2 = [fea2; PyramidMaxSILTPHist( images, numScales, blockSize, blockStep, tau, R(i), numPoints )]; %#ok 91 | end 92 | 93 | %% finishing 94 | descriptors = [fea1; fea2]; 95 | clear Fea1 Fea2 96 | 97 | feaTime = toc(t0); 98 | meanTime = feaTime / size(images, 4); 99 | fprintf('LOMO feature extraction finished. Running time: %.3f seconds in total, %.3f seconds per image.\n', feaTime, meanTime); 100 | end 101 | 102 | 103 | function descriptors = PyramidMaxJointHist( oriImgs, numScales, blockSize, blockStep, colorBins ) 104 | %% PyramidMaxJointHist: HSV based LOMO representation 105 | 106 | if nargin == 1 107 | numScales = 3; 108 | blockSize = 10; 109 | blockStep = 5; 110 | colorBins = [8,8,8]; 111 | end 112 | 113 | totalBins = prod(colorBins); 114 | numImgs = size(oriImgs, 4); 115 | images = zeros(size(oriImgs)); 116 | 117 | % color transformation 118 | for i = 1 : numImgs 119 | I = oriImgs(:,:,:,i); 120 | I = Retinex(I); 121 | 122 | I = rgb2hsv(I); 123 | I(:,:,1) = min( floor( I(:,:,1) * colorBins(1) ), colorBins(1)-1 ); 124 | I(:,:,2) = min( floor( I(:,:,2) * colorBins(2) ), colorBins(2)-1 ); 125 | I(:,:,3) = min( floor( I(:,:,3) * colorBins(3) ), colorBins(3)-1 ); 126 | images(:,:,:,i) = I; % HSV 127 | end 128 | 129 | minRow = 1; 130 | minCol = 1; 131 | descriptors = []; 132 | 133 | % Scan multi-scale blocks and compute histograms 134 | for i = 1 : numScales 135 | patterns = images(:,:,3,:) * colorBins(2) * colorBins(1) + images(:,:,2,:)*colorBins(1) + images(:,:,1,:); % HSV 136 | patterns = reshape(patterns, [], numImgs); 137 | 138 | height = size(images, 1); 139 | width = size(images, 2); 140 | maxRow = height - blockSize + 1; 141 | maxCol = width - blockSize + 1; 142 | 143 | [cols,rows] = meshgrid(minCol:blockStep:maxCol, minRow:blockStep:maxRow); % top-left positions 144 | cols = cols(:); 145 | rows = rows(:); 146 | numBlocks = length(cols); 147 | numBlocksCol = length(minCol:blockStep:maxCol); 148 | 149 | if numBlocks == 0 150 | break; 151 | end 152 | 153 | offset = bsxfun(@plus, (0 : blockSize-1)', (0 : blockSize-1) * height); % offset to the top-left positions. blockSize-by-blockSize 154 | index = sub2ind([height, width], rows, cols); 155 | index = bsxfun(@plus, offset(:), index'); % (blockSize*blockSize)-by-numBlocks 156 | patches = patterns(index(:), :); % (blockSize * blockSize * numBlocks)-by-numImgs 157 | patches = reshape(patches, [], numBlocks * numImgs); % (blockSize * blockSize)-by-(numBlocks * numChannels * numImgs) 158 | 159 | fea = hist(patches, 0 : totalBins-1); % totalBins-by-(numBlocks * numImgs) 160 | fea = reshape(fea, [totalBins, numBlocks / numBlocksCol, numBlocksCol, numImgs]); 161 | fea = max(fea, [], 3); 162 | fea = reshape(fea, [], numImgs); 163 | 164 | descriptors = [descriptors; fea]; %#ok 165 | 166 | if i < numScales 167 | images = ColorPooling(images, 'average'); 168 | end 169 | end 170 | 171 | descriptors = log(descriptors + 1); 172 | descriptors = normc(descriptors); 173 | end 174 | 175 | 176 | function outImages = ColorPooling(images, method) 177 | [height, width, numChannels, numImgs] = size(images); 178 | outImages = images; 179 | 180 | if mod(height, 2) == 1 181 | outImages(end, :, :, :) = []; 182 | height = height - 1; 183 | end 184 | 185 | if mod(width, 2) == 1 186 | outImages(:, end, :, :) = []; 187 | width = width - 1; 188 | end 189 | 190 | if height == 0 || width == 0 191 | error('Over scaled image: height=%d, width=%d.', height, width); 192 | end 193 | 194 | height = height / 2; 195 | width = width / 2; 196 | 197 | outImages = reshape(outImages, 2, height, 2, width, numChannels, numImgs); 198 | outImages = permute(outImages, [2, 4, 5, 6, 1, 3]); 199 | outImages = reshape(outImages, height, width, numChannels, numImgs, 2*2); 200 | 201 | if strcmp(method, 'average') 202 | outImages = floor(mean(outImages, 5)); 203 | else if strcmp(method, 'max') 204 | outImages = max(outImages, [], 5); 205 | else 206 | error('Error pooling method: %s.', method); 207 | end 208 | end 209 | end 210 | 211 | function descriptors = PyramidMaxSILTPHist( oriImgs, numScales, blockSize, blockStep, tau, R, numPoints ) 212 | %% PyramidMaxSILTPHist: SILTP based LOMO representation 213 | 214 | if nargin == 1 215 | numScales = 3; 216 | blockSize = 10; 217 | blockStep = 5; 218 | tau = 0.3; 219 | R = 5; 220 | numPoints = 4; 221 | end 222 | 223 | totalBins = 3^numPoints; 224 | 225 | [imgHeight, imgWidth, ~, numImgs] = size(oriImgs); 226 | images = zeros(imgHeight,imgWidth, numImgs); 227 | 228 | % Convert gray images 229 | for i = 1 : numImgs 230 | I = oriImgs(:,:,:,i); 231 | I = rgb2gray(I); 232 | images(:,:,i) = double(I) / 255; 233 | end 234 | 235 | minRow = 1; 236 | minCol = 1; 237 | descriptors = []; 238 | 239 | % Scan multi-scale blocks and compute histograms 240 | for i = 1 : numScales 241 | height = size(images, 1); 242 | width = size(images, 2); 243 | 244 | if width < R * 2 + 1 245 | fprintf('Skip scale R = %d, width = %d.\n', R, width); 246 | continue; 247 | end 248 | 249 | patterns = SILTP(images, tau, R, numPoints); 250 | patterns = reshape(patterns, [], numImgs); 251 | 252 | maxRow = height - blockSize + 1; 253 | maxCol = width - blockSize + 1; 254 | 255 | [cols,rows] = meshgrid(minCol:blockStep:maxCol, minRow:blockStep:maxRow); % top-left positions 256 | cols = cols(:); 257 | rows = rows(:); 258 | numBlocks = length(cols); 259 | numBlocksCol = length(minCol:blockStep:maxCol); 260 | 261 | if numBlocks == 0 262 | break; 263 | end 264 | 265 | offset = bsxfun(@plus, (0 : blockSize-1)', (0 : blockSize-1) * height); % offset to the top-left positions. blockSize-by-blockSize 266 | index = sub2ind([height, width], rows, cols); 267 | index = bsxfun(@plus, offset(:), index'); % (blockSize*blockSize)-by-numBlocks 268 | patches = patterns(index(:), :); % (blockSize * blockSize * numBlocks)-by-numImgs 269 | patches = reshape(patches, [], numBlocks * numImgs); % (blockSize * blockSize)-by-(numBlocks * numChannels * numImgs) 270 | 271 | fea = hist(patches, 0:totalBins-1); % totalBins-by-(numBlocks * numImgs) 272 | fea = reshape(fea, [totalBins, numBlocks / numBlocksCol, numBlocksCol, numImgs]); 273 | fea = max(fea, [], 3); 274 | fea = reshape(fea, [], numImgs); 275 | 276 | descriptors = [descriptors; fea]; %#ok 277 | 278 | if i < numScales 279 | images = Pooling(images, 'average'); 280 | end 281 | end 282 | 283 | descriptors = log(descriptors + 1); 284 | descriptors = normc(descriptors); 285 | end 286 | 287 | 288 | function outImages = Pooling(images, method) 289 | [height, width, numImgs] = size(images); 290 | outImages = images; 291 | 292 | if mod(height, 2) == 1 293 | outImages(end, :, :) = []; 294 | height = height - 1; 295 | end 296 | 297 | if mod(width, 2) == 1 298 | outImages(:, end, :) = []; 299 | width = width - 1; 300 | end 301 | 302 | if height == 0 || width == 0 303 | error('Over scaled image: height=%d, width=%d.', height, width); 304 | end 305 | 306 | height = height / 2; 307 | width = width / 2; 308 | 309 | outImages = reshape(outImages, 2, height, 2, width, numImgs); 310 | outImages = permute(outImages, [2, 4, 5, 1, 3]); 311 | outImages = reshape(outImages, height, width, numImgs, 2*2); 312 | 313 | if strcmp(method, 'average') 314 | outImages = mean(outImages, 4); 315 | else if strcmp(method, 'max') 316 | outImages = max(outImages, [], 4); 317 | else 318 | error('Error pooling method: %s.', method); 319 | end 320 | end 321 | end 322 | -------------------------------------------------------------------------------- /MahDist.m: -------------------------------------------------------------------------------- 1 | function dist = MahDist(M, Xg, Xp) 2 | %% function dist = MahDist(M, Xg, Xp) 3 | % Mahalanobis distance 4 | % 5 | % Input: 6 | % : the metric kernel 7 | % : features of the gallery samples. Size: [n, d] 8 | % [Xp]: features of the probe samples. Optional. Size: [m, d] 9 | % 10 | % Note: MahDist(M, Xg) is the same as MahDist(M, Xg, Xg). 11 | % 12 | % Output: 13 | % dist: the computed distance matrix between Xg and Xp 14 | % 15 | % Reference: 16 | % Shengcai Liao, Yang Hu, Xiangyu Zhu, and Stan Z. Li. Person 17 | % re-identification by local maximal occurrence representation and metric 18 | % learning. In IEEE Conference on Computer Vision and Pattern Recognition, 2015. 19 | % 20 | % Version: 1.0 21 | % Date: 2014-05-15 22 | % 23 | % Author: Shengcai Liao 24 | % Institute: National Laboratory of Pattern Recognition, 25 | % Institute of Automation, Chinese Academy of Sciences 26 | % Email: scliao@nlpr.ia.ac.cn 27 | 28 | 29 | if nargin == 2 30 | D = Xg * M * Xg'; 31 | u = diag(D); 32 | dist = bsxfun(@plus, u, u') - 2 * D; 33 | else 34 | u = sum( (Xg * M) .* Xg, 2); 35 | v = sum( (Xp * M) .* Xp, 2); 36 | dist = bsxfun(@plus, u, v') - 2 * Xg * M * Xp'; 37 | end 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs 2 | ### Authors: Neelabhro Roy (IIIT-Delhi) & Sauranil Debarshi (IISc Bangalore) 3 | 4 | Cite us: 5 | 6 | - Plain text: 7 | 8 | ```text 9 | N. Roy and S. Debarshi, "UAV-based Person Re-Identification and Dynamic Image Routing using Wireless Mesh Networking," 2020 7th International Conference on Signal Processing and Integrated Networks (SPIN), 2020, pp. 914-917, doi: 10.1109/SPIN48934.2020.9071078. 10 | ``` 11 | 12 | - BibLatex/BibTex: 13 | 14 | ```bibtex 15 | @INPROCEEDINGS{9071078, 16 | author={Roy, Neelabhro and Debarshi, Sauranil}, 17 | booktitle={2020 7th International Conference on Signal Processing and Integrated Networks (SPIN)}, 18 | title={UAV-based Person Re-Identification and Dynamic Image Routing using Wireless Mesh Networking}, 19 | year={2020}, 20 | volume={}, 21 | number={}, 22 | pages={914-917}, 23 | doi={10.1109/SPIN48934.2020.9071078}} 24 | 25 | ``` 26 | (https://ieeexplore.ieee.org/document/9071078) 27 | 28 | ### Abstract: 29 | Person Re-Identification (PRID) has been one of the most challenging tasks in intelligent video surveillance. Most existing PRID based surveillance methods rely on a single camera mounted on emplacements. This approach assumes that any scrutinized person appears again in the field of view of that cam-era, which is unreliable in real-world settings. In this paper, we propose a system wherein we explore on-board re-identification of persons using cameras mounted on Unmanned Aerial Vehicles (UAVs). The captured images are dynamically shared between multiple UAVs by virtue of wireless mesh networking, also making use of the Robot Operating System (ROS) for on-ground control of the UAVs from a control station, facilitating the exchange of images between them. We demonstrate our approach on an institutional dataset we created, and our experimental results show that the system could be indispensable in aiding airborne surveillance operations. 30 | 31 | #### Wireless mesh network consisting of UAVs and a base station: 32 | ![](Images/fig1.png) 33 | 34 | #### Experimental setting for building the dataset: 35 | ![](Images/subjects.png) 36 | 37 | #### Illustration of our experimental setup: 38 | ![](Images/stages.png) 39 | -------------------------------------------------------------------------------- /SILTP.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neelabhro/UAV-based-Person-Re-Identification-and-Dynamic-Image-Routing-using-WMNs/3395cf8599a4b94501cb9feaabded1a84b6104fb/SILTP.m -------------------------------------------------------------------------------- /XQDA.m: -------------------------------------------------------------------------------- 1 | function [W, M, inCov, exCov] = XQDA(galX, probX, galLabels, probLabels, options) 2 | %% function [W, M, inCov, exCov] = XQDA(galX, probX, galLabels, probLabels, options) 3 | % Cross-view Quadratic Discriminant Analysis for subspace and metric 4 | % learning 5 | % 6 | % Input: 7 | % : features of gallery samples. Size: [n, d] 8 | % : features of probe samples. Size: [m, d] 9 | % : class labels of the gallery samples 10 | % : class labels of the probe samples 11 | % [options]: optional parameters. A structure containing any of the 12 | % following fields: 13 | % lambda: the regularizer. Default: 0.001 14 | % qdaDims: the number of dimensions to be preserved in the learned 15 | % subspace. Negative values indicate automatic dimension selection by 16 | % perserving latent values larger than 1. Default: -1. 17 | % verbose: whether to print the learning details. Default: false 18 | % 19 | % Output: 20 | % W: the subspace projection matrix. Size: [d, r], where r is the 21 | % subspace dimension. 22 | % M: the learned metric kernel. Size: [r,r] 23 | % inCov: covariance matrix of the intra-personal difference class. Size: 24 | % [r,r] 25 | % exCov: covriance matrix of the extra-personal difference class. Size: 26 | % [r,r] 27 | % 28 | % With W, inCov, and exCov, we can quickly learn another metric of different dimensions: 29 | % W2 = W(:, 1:r2); 30 | % M2 = inv(inCov(1:r2, 1:r2)) - inv(exCov(1:r2, 1:r2)); 31 | % 32 | % Example: 33 | % Please see Demo_XQDA.m. 34 | % 35 | % Reference: 36 | % Shengcai Liao, Yang Hu, Xiangyu Zhu, and Stan Z. Li. Person 37 | % re-identification by local maximal occurrence representation and metric 38 | % learning. In IEEE Conference on Computer Vision and Pattern Recognition, 2015. 39 | % 40 | % Version: 1.0 41 | % Date: 2015-04-30 42 | % 43 | % Author: Shengcai Liao 44 | % Institute: National Laboratory of Pattern Recognition, 45 | % Institute of Automation, Chinese Academy of Sciences 46 | % Email: scliao@nlpr.ia.ac.cn 47 | 48 | 49 | lambda = 0.001; 50 | qdaDims = -1; 51 | verbose = false; 52 | 53 | if nargin >= 5 && ~isempty(options) 54 | if isfield(options,'lambda') && ~isempty(options.lambda) && isscalar(options.lambda) && isnumeric(options.lambda) 55 | lambda = options.lambda; 56 | end 57 | if isfield(options,'qdaDims') && ~isempty(options.qdaDims) && isscalar(options.qdaDims) && isnumeric(options.qdaDims) && options.qdaDims > 0 58 | qdaDims = options.qdaDims; 59 | end 60 | if isfield(options,'verbose') && ~isempty(options.verbose) && isscalar(options.verbose) && islogical(options.verbose) 61 | verbose = options.verbose; 62 | end 63 | end 64 | 65 | if verbose == true 66 | fprintf('options.lambda = %g.\n', lambda); 67 | fprintf('options.qdaDims = %d.\n', qdaDims); 68 | fprintf('options.verbose = %d.\n', verbose); 69 | end 70 | 71 | [numGals, d] = size(galX); % n 72 | numProbs = size(probX, 1); % m 73 | 74 | % If d > numGals + numProbs, it is not necessary to apply XQDA on the high dimensional space. 75 | % In this case we can apply XQDA on QR decomposed space, achieving the same performance but much faster. 76 | if d > numGals + numProbs 77 | if verbose == true 78 | fprintf('\nStart to apply QR decomposition.\n'); 79 | end 80 | 81 | t0 = tic; 82 | [W, X] = qr([galX', probX'], 0); % [d, n] 83 | galX = X(:, 1:numGals)'; 84 | probX = X(:, numGals+1:end)'; 85 | d = size(X,1); 86 | clear X; 87 | 88 | if verbose == true 89 | fprintf('QR decomposition time: %.3g seconds.\n', toc(t0)); 90 | end 91 | end 92 | 93 | 94 | labels = unique([galLabels; probLabels]); 95 | c = length(labels); 96 | 97 | if verbose == true 98 | fprintf('#Classes: %d\n', c); 99 | fprintf('Compute intra/extra-class covariance matrix...'); 100 | end 101 | 102 | t0 = tic; 103 | 104 | galW = zeros(numGals, 1); 105 | galClassSum = zeros(c, d); 106 | probW = zeros(numProbs, 1); 107 | probClassSum = zeros(c, d); 108 | ni = 0; 109 | 110 | for k = 1 : c 111 | galIndex = find(galLabels == labels(k)); 112 | nk = length(galIndex); 113 | galClassSum(k, :) = sum( galX(galIndex, :), 1 ); 114 | 115 | probIndex = find(probLabels == labels(k)); 116 | mk = length(probIndex); 117 | probClassSum(k, :) = sum( probX(probIndex, :), 1 ); 118 | 119 | ni = ni + nk * mk; 120 | galW(galIndex) = sqrt(mk); 121 | probW(probIndex) = sqrt(nk); 122 | end 123 | 124 | galSum = sum(galClassSum, 1); 125 | probSum = sum(probClassSum, 1); 126 | galCov = galX' * galX; 127 | probCov = probX' * probX; 128 | 129 | galX = bsxfun( @times, galW, galX ); 130 | probX = bsxfun( @times, probW, probX ); 131 | inCov = galX' * galX + probX' * probX - galClassSum' * probClassSum - probClassSum' * galClassSum; 132 | exCov = numProbs * galCov + numGals * probCov - galSum' * probSum - probSum' * galSum - inCov; 133 | 134 | ne = numGals * numProbs - ni; 135 | inCov = inCov / ni; 136 | exCov = exCov / ne; 137 | 138 | inCov = inCov + lambda * eye(d); 139 | 140 | if verbose == true 141 | fprintf(' %.3g seconds.\n', toc(t0)); 142 | fprintf('#Intra: %d, #Extra: %d\n', ni, ne); 143 | fprintf('Compute eigen vectors...'); 144 | end 145 | 146 | 147 | t0 = tic; 148 | [V, S] = svd(inCov \ exCov); 149 | 150 | if verbose == true 151 | fprintf(' %.3g seconds.\n', toc(t0)); 152 | end 153 | 154 | latent = diag(S); 155 | [latent, index] = sort(latent, 'descend'); 156 | energy = sum(latent); 157 | minv = latent(end); 158 | 159 | r = sum(latent > .35); 160 | energy = sum(latent(1:r)) / energy; 161 | 162 | if qdaDims > r 163 | qdaDims = r; 164 | end 165 | 166 | if qdaDims <= 0 167 | qdaDims = max(1,r); 168 | end 169 | 170 | if verbose == true 171 | fprintf('Energy remained: %f, max: %f, min: %f, all min: %f, #opt-dim: %d, qda-dim: %d.\n', energy, latent(1), latent(max(1,r)), minv, r, qdaDims); 172 | end 173 | 174 | V = V(:, index(1:qdaDims)); 175 | if ~exist('W', 'var'); 176 | W = V; 177 | else 178 | W = W * V; 179 | end 180 | 181 | if verbose == true 182 | fprintf('Compute kernel matrix...'); 183 | end 184 | 185 | t0 = tic; 186 | 187 | inCov = V' * inCov * V; 188 | exCov = V' * exCov * V; 189 | M = inv(inCov) - inv(exCov); 190 | 191 | if verbose == true 192 | fprintf(' %.3g seconds.\n\n', toc(t0)); 193 | end 194 | -------------------------------------------------------------------------------- /report.m: -------------------------------------------------------------------------------- 1 | %CSPL Paper Replication 65% Accuracy 2 | %Neelabhro Roy 3 | %IIIT-Delhi 4 | 5 | %CUHK01 + LOMO 6 | %Neelabhro Roy 7 | %IIIT-Delhi 8 | 9 | clear; 10 | clc; 11 | close all; 12 | 13 | %pcaFile = 'CUHK01_LOMO_XQDA.mat'; 14 | 15 | feaFile1 = 'custom_probe2.mat'; 16 | feaFile2 = 'custom_gal2.mat'; 17 | pcaFile = 'custom_PCA20.mat'; 18 | 19 | numClass = 40; 20 | %numFolds = 20; 21 | %numRanks = 20; 22 | 23 | %% load the extracted LOMO features 24 | load(feaFile1, 'probe'); 25 | load(feaFile2, 'gallery'); 26 | galFea = gallery(:,1 : numClass); 27 | probFea = probe(:,1 : numClass); 28 | galFea = galFea'; 29 | probFea = probFea'; 30 | %p = randperm(numClass); 31 | 32 | galFea1 = galFea( (1:numClass/2),: ); 33 | probFea1 = probFea( (1:numClass/2), : ); 34 | galFea2 = galFea((numClass/2+1 : end), : ); 35 | probFea2 = probFea((numClass/2+1 : end), : ); 36 | 37 | TrainSet = zeros(40,26960); 38 | TrainSet(1:20 ,:) = galFea1; 39 | TrainSet(21: end,:) = probFea1; 40 | 41 | t0 = tic; 42 | 43 | trainTime = toc(t0); 44 | 45 | TestSet = zeros(40,26960); 46 | TestSet(1:20 ,:) = galFea2; 47 | TestSet(21: end,:) = probFea2; 48 | 49 | Lu = 0.05*1; 50 | L = 0.0000000001; 51 | Lv = 0.2*1; 52 | La = 0.2*1; 53 | Lp = 0.2*1; 54 | Lw = 0.5*1; 55 | 56 | nu = 1*1; 57 | beta = 1*1; 58 | 59 | n = 20; 60 | d = 20; 61 | k = d; 62 | 63 | %[X , W] = matlabPCA(TrainSet',100); 64 | load(pcaFile, 'X'); 65 | load(pcaFile, 'W'); 66 | 67 | X2 = X(:, 1:20); 68 | X1 = X(:, 21:end); 69 | 70 | TestPCA = W' * TestSet'; 71 | X22 = TestPCA(:, 1:20); 72 | X12 = TestPCA(:, 21:end); 73 | 74 | U = randi([0, 1], [d,k]); 75 | V1 = randi([0, 1], [k,n]); 76 | V2 = randi([0, 1], [k,n]); 77 | A = randi([0, 1], [k,k]); 78 | P1 = randi([0, 1], [k,d]); 79 | P2 = randi([0, 1], [k,d]); 80 | W2 = eye(k); 81 | 82 | 83 | 84 | 85 | %% Main algorithm 86 | for i = 1:500 87 | U = (( W2*X1 * transpose(V1)) + ( W2*X2 * transpose(V2)))/((( V1 * transpose(V1)) + ( V2 * transpose(V2)) + (Lu*eye(k)))); 88 | V1 = (((transpose(U) * U) + (nu + beta + Lv) * eye(k))) \ ((transpose(U) *W2* X1) + (beta* A * V2) + nu * P1 * W2*X1); 89 | V2 = (((transpose(U) * U) + ( beta * transpose(A) * A) + (nu + Lv) .* eye(k))) \ ((transpose(U) *W2* X2) + (beta* transpose(A) * V1) + nu * P2*W2 * X2); 90 | P1 = (V1 * transpose(X1)) / ((X1 * transpose(X1)) + (Lp/nu)*eye(k)); 91 | P2 = (V2 * transpose(X2)) / ((X2 * transpose(X2)) + (Lp/nu)*eye(k)); 92 | A = (V1 * transpose(V2)) / ((V2 * transpose(V2)) + (La/beta)*eye(k)); 93 | 94 | end 95 | 96 | 97 | D = zeros(n,n); 98 | for m = 1:n 99 | 100 | v1 = P1*W2*(X12(:,m)); 101 | 102 | for i = 1:n 103 | v2 = P2*W2*(X22(:,i)); 104 | D(m,i) = norm(((v1 - A*v2))); 105 | end 106 | 107 | end 108 | 109 | CMC(D,20); 110 | hold on; 111 | 112 | 113 | 114 | 115 | feaFile1 = 'custom_probe2.mat'; 116 | feaFile2 = 'custom_gal2.mat'; 117 | 118 | numClass = 40; 119 | numFolds = 10; 120 | numRanks = 20; 121 | 122 | %% load the extracted LOMO features 123 | load(feaFile1, 'probe'); 124 | load(feaFile2, 'gallery'); 125 | galFea = gallery(:,1 : numClass); 126 | probFea = probe(:,1 : numClass); 127 | galFea = galFea'; 128 | probFea = probFea'; 129 | clear descriptors 130 | 131 | %% set the seed of the random stream. The results reported in our CVPR 2015 paper are achieved by setting seed = 0. 132 | seed = 0; 133 | rng(seed); 134 | 135 | %% evaluate 136 | cms = zeros(numFolds, numRanks); 137 | 138 | for nf = 1 : numFolds 139 | p = randperm(numClass); 140 | 141 | galFea1 = galFea( p(1:numClass/2), : ); 142 | probFea1 = probFea( p(1:numClass/2), : ); 143 | 144 | t0 = tic; 145 | [W, M] = XQDA(galFea1, probFea1, (1:numClass/2)', (1:numClass/2)'); 146 | 147 | %{ 148 | %% if you need to set different parameters other than the defaults, set them accordingly 149 | options.lambda = 0.001; 150 | options.qdaDims = -1; 151 | options.verbose = true; 152 | [W, M] = XQDA(galFea1, probFea1, (1:numClass/2)', (1:numClass/2)', options); 153 | %} 154 | 155 | 156 | trainTime = toc(t0); 157 | galFea2 = galFea(p(numClass/2+1 : end), : ); 158 | probFea2 = probFea(p(numClass/2+1 : end), : ); 159 | 160 | 161 | 162 | t0 = tic; 163 | dist = MahDist(M, galFea2 * W, probFea2 * W); 164 | clear galFea2 probFea2 M W 165 | matchTime = toc(t0); 166 | 167 | fprintf('Fold %d: ', nf); 168 | fprintf('Training time: %.3g seconds. ', trainTime); 169 | fprintf('Matching time: %.3g seconds.\n', matchTime); 170 | grid on; 171 | cms(nf,:) = EvalCMC( -dist,1 : numClass / 2, 1 : numClass / 2, numRanks ); 172 | clear dist 173 | 174 | fprintf(' Rank1, Rank5, Rank10, Rank15, Rank20\n'); 175 | fprintf('%5.2f%%, %5.2f%%, %5.2f%%, %5.2f%%, %5.2f%%\n\n', cms(nf,[1,5,10]) * 100); 176 | end 177 | 178 | meanCms = mean(cms); 179 | plot(1 : numRanks, meanCms*100,'LineWidth',3); 180 | hold on; 181 | legend('CSPL','XQDA'); --------------------------------------------------------------------------------