diff --git a/lab01_git/questions.md b/lab01_git/questions.md index c9af08a..1e822ae 100644 --- a/lab01_git/questions.md +++ b/lab01_git/questions.md @@ -1,24 +1,27 @@ # Warmup Questions 1. What is the clone url of this repository? - > answer + > https://github.com/diego0020/lab_vision 2. What is the output of the ``cal`` command? - multi - line - answer + -1 Displays a month ++ -3 Displays previous/actual/Next Month ++ -m Shows monday as first week's day ++ -j Days sorted and numerated from January 1st ++ -y Current year's calendar. # Homework Questions 1. What is the ``grep``command? - > answer + > It means "Global regular expression print" it looks for a pattern of text inside a file. 2. What is a *makefile*? - > answer + > A command that helps to compile programs. It recompiles files that are attached to the file that is being worked on. 4. What does the ``-prune`` option of ``find`` do? Give an example - > answer + > -prune in find command ignores directories and files that do not contain the item being searched for. ++ i.e. : find / -name Test.txt -prune 5. Where is the ``grub.cfg`` file > answer @@ -30,25 +33,25 @@ > answer 8. What does the ``cut`` command do? - > answer + > Extracts/cuts txt characters from a .txt file 9. What does the ``wget`` command do? - > answer + > It downloads archives/applications from the http listed after wget 9. What does the ``rsync`` command do? - > answer + > Copies and/or synchronizes files and directories remotely and locally in the system 10. What does the ``diff`` command do? - > answer + > Looks for differences in two files and/or the previous versions of a file. 10. What does the ``tail`` command do? - > answer + > Shows the last 10 lines modified in a file. 10. What does the ``tail -f`` command do? - > answer + > Shows the last 10 lines modified on a file and keeps track on them 10. What does the ``link`` command do? - > answer + > Binds together two files in a new file containing information about said files, should one file be modified, it will apply to the other file too. 11. How many users exist in the course server? > answer @@ -64,7 +67,7 @@ Save this script as ``find_duplicates.sh`` in this directory and commit your changes to github 16. What is the meaning of ``#! /bin/bash`` at the start of scripts? - > answer + > "#! /bin/bash Specifies which program to use to run said script, #(Sharp) !(Bin) 17. How many unique images are in the ``sipi_images`` database? > answer diff --git a/lab3_hybrid/Boot.jpg b/lab3_hybrid/Boot.jpg new file mode 100644 index 0000000..416d808 Binary files /dev/null and b/lab3_hybrid/Boot.jpg differ diff --git a/lab3_hybrid/Bottle.jpg b/lab3_hybrid/Bottle.jpg new file mode 100644 index 0000000..2088f57 Binary files /dev/null and b/lab3_hybrid/Bottle.jpg differ diff --git a/lab3_hybrid/Image_Description.txt b/lab3_hybrid/Image_Description.txt new file mode 100644 index 0000000..63e2d92 --- /dev/null +++ b/lab3_hybrid/Image_Description.txt @@ -0,0 +1,17 @@ +Boot Image + +The Boot Image was taken from myself in the very same classroom in which we take the course +"Computer Vision", the idea of taking a picture of my very own boot was due to the fact that I +tried before implementing the code with my own face and a coin, yet I had no good results. +This boot has been with me for about a year now, and all I can say about it, is that it is a very +good piece of leatherwork and also it is a reliable foot wear. + +Bottle Image + +As soon as I took the picture of my boot, I thought about it's shape, I needed to have an item +with about the same proportions of said piece, and so as I was going home I realised how much +water I needed to drink due to the flue that I currently have, and so as I took up my bottle and +drank a little bit of water I realised that the bottle had about the same size and shape of my +boot, and so as I arrived home, I took my bottle out and had a picture of it. +This bottle is a sturdy one even if it is made of plastic, it has been with me for one and a half +years. \ No newline at end of file diff --git a/lab3_hybrid/Pyramid.png b/lab3_hybrid/Pyramid.png new file mode 100644 index 0000000..f5964df Binary files /dev/null and b/lab3_hybrid/Pyramid.png differ diff --git a/lab3_hybrid/TorresF_LabHyb.m b/lab3_hybrid/TorresF_LabHyb.m new file mode 100644 index 0000000..67b74d7 --- /dev/null +++ b/lab3_hybrid/TorresF_LabHyb.m @@ -0,0 +1,46 @@ +clc, clear all, close all +%% Begining, reading the images +%For the Boot +Boot = imread('https://www.dropbox.com/s/snulaqkfod3shmu/Boot.jpg?dl=1'); +Boot = rgb2gray(imresize(Boot,[256,256])); +%For the Bottle +Bottle = imread('https://www.dropbox.com/s/jp4trcppem35o6m/Bottle.jpg?dl=1'); +Bottle = rgb2gray(imresize(Bottle,[256,256])); + +%% Creation of the filters. +Gss = fspecial('gaussian',[45 45],4); +Gss2 = fspecial('gaussian',[30,30],8); + +%% Filtering of the images +Low = imfilter(Bottle,Gss2); +Hi = Boot-imfilter(Boot,Gss); + +Hyb = Low-Hi; +figure +imshow(Hyb), +title('Hybrid image between the bottle and the boot'); + +pause + +%% Creation of the Piramid + +Vec = [256,200,175,150,125,100,75,50,25]; +Ims = im2uint8(zeros([256,256,3,length(Vec)])); +for x = 1:length(Vec) + Im = imresize(Hyb,[Vec(x) Vec(x)]); + Im = imresize(Im,[256 256]); + Im = repmat(Im,1,1,3); + Ims(:,:,:,x) = Im; +end + +figure +montage(Ims); +title('Image Pyramid'); + +%Storing the Pyramid +im = getimage(gca); +imwrite(im,'Pyramid.png'); + + + + diff --git a/lab4_segmentation/segment_by_clustering.m b/lab4_segmentation/segment_by_clustering.m new file mode 100644 index 0000000..44595aa --- /dev/null +++ b/lab4_segmentation/segment_by_clustering.m @@ -0,0 +1,123 @@ +function Out = segment_by_clustering(rgb_image,feature_space,... + clustering_method,number_of_clusters) +% FUNCTION SEGMENT_BY_ClUSTERING +% OUT = SEGMENT_BY_CLUSTERING(RGB_IMAGE,FEATURE_SPACE,... +% CLUSTERING_METHOD,NUMBER_OF_CLUSTERS) +% RGB_IMAGE. Rgb_image is the input image, it represents what you +% would want to segment. +% FEATURE_SPACE.Feature_Space sets the space of representation for the +% segmentation, it can either be RGB,L*AB,HSV; as well RGB+XY,L*AB+XY +% and HSV+XY. XY means that the input will be considering X and Y +% distances in the image, meaning localization. +% ClUSTERING_METHOD. Clustering method adjusts the way the clusters +% will be selected. It can either be 'K-Means','GMM','Watershed'. If +% watershed is selected, the number of clusters selecetd as an input +% will need to be a value from the range [0,255]. +% NUMBER_OF_CLUSTERS. Number of Clusters adjusts the 'K' for each +% clustering method that requires it, if no argument for +% Number_of_clusters is input, the default will be 3. +% You may want to set the dynamic range of the image in imshow as []. +% Then I highly suggest you to use colormap(colorcube) to see the +% output. +% Felipe Torres F. Universidad de los Andes + +%% Initialization +%Check for input variables +if(nargin<4) + number_of_clusters = 3; +end +if(nargin<3) + clustering_method = 'k-means'; +end +if(nargin<2) + feature_space = 'rgb'; +end + +%Assigning the image to the right data type (optimal for k-means and GMM) +rgb_image = im2double(rgb_image); +[sr,sc,N] = size(rgb_image); +%% Check for input names of variables. +%Feature_Space +if(strcmpi(feature_space,'rgb')==1) + Im = rgb_image; +elseif(strcmpi(feature_space,'lab')==1) + Im = rgb2lab(rgb_image); +elseif(strcmpi(feature_space,'hsv')==1) + Im = rgb2hsv(rgb_image); +elseif(strcmpi(feature_space,'rgb+xy')==1) + Im = zeros([sr,sc,N+2]); + %Assignation of the distance coordinates. + Im(:,:,1:3) = rgb_image; + [F,C] = meshgrid(1:sr,1:sc); + Im(:,:,4) = C'/sc;Im(:,:,5) = F'/sr; +elseif(strcmpi(feature_space,'lab+xy')==1) + Im = zeros([sr,sc,N+2]); + Im(:,:,1:3) = rgb2lab(rgb_image); + %Assignation of the distance coordinates. + [F,C] = meshgrid(1:sr,1:sc); + Im(:,:,4) = C'/sc;Im(:,:,5) = F'/sr; +elseif(strcmpi(feature_space,'hsv+xy')==1) + Im = zeros([sr,sc,N+2]); + Im(:,:,1:3) = rgb2hsv(rgb_image); + %Assignation of the distance coordinates. + [F,C] = meshgrid(1:sr,1:sc); + Im(:,:,4) = C'/sc;Im(:,:,5) = F'/sr; +else + msg = strcat('The feature space must be either: rgb, lab, hsv,',... + {' '},'rgb+xy, lab+xy or hsv+xy'); + error(char(msg)); +end + +%Clustering method +if(strcmpi(clustering_method,'K-MEANS')==1) + %Check for image size. + [~,~,z] = size(Im); + %Creation of the N-Dimensional array. + Vk = zeros(numel(Im(:,:,1)),z); + for x = 1:z + chn = Im(:,:,x); + Vk(:,x) = chn(:); + end + %Application of K-means. + [lbl,cent] = kmeans(Vk,number_of_clusters); + [~,I] = size(cent); + %Assigning centroids to values. + Vec = cent(lbl,round(I/2)); + %Reshaping to get the clustered Image. + Out = reshape(Vec,[sr,sc]); + +elseif(strcmpi(clustering_method,'GMM')==1) + [~,~,z] = size(Im); + %Creation of the N-Dimensional Vector. + Vk = zeros(numel(Im(:,:,1)),z); + for x = 1:z + chn = Im(:,:,x); + Vk(:,x) = chn(:); + end + %Creation of the GMM model. + GM = fitgmdist(Vk,number_of_clusters); + %Finding the values for each cluster. + idx = cluster(GM,Vk); + %Reshaping to get the clustered Image. + Out = reshape(idx,[sr,sc]); + +elseif(strcmpi(clustering_method,'WATERSHED')==1) + h = number_of_clusters; + Im2 = Im(:,:,1:3); + Im2 = im2uint8(rgb2gray(Im2)); + str = strel('disk',1); + dil = imdilate(Im2,str); + ero = imerode(Im2,str); + grad = dil-ero; + marker = imextendedmin(grad, h); + new_grad = imimposemin(grad, marker); + ws = watershed(new_grad); + Out = (ws); +else + %Error message to be displayed if the clustering methods does not exist + %in the parameters. + msg2 = strcat('Clustering method must be either K-Means,GMM or'... + ,{' '},'Watersed'); + error(char(msg2)) +end +end diff --git a/lab6_textons/Assign_THist.m b/lab6_textons/Assign_THist.m new file mode 100644 index 0000000..d379f1e --- /dev/null +++ b/lab6_textons/Assign_THist.m @@ -0,0 +1,40 @@ +function OutStruct = Assign_THist(Struct,k) +%Function ASSIGN TEXTON MAP & HISTOGRAM OUTSTRUCT = ASSIGN_TMAP(STRUCT, +% FBANK,K,assign) +% Function that Assigns to each +% and every single image of the +% images contained in structure +% STRUCT the texton map obtained +% with the Texton Dictionary. +% Struct. The ingresed Structure +% must contain fields: 'name', +% 'image', 'map', 'textons'. +% Otherwise an error will be +% K. K used to implement K-Means +% in the construction of the +% Dictionary. Default K = 50. + +%% Check for input variables +%Check for K. +if(nargin<2) + k = 50; +end + +%% Running the body of the function +addpath('lib'); +%Check for the length of the structure +Lg = length(Struct); +try + for x = 1:Lg + %Recovering the Texton Map + Tmap = Struct(x).map; + %Assigning the Histogram + Struct(x).Hist = histc(Tmap(:),1:k)/numel(Tmap); + end +catch + er = strcat('Input arguments not correct, perhaps the Structure does',... + {' '},'have the required field "map"'); + error(char(er)) +end +OutStruct = Struct; +end \ No newline at end of file diff --git a/lab6_textons/DicoCreation_Test.m b/lab6_textons/DicoCreation_Test.m new file mode 100644 index 0000000..b885184 --- /dev/null +++ b/lab6_textons/DicoCreation_Test.m @@ -0,0 +1,70 @@ +clc, clear all, close all +%% Initiation +%Addition of the path +addpath('lib') +%Route to train directory +dTrain = fullfile(cd,'test'); +%Creation of structure containing the names of the cathegories +nomCat = {'bark1','bark2','bark3','wood1','wood2','wood3',... + 'water','granite','marble','floor1','floor2','pebbles',... + 'wall','brick1','brick2','glass1','glass2','carpet1',... + 'carpet2','upholstery','wallpaper','fur','knit','corduory',... + 'plaid'}; +%Cathegory-wise codes +indCat = {'T01','T02','T03','T04','T05','T06','T07','T08','T09','T10',... +'T11','T12','T13','T14','T15','T16','T17','T18','T19','T20','T21','T22',... +'T23','T24','T25'}; + +%Creation of Structure containing the suffixes for each image in each +%cathegory +for x = 30:40 + if x <10 + nomPerm{x} = strcat('_0',num2str(x),'.jpg'); + else + nomPerm{x} = strcat('_',num2str(x),'.jpg'); + end +end + +%Filter Bank creation +[fb] = fbCreate; +%Number of textons in the dictionary +k = 50; + +%Creation of Texton Dictionary Structure +TXT=struct('name',[],'image',[],'map',[],'textons',[]); +%% Preparation before passing the for to assign textons for ten images per +%cathegory +inds = 1:10:260; +cats = 1:25; +%Aleatorization of the system +vec = 31:40; +VEC = repmat(vec,1,100); +%For assigning the textons and creating the texton dictionary per +%cathegory, each cathegory has 10 sample images +for x = 1:250 + for y = 2:length(inds) + if x>=inds(y-1)&&x=inds(y-1)&&x0); +if(isempty(Pos)==1) + er = strcat('Input name for Kernel type does not exist, Kernel types'... + ,{' '},'should be either:',{' '},char(39),'Chi-square',char(39),',',... + {' '},'or',{' '},char(39),'Intersection',char(39),'.'); + error(char(er)); +end + +%% Check according to methods +[nR,~] = size(Histdata); +%Check for number of classes and the bins taken by each +nbins = nR/numel(Labels); +%Vector containing the separation data +Vec = 0:nbins:nR; +D = 0*(1:nR); +%Check method implementation +if(strcmpi(Method,'Chi-Square')==1) + for x = 1:nR + H1 = ((Histdata(x,:)-Histo).^2)/Histdata(x,:); + D(x) = sum(H1); + end +end +if(strcmpi(Method,'Intersection')==1) + for x = 1:nR + D(x) = sum(min(Histdata(x,:),Histo)); + end +end +%Rescuing label name from possible labels. +lemin = min(D); +index = find(D==lemin); +for x = 2:length(Vec) + if(index>=Vec(x-1)&&index/-model.mat. This model can be used to +% test novel images independently of the Caltech data. +% +% load('data/baseline-model.mat') ; # change to the model path +% label = model.classify(model, im) ; +% + +% Author: Andrea Vedaldi + +% Copyright (C) 2011-2013 Andrea Vedaldi +% All rights reserved. +% +% This file is part of the VLFeat library and is made available under +% the terms of the BSD license (see the COPYING file). + +InstallVLFEAT +conf.calDir = 'data/caltech-101' ; +conf.dataDir = cd ; +conf.autoDownloadData = true ; +conf.numTrain = numIm ; +conf.numTest = numIm ; +conf.numClasses = 102 ; +conf.numWords = 600 ; +conf.numSpatialX = spatial ; +conf.numSpatialY = spatial ; +conf.quantizer = 'kdtree' ; +conf.svm.C = 10 ; + +conf.svm.solver = 'sdca' ; +nel = numel(dir(Folder))-2; +testNom = strcat(Folder,'_',num2str(nel)); +Attemp = fullfile(Folder,testNom); +mkdir(conf.dataDir,Attemp) +%conf.svm.solver = 'sgd' ; +%conf.svm.solver = 'liblinear' ; + +conf.svm.biasMultiplier = 1 ; +conf.phowOpts = {'Step', 3} ; +conf.clobber = false ; +conf.tinyProblem = true ; +conf.prefix = 'baseline' ; +conf.randSeed = 1 ; + +if conf.tinyProblem + conf.prefix = 'tiny' ; + conf.numClasses = Claszs ; + conf.numSpatialX = spatial ; + conf.numSpatialY = spatial ; + conf.numWords = 300 ; + conf.phowOpts = {'Verbose', 2, 'Sizes', 7, 'Step', 5} ; +end + +conf.vocabPath = fullfile(conf.dataDir,Attemp, [conf.prefix '-vocab.mat']) ; +conf.histPath = fullfile(conf.dataDir,Attemp, [conf.prefix '-hists.mat']) ; +conf.modelPath = fullfile(conf.dataDir,Attemp, [conf.prefix '-model.mat']) ; +conf.resultPath = fullfile(conf.dataDir,Attemp, [conf.prefix '-result']) ; + +randn('state',conf.randSeed) ; +rand('state',conf.randSeed) ; +vl_twister('state',conf.randSeed) ; + +% -------------------------------------------------------------------- +% Download Caltech-101 data +% -------------------------------------------------------------------- + +if ~exist(conf.calDir, 'dir') || ... + (~exist(fullfile(conf.calDir, 'airplanes'),'dir') && ... + ~exist(fullfile(conf.calDir, '101_ObjectCategories', 'airplanes'))) + if ~conf.autoDownloadData + error(... + ['Caltech-101 data not found. ' ... + 'Set conf.autoDownloadData=true to download the required data.']) ; + end + vl_xmkdir(conf.calDir) ; + calUrl = ['http://www.vision.caltech.edu/Image_Datasets/' ... + 'Caltech101/101_ObjectCategories.tar.gz'] ; + fprintf('Downloading Caltech-101 data to ''%s''. This will take a while.', conf.calDir) ; + untar(calUrl, conf.calDir) ; +end + +if ~exist(fullfile(conf.calDir, 'airplanes'),'dir') + conf.calDir = fullfile(conf.calDir, '101_ObjectCategories') ; +end + +% -------------------------------------------------------------------- +% Setup data +% -------------------------------------------------------------------- +classes = dir(conf.calDir) ; +classes = classes([classes.isdir]) ; +classes = {classes(3:conf.numClasses+2).name} ; + +images = {} ; +imageClass = {} ; +for ci = 1:length(classes) + ims = dir(fullfile(conf.calDir, classes{ci}, '*.jpg'))' ; + ims = vl_colsubset(ims, conf.numTrain + conf.numTest) ; + ims = cellfun(@(x)fullfile(classes{ci},x),{ims.name},'UniformOutput',false) ; + images = {images{:}, ims{:}} ; + imageClass{end+1} = ci * ones(1,length(ims)) ; +end +selTrain = find(mod(0:length(images)-1, conf.numTrain+conf.numTest) < conf.numTrain) ; +selTest = setdiff(1:length(images), selTrain) ; +imageClass = cat(2, imageClass{:}) ; + +model.classes = classes ; +model.phowOpts = conf.phowOpts ; +model.numSpatialX = conf.numSpatialX ; +model.numSpatialY = conf.numSpatialY ; +model.quantizer = conf.quantizer ; +model.vocab = [] ; +model.w = [] ; +model.b = [] ; +model.classify = @classify ; + +% -------------------------------------------------------------------- +% Train vocabulary +% -------------------------------------------------------------------- + +if ~exist(conf.vocabPath) || conf.clobber + + % Get some PHOW descriptors to train the dictionary + selTrainFeats = vl_colsubset(selTrain, 30) ; + descrs = {} ; + %for ii = 1:length(selTrainFeats) + parfor ii = 1:length(selTrainFeats) + im = imread(fullfile(conf.calDir, images{selTrainFeats(ii)})) ; + im = standarizeImage(im) ; + [drop, descrs{ii}] = vl_phow(im, model.phowOpts{:}) ; + end + + descrs = vl_colsubset(cat(2, descrs{:}), 10e4) ; + descrs = single(descrs) ; + + % Quantize the descriptors to get the visual words + vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan', 'MaxNumIterations', 50) ; + save(conf.vocabPath, 'vocab') ; +else + load(conf.vocabPath) ; +end + +model.vocab = vocab ; + +if strcmp(model.quantizer, 'kdtree') + model.kdtree = vl_kdtreebuild(vocab) ; +end + +% -------------------------------------------------------------------- +% Compute spatial histograms +% -------------------------------------------------------------------- + +if ~exist(conf.histPath) || conf.clobber + hists = {} ; + parfor ii = 1:length(images) + % for ii = 1:length(images) + fprintf('Processing %s (%.2f %%)\n', images{ii}, 100 * ii / length(images)) ; + im = imread(fullfile(conf.calDir, images{ii})) ; + hists{ii} = getImageDescriptor(model, im); + end + + hists = cat(2, hists{:}) ; + save(conf.histPath, 'hists') ; +else + load(conf.histPath) ; +end + +% -------------------------------------------------------------------- +% Compute feature map +% -------------------------------------------------------------------- + +psix = vl_homkermap(hists, 1, 'kchi2', 'gamma', .5) ; + +% -------------------------------------------------------------------- +% Train SVM +% -------------------------------------------------------------------- + +if ~exist(conf.modelPath) || conf.clobber + switch conf.svm.solver + case {'sgd', 'sdca'} + lambda = 1 / (conf.svm.C * length(selTrain)) ; + w = [] ; + parfor ci = 1:length(classes) + perm = randperm(length(selTrain)) ; + fprintf('Training model for class %s\n', classes{ci}) ; + y = 2 * (imageClass(selTrain) == ci) - 1 ; + [w(:,ci) b(ci) info] = vl_svmtrain(psix(:, selTrain(perm)), y(perm), lambda, ... + 'Solver', conf.svm.solver, ... + 'MaxNumIterations', 50/lambda, ... + 'BiasMultiplier', conf.svm.biasMultiplier, ... + 'Epsilon', 1e-3); + end + + case 'liblinear' + svm = train(imageClass(selTrain)', ... + sparse(double(psix(:,selTrain))), ... + sprintf(' -s 3 -B %f -c %f', ... + conf.svm.biasMultiplier, conf.svm.C), ... + 'col') ; + w = svm.w(:,1:end-1)' ; + b = svm.w(:,end)' ; + end + + model.b = conf.svm.biasMultiplier * b ; + model.w = w ; + + save(conf.modelPath, 'model') ; +else + load(conf.modelPath) ; +end + +% -------------------------------------------------------------------- +% Test SVM and evaluate +% -------------------------------------------------------------------- + +% Estimate the class of the test images +scores = model.w' * psix + model.b' * ones(1,size(psix,2)) ; +[drop, imageEstClass] = max(scores, [], 1) ; + +% Compute the confusion matrix +idx = sub2ind([length(classes), length(classes)], ... + imageClass(selTest), imageEstClass(selTest)) ; +confus = zeros(length(classes)) ; +confus = vl_binsum(confus, ones(size(idx)), idx) ; + +% Plots +figure ; clf; +subplot(1,2,1) ; +imagesc(scores(:,[selTrain selTest])) ; title('Scores') ; +set(gca, 'ytick', 1:length(classes), 'yticklabel', classes) ; +subplot(1,2,2) ; +imagesc(confus) ; +title(sprintf('Confusion matrix (%.2f %% accuracy)', ... + 100 * mean(diag(confus)/conf.numTest) )) ; +print('-depsc2', [conf.resultPath '.ps']) ; +save([conf.resultPath '.mat'], 'confus', 'conf') ; +savefig(fullfile(conf.dataDir,Attemp, strcat(testNom,'.fig'))) +print(fullfile(conf.dataDir,Attemp, strcat(testNom)),'-dpng') + +% ------------------------------------------------------------------------- +function im = standarizeImage(im) +% ------------------------------------------------------------------------- + +im = im2single(im) ; +if size(im,1) > 480, im = imresize(im, [480 NaN]) ; end + +% ------------------------------------------------------------------------- +function hist = getImageDescriptor(model, im) +% ------------------------------------------------------------------------- + +im = standarizeImage(im) ; +width = size(im,2) ; +height = size(im,1) ; +numWords = size(model.vocab, 2) ; + +% get PHOW features +[frames, descrs] = vl_phow(im, model.phowOpts{:}) ; + +% quantize local descriptors into visual words +switch model.quantizer + case 'vq' + [drop, binsa] = min(vl_alldist(model.vocab, single(descrs)), [], 1) ; + case 'kdtree' + binsa = double(vl_kdtreequery(model.kdtree, model.vocab, ... + single(descrs), ... + 'MaxComparisons', 50)) ; +end + +for i = 1:length(model.numSpatialX) + binsx = vl_binsearch(linspace(1,width,model.numSpatialX(i)+1), frames(1,:)) ; + binsy = vl_binsearch(linspace(1,height,model.numSpatialY(i)+1), frames(2,:)) ; + + % combined quantization + bins = sub2ind([model.numSpatialY(i), model.numSpatialX(i), numWords], ... + binsy,binsx,binsa) ; + hist = zeros(model.numSpatialY(i) * model.numSpatialX(i) * numWords, 1) ; + hist = vl_binsum(hist, ones(size(bins)), bins) ; + hists{i} = single(hist / sum(hist)) ; +end +hist = cat(1,hists{:}) ; +hist = hist / sum(hist) ; + +% ------------------------------------------------------------------------- +function [className, score] = classify(model, im) +% ------------------------------------------------------------------------- + +hist = getImageDescriptor(model, im) ; +psix = vl_homkermap(hist, 1, 'kchi2', 'gamma', .5) ; +scores = model.w' * psix + model.b' ; +[score, best] = max(scores) ; +className = model.classes{best} ;