function vl_demo_kdtree_sift % VL_DEMO_KDTREE_SIFT % Demonstrates the use of a kd-tree forest to match SIFT % features. If FLANN is present, this function runs a comparison % against it. % AUTORIGHS rand('state',0) ; randn('state',0); do_median = 0 ; do_mean = 1 ; % try to setup flann if ~exist('flann_search', 'file') if exist(fullfile(vl_root, 'opt', 'flann', 'build', 'matlab')) addpath(fullfile(vl_root, 'opt', 'flann', 'build', 'matlab')) ; end end do_flann = exist('nearest_neighbors') == 3 ; if ~do_flann warning('FLANN not found. Comparison disabled.') ; end maxNumComparisonsRange = [1 10 50 100 200 300 400] ; numTreesRange = [1 2 5 10] ; % get data (SIFT features) im1 = imread(fullfile(vl_root, 'data', 'roofs1.jpg')) ; im2 = imread(fullfile(vl_root, 'data', 'roofs2.jpg')) ; im1 = single(rgb2gray(im1)) ; im2 = single(rgb2gray(im2)) ; [f1,d1] = vl_sift(im1,'firstoctave',-1,'floatdescriptors','verbose') ; [f2,d2] = vl_sift(im2,'firstoctave',-1,'floatdescriptors','verbose') ; % add some noise to make matches unique d1 = single(d1) + rand(size(d1)) ; d2 = single(d2) + rand(size(d2)) ; % match exhaustively to get the ground truth elapsedDirect = tic ; D = vl_alldist(d1,d2) ; [drop, best] = min(D, [], 1) ; elapsedDirect = toc(elapsedDirect) ; for ti=1:length(numTreesRange) for vi=1:length(maxNumComparisonsRange) v = maxNumComparisonsRange(vi) ; t = numTreesRange(ti) ; if do_median tic ; kdtree = vl_kdtreebuild(d1, ... 'verbose', ... 'thresholdmethod', 'median', ... 'numtrees', t) ; [i, d] = vl_kdtreequery(kdtree, d1, d2, ... 'verbose', ... 'maxcomparisons',v) ; elapsedKD_median(vi,ti) = toc ; errors_median(vi,ti) = sum(double(i) ~= best) / length(best) ; errorsD_median(vi,ti) = mean(abs(d - drop) ./ drop) ; end if do_mean tic ; kdtree = vl_kdtreebuild(d1, ... 'verbose', ... 'thresholdmethod', 'mean', ... 'numtrees', t) ; %kdtree = readflann(kdtree, '/tmp/flann.txt') ; %checkx(kdtree, d1, 1, 1) ; [i, d] = vl_kdtreequery(kdtree, d1, d2, ... 'verbose', ... 'maxcomparisons', v) ; elapsedKD_mean(vi,ti) = toc ; errors_mean(vi,ti) = sum(double(i) ~= best) / length(best) ; errorsD_mean(vi,ti) = mean(abs(d - drop) ./ drop) ; end if do_flann tic ; [i, d] = flann_search(d1, d2, 1, struct('algorithm','kdtree', ... 'trees', t, ... 'checks', v)); ifla = i ; elapsedKD_flann(vi,ti) = toc; errors_flann(vi,ti) = sum(i ~= best) / length(best) ; errorsD_flann(vi,ti) = mean(abs(d - drop) ./ drop) ; end end end figure(1) ; clf ; leg = {} ; hnd = [] ; sty = {{'color','r'},{'color','g'},... {'color','b'},{'color','c'},... {'color','k'}} ; for ti=1:length(numTreesRange) s = sty{mod(ti,length(sty))+1} ; if do_median h1=loglog(elapsedDirect ./ elapsedKD_median(:,ti),100*errors_median(:,ti),'-*',s{:}) ; hold on ; leg{end+1} = sprintf('VLFeat median (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h1 ; end if do_mean h2=loglog(elapsedDirect ./ elapsedKD_mean(:,ti), 100*errors_mean(:,ti), '-o',s{:}) ; hold on ; leg{end+1} = sprintf('VLFeat (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h2 ; end if do_flann h3=loglog(elapsedDirect ./ elapsedKD_flann(:,ti), 100*errors_flann(:,ti), '+--',s{:}) ; hold on ; leg{end+1} = sprintf('FLANN (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h3 ; end end set([hnd], 'linewidth', 2) ; xlabel('speedup over linear search (log times)') ; ylabel('percentage of incorrect matches (%)') ; h=legend(hnd, leg{:}, 'location', 'southeast') ; set(h,'fontsize',8) ; grid on ; axis square ; vl_demo_print('kdtree_sift_incorrect',.6) ; figure(2) ; clf ; leg = {} ; hnd = [] ; for ti=1:length(numTreesRange) s = sty{mod(ti,length(sty))+1} ; if do_median h1=loglog(elapsedDirect ./ elapsedKD_median(:,ti),100*errorsD_median(:,ti),'*-',s{:}) ; hold on ; leg{end+1} = sprintf('VLFeat median (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h1 ; end if do_mean h2=loglog(elapsedDirect ./ elapsedKD_mean(:,ti), 100*errorsD_mean(:,ti), 'o-',s{:}) ; hold on ; leg{end+1} = sprintf('VLFeat (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h2 ; end if do_flann h3=loglog(elapsedDirect ./ elapsedKD_flann(:,ti), 100*errorsD_flann(:,ti), '+--',s{:}) ; hold on ; leg{end+1} = sprintf('FLANN (%d tr.)', numTreesRange(ti)) ; hnd(end+1) = h3 ; end end set([hnd], 'linewidth', 2) ; xlabel('speedup over linear search (log times)') ; ylabel('relative overestimation of minmium distannce (%)') ; h=legend(hnd, leg{:}, 'location', 'southeast') ; set(h,'fontsize',8) ; grid on ; axis square ; vl_demo_print('kdtree_sift_distortion',.6) ; % -------------------------------------------------------------------- function checkx(kdtree, X, t, n, mib, mab) % -------------------------------------------------------------------- if nargin <= 4 mib = -inf * ones(size(X,1),1) ; mab = +inf * ones(size(X,1),1) ; end lc = kdtree.trees(t).nodes.lowerChild(n) ; uc = kdtree.trees(t).nodes.upperChild(n) ; if lc < 0 for i=-lc:-uc-1 di = kdtree.trees(t).dataIndex(i) ; if any(X(:,di) > mab) error('a') ; end if any(X(:,di) < mib) error('b') ; end end return end i = kdtree.trees(t).nodes.splitDimension(n) ; v = kdtree.trees(t).nodes.splitThreshold(n) ; mab_ = mab ; mab_(i) = min(mab(i), v) ; checkx(kdtree, X, t, lc, mib, mab_) ; mib_ = mib ; mib_(i) = max(mib(i), v) ; checkx(kdtree, X, t, uc, mib_, mab) ; % -------------------------------------------------------------------- function kdtree = readflann(kdtree, path) % -------------------------------------------------------------------- data = textread(path)' ; for i=1:size(data,2) nodeIds = data(1,:) ; ni = find(nodeIds == data(1,i)) ; if ~isnan(data(2,i)) % internal node li = find(nodeIds == data(4,i)) ; ri = find(nodeIds == data(5,i)) ; kdtree.trees(1).nodes.lowerChild(ni) = int32(li) ; kdtree.trees(1).nodes.upperChild(ni) = int32(ri) ; kdtree.trees(1).nodes.splitThreshold(ni) = single(data(2,i)) ; kdtree.trees(1).nodes.splitDimension(ni) = single(data(3,i)+1) ; else di = data(3,i) + 1 ; kdtree.trees(1).nodes.lowerChild(ni) = int32(- di) ; kdtree.trees(1).nodes.upperChild(ni) = int32(- di - 1) ; end kdtree.trees(1).dataIndex = uint32(1:kdtree.numData) ; end