├── .gitignore ├── .gitmodules ├── LICENCE ├── README.md ├── caffe ├── MVCNNDataLayer.py ├── MVCNNDataLayerPreTrain.py ├── README.md ├── alexNet.prototxt ├── ilsvrc_2012_mean.npy ├── mvccn_12view.prototxt ├── mvcnn_PreTrain.prototxt ├── mvcnn_Train.prototxt ├── trainAlex.py ├── trainCNN.py └── trainMVCNN.py ├── cnn_shape.m ├── cnn_shape_get_batch.m ├── cnn_shape_get_features.m ├── cnn_shape_init.m ├── cnn_shape_train.m ├── contributors.txt ├── data └── .gitignore ├── dataset ├── setup_imdb_generic.m ├── setup_imdb_modelnet.m └── setup_imdb_shapenet.m ├── dependencies └── liblinear-1.96 │ ├── .gitignore │ ├── COPYRIGHT │ ├── Makefile │ ├── Makefile.win │ ├── README │ ├── blas │ ├── Makefile │ ├── blas.h │ ├── blasp.h │ ├── daxpy.c │ ├── ddot.c │ ├── dnrm2.c │ └── dscal.c │ ├── heart_scale │ ├── linear.cpp │ ├── linear.def │ ├── linear.h │ ├── matlab │ ├── Makefile │ ├── README │ ├── libsvmread.c │ ├── libsvmwrite.c │ ├── linear_model_matlab.c │ ├── linear_model_matlab.h │ ├── make.m │ ├── predict.c │ └── train.c │ ├── predict.c │ ├── python │ ├── Makefile │ ├── README │ ├── liblinear.py │ └── liblinearutil.py │ ├── train.c │ ├── tron.cpp │ ├── tron.h │ └── windows │ ├── liblinear.dll │ ├── libsvmread.mexw64 │ ├── libsvmwrite.mexw64 │ ├── predict.exe │ ├── predict.mexw64 │ ├── train.exe │ └── train.mexw64 ├── evalkit ├── Evaluator.js ├── Metrics.js ├── README.txt ├── evaluate.js ├── train.csv └── val.csv ├── exp_scripts ├── confmat.m ├── display_retrieval_results.m ├── display_right_wrong.m ├── learn_metric.m ├── prfigure.m └── visualize_saliency.m ├── get_imdb.m ├── rerank_retrieval.m ├── run_experiments.m ├── run_retrieval.m ├── setup.m ├── shape_compute_descriptor.m └── utils ├── RenderMe └── RenderDepth │ ├── RenderMex.cpp │ ├── RenderMex.mexa64 │ ├── chair000009.off │ ├── demo_tmp.m │ ├── off2im.m │ ├── offLoader.m │ └── render_edge.m ├── add_rotated_copies.m ├── convert_net_format.m ├── diagnose_confusionmat.m ├── dirfun.m ├── get_augmentation_matrix.m ├── imread_255.m ├── loadMesh.m ├── modify_net.m ├── next_samples.m ├── normals.m ├── par_alldist.m ├── parfor_progress.m ├── parsave.m ├── plotMesh.m ├── plot_confusionmat.m ├── python ├── get_top_classes.py └── keep_first_instances.py ├── rdir.m ├── render_views.m ├── render_views_of_all_meshes_in_a_folder.m └── vgg_metric ├── evalBestThresh.m ├── getNegPairs.m ├── getPCAWhite.m ├── getPosPairs.m ├── initValidBias.m └── trainProj.m /.gitignore: -------------------------------------------------------------------------------- 1 | log 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dependencies/vlfeat"] 2 | path = dependencies/vlfeat 3 | url = git@github.com:vlfeat/vlfeat.git 4 | [submodule "dependencies/matconvnet"] 5 | path = dependencies/matconvnet 6 | url = git@github.com:suhangpro/matconvnet-18.git 7 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Hang Su 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multi-view CNN (MVCNN) for shape recognition 2 | 3 | [Project Page](http://vis-www.cs.umass.edu/mvcnn/) 4 | ![MVCNN pipeline](http://vis-www.cs.umass.edu/mvcnn/images/mvcnn.png) 5 | 6 | The goal of the project is to learn a general purpose descriptor for shape recognition. To do this we train discriminative models for shape recognition using convolutional neural networks (CNNs) where view-based shape representations are the only cues. Examples include **line-drawings**, **clip art images where color is removed**, or **renderings of 3D models** where there is little or no texture information present. 7 | 8 | If you use any part of the code from this project, please cite: 9 | 10 | @inproceedings{su15mvcnn, 11 | author = {Hang Su and Subhransu Maji and Evangelos Kalogerakis and Erik G. Learned{-}Miller}, 12 | title = {Multi-view convolutional neural networks for 3d shape recognition}, 13 | booktitle = {Proc. ICCV}, 14 | year = {2015}} 15 | 16 | ## Other implementations 17 | 18 | (These are implementations provided by friends or found online, and are listed here for your convenience. I do not provide direct support on them.) 19 | 20 | * PyTorch (from my UMass labmate @jongchyisu): [mvcnn_pytorch](https://github.com/jongchyisu/mvcnn_pytorch) 21 | * Caffe (from my UMass labmate @brotherhuang): Check out the [caffe](https://github.com/suhangpro/mvcnn/tree/master/caffe) folder 22 | * Tensorflow (from @WeiTang114): [MVCNN-Tensorflow](https://github.com/WeiTang114/MVCNN-TensorFlow) 23 | * Torch (from @eriche2016): [mvcnn.torch](https://github.com/eriche2016/mvcnn.torch) 24 | * PyTorch (from @RBirkeland): [MVCNN-ResNet](https://github.com/RBirkeland/MVCNN-ResNet) 25 | 26 | ## Installation 27 | 28 | * Install dependencies 29 | ```bash 30 | git submodule update --init 31 | ``` 32 | 33 | * Compile 34 | 35 | compile for CPU: 36 | ```bash 37 | # two environment variables might need to be set, e.g. MATLABDIR= MEX=/bin/mex 38 | matlab -nodisplay -r "setup(true);exit;" 39 | ``` 40 | compile for GPU (w/ cuDNN): 41 | ```bash 42 | # 1) two environment variables might need to be set, e.g. MATLABDIR= MEX=/bin/mex 43 | # 2) other compilation options (e.g. 'cudaRoot',,'cudaMethod','nvcc','cudnnRoot',) 44 | # might be needed in the 'struct(...)' as well depending on you system settings 45 | matlab -nodisplay -r "setup(true,struct('enableGpu',true,'enableCudnn',true));exit;" 46 | ``` 47 | **Note**: you can alternatively run directly the scripts from the Matlab command window, e.g. for Windows installations: 48 | setup(true,struct('enableGpu',true,'cudaRoot','C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v7.0','cudaMethod','nvcc')); 49 | You may also need to add Visual Studio's cl.exe in your PATH environment (e.g., C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\amd64) 50 | 51 | ## Usage 52 | 53 | * Extract descriptor for a shape (.off/.obj mesh). The descriptor will be saved in a .txt file (e.g. bunny_descriptor.txt). Uses default model with no fine-tuning. Assumes upright orientation by default. 54 | 55 | ```matlab 56 | MATLAB> shape_compute_descriptor('bunny.off'); 57 | ``` 58 | 59 | * Extract descriptor for all shapes in a folder (.off/.obj meshes). The descriptors will be saved in .txt files in the same folder. Assumes no upright orientation. 60 | 61 | ```matlab 62 | MATLAB> shape_compute_descriptor('my_mesh_folder/','useUprightAssumption',false); 63 | ``` 64 | 65 | * Extract descriptors for all shapes in a folder (.off/.obj meshes) and post-process descriptors with learned metric. Uses non-default models. 66 | 67 | ```matlab 68 | MATLAB> shape_compute_descriptor('my_mesh_folder/', 'cnnModel', 'my_cnn.mat', ... 69 | 'metricModel', 'my_metric.mat','applyMetric',true); 70 | ``` 71 | 72 | * Download datasets for training/evaluation (should be placed under data/) 73 | * modelnet40v1 (12 views w/ upright assumption): [tarball](http://maxwell.cs.umass.edu/mvcnn-data/modelnet40v1.tar) (204M) 74 | * modelnet40v2 (80 views w/o upright assumption): [tarball](http://maxwell.cs.umass.edu/mvcnn-data/modelnet40v2.tar) (1.3G) 75 | * shapenet55v1 (12 views w/ upright assumption): [tarball](http://maxwell.cs.umass.edu/mvcnn-data/shapenet55v1.tar) (2.4G) 76 | * shapenet55v2 (80 views w/o upright assumption): [tarball](http://maxwell.cs.umass.edu/mvcnn-data/shapenet55v2.tar) (15G) 77 | 78 | * Run training examples (see run_experiments.m for details) 79 | ```bash 80 | # LD_LIBRARY_PATH might need to be set, e.g. LD_LIBRARY_PATH=/lib64: 81 | matlab -nodisplay -r "run_experiments;exit;" 82 | ``` 83 | -------------------------------------------------------------------------------- /caffe/MVCNNDataLayer.py: -------------------------------------------------------------------------------- 1 | # Caffe data layer of generating moving mnist images 2 | 3 | import caffe 4 | import numpy as np 5 | import math 6 | import scipy 7 | import sys 8 | import os 9 | import cv2 10 | from PIL import Image 11 | from random import shuffle 12 | 13 | class MVCNNDataLayer(caffe.Layer): 14 | """Caffe moving mnist data layer used for training.""" 15 | def setup(self, bottom, top): 16 | """Setup the MovingMnistDataLayer.""" 17 | 18 | # parse the layer parameter string, which must be valid YAML 19 | layer_params = eval(self.param_str) 20 | self._dataPath = layer_params['data_path'] 21 | self._batch_size = layer_params['batch_size'] 22 | #self._mean_file = layer_params['mean_file'] 23 | self._view_Size = layer_params['view_size'] 24 | self._channel_Size = layer_params['channel_size'] 25 | self._phase = layer_params['phase'] # train or test 26 | self._name_to_top_map = {'data': 0, 'label': 1} 27 | self._classList = sorted(next(os.walk(self._dataPath))[1]) 28 | self._modelList = [] 29 | self._model2lable = {} 30 | self._train_iteration = 0 31 | for i in range(len(self._classList)): 32 | imageFiles = os.listdir(self._dataPath + '/' + self._classList[i] + '/' + self._phase) 33 | for image in imageFiles: 34 | modelName = image[0:-8] 35 | self._model2lable[modelName] = i 36 | if self._modelList.count(modelName) == 0: 37 | self._modelList.append(modelName) 38 | top[0].reshape(self._batch_size * self._view_Size ,self._channel_Size, 227, 227) 39 | top[1].reshape(self._batch_size, 1, 1, 1) 40 | 41 | def forward(self, bottom, top): 42 | """Get blobs and copy them into this layer's top blob vector.""" 43 | blobs = self._get_next_minibatch() 44 | 45 | for blob_name, blob in blobs.iteritems(): 46 | top_ind = self._name_to_top_map[blob_name] 47 | # Reshape net's input blobs 48 | top[top_ind].reshape(*(blob.shape)) 49 | # Copy data into net's input blobs 50 | top[top_ind].data[...] = blob.astype(np.float32, copy=False) 51 | 52 | def backward(self, top, propagate_down, bottom): 53 | """This layer does not propagate gradients.""" 54 | pass 55 | 56 | def _loadSampleImage(self, modelName): 57 | mean_file = np.load('ilsvrc_2012_mean.npy') 58 | pixelMeans = mean_file.mean(1).mean(1) 59 | imh = 227 60 | imw = 227 61 | imc = 3 62 | ims = np.zeros((imh, imw, imc, self._view_Size), dtype=np.float32) 63 | label = self._model2lable[modelName] 64 | for view in range(1, self._view_Size + 1): 65 | viewID = str(view) 66 | while len(viewID) < 3 : viewID = '0' + viewID 67 | img = self._dataPath + '/' + self._classList[label] + '/' + self._phase + '/' + modelName + '_' + viewID + '.jpg' 68 | im = cv2.imread(img) 69 | assert(im is not None) 70 | im = cv2.resize(im, (imh, imw)) 71 | im = im.astype(np.float32, copy=False) - pixelMeans 72 | ims[:,:,:,view-1] = im 73 | ims = ims.transpose(3,2,0,1) 74 | return ims 75 | 76 | def reshape(self, bottom, top): 77 | """Reshaping happens during the call to forward.""" 78 | pass 79 | 80 | def _get_next_minibatch(self): 81 | height = 227 82 | width = 227 83 | batch_size = self._batch_size 84 | #if self._train_iteration == 0 : shuffle(self._modelList) 85 | data = np.ones((self._batch_size * self._view_Size, self._channel_Size, height, width), dtype=np.float32) 86 | label = np.ones((batch_size, 1, 1, 1), dtype=np.float32) 87 | for i in range(batch_size): 88 | currentModel = self._modelList[(self._train_iteration * self._batch_size + i) % len(self._modelList)] 89 | #print currentModel 90 | currentImage = self._loadSampleImage(currentModel) 91 | for j in range(self._view_Size) : 92 | data[j * self._batch_size + i,:,:,:] = currentImage[j,:,:,:] 93 | label[i,:,:,:] = self._model2lable[currentModel] 94 | self._train_iteration += 1 95 | blobs = {'data': data, 'label': label} 96 | return blobs 97 | -------------------------------------------------------------------------------- /caffe/MVCNNDataLayerPreTrain.py: -------------------------------------------------------------------------------- 1 | # Caffe data layer of generating moving mnist images 2 | 3 | import caffe 4 | import numpy as np 5 | import math 6 | import scipy 7 | import sys 8 | import os 9 | import cv2 10 | from PIL import Image 11 | from random import shuffle 12 | 13 | class MVCNNDataLayerPreTrain(caffe.Layer): 14 | """Caffe moving mnist data layer used for training.""" 15 | def setup(self, bottom, top): 16 | """Setup the MovingMnistDataLayer.""" 17 | 18 | # parse the layer parameter string, which must be valid YAML 19 | layer_params = eval(self.param_str) 20 | self._dataPath = layer_params['data_path'] 21 | self._batch_size = layer_params['batch_size'] 22 | #self._mean_file = layer_params['mean_file'] 23 | self._view_Size = layer_params['view_size'] 24 | self._channel_Size = layer_params['channel_size'] 25 | self._phase = layer_params['phase'] # train or test 26 | self._name_to_top_map = {'data': 0, 'label': 1} 27 | self._classList = sorted(next(os.walk(self._dataPath))[1]) 28 | self._modelList = [] 29 | self._model2lable = {} 30 | self._train_iteration = 0 31 | for i in range(len(self._classList)): 32 | imageFiles = os.listdir(self._dataPath + '/' + self._classList[i] + '/' + self._phase) 33 | for image in imageFiles: 34 | self._modelList.append(image) 35 | self._model2lable[image] = i 36 | top[0].reshape(self._batch_size,self._channel_Size, 227, 227) 37 | top[1].reshape(self._batch_size, 1, 1, 1) 38 | 39 | def forward(self, bottom, top): 40 | """Get blobs and copy them into this layer's top blob vector.""" 41 | blobs = self._get_next_minibatch() 42 | 43 | for blob_name, blob in blobs.iteritems(): 44 | top_ind = self._name_to_top_map[blob_name] 45 | # Reshape net's input blobs 46 | top[top_ind].reshape(*(blob.shape)) 47 | # Copy data into net's input blobs 48 | top[top_ind].data[...] = blob.astype(np.float32, copy=False) 49 | 50 | def backward(self, top, propagate_down, bottom): 51 | """This layer does not propagate gradients.""" 52 | pass 53 | 54 | def _loadSampleImage(self, modelName): 55 | mean_file = np.load('ilsvrc_2012_mean.npy') 56 | pixelMeans = mean_file.mean(1).mean(1) 57 | imh = 227 58 | imw = 227 59 | imc = 3 60 | ims = np.zeros((imh, imw, imc, 1), dtype=np.float32) 61 | label = self._model2lable[modelName] 62 | img = self._dataPath + '/' + self._classList[label] + '/' + self._phase + '/' + modelName 63 | im = cv2.imread(img) 64 | assert(im is not None) 65 | im = cv2.resize(im, (imh, imw)) 66 | im = im.astype(np.float32, copy=False) - pixelMeans 67 | ims[:,:,:,0] = im 68 | ims = ims.transpose(3,2,0,1) 69 | return ims 70 | 71 | def reshape(self, bottom, top): 72 | """Reshaping happens during the call to forward.""" 73 | pass 74 | 75 | def _get_next_minibatch(self): 76 | height = 227 77 | width = 227 78 | if self._train_iteration == 0 : shuffle(self._modelList) 79 | data = np.ones((self._batch_size, self._channel_Size, height, width), dtype=np.float32) 80 | label = np.ones((self._batch_size, 1, 1, 1), dtype=np.float32) 81 | for i in range(self._batch_size): 82 | currentModel = self._modelList[(self._train_iteration * self._batch_size + i) % len(self._modelList)] 83 | currentImage = self._loadSampleImage(currentModel) 84 | data[i,:,:,:] = currentImage 85 | label[i,:,:,:] = self._model2lable[currentModel] 86 | self._train_iteration += 1 87 | blobs = {'data': data, 'label': label} 88 | return blobs 89 | -------------------------------------------------------------------------------- /caffe/README.md: -------------------------------------------------------------------------------- 1 | This demo code shows an caffe implementation of MVCNN based on a 12-view setting. 2 | 3 | 1. The network is defined in mvccn_12view.prototxt, where we use the caffe 'Slice' layer to sperate views and "Eltwise" layer for max view-pooling. Please check the prototxt file for more details. 4 | 5 | 2. MVCNNDataLayer.py provides an example to prepare the input data layer for the network. It requires caffe compiled with python layer. one needs to change the load function based on the data folder organization. 6 | 7 | 3. Also provide a prototxt and data layer for finetuning with AlexNet, to improve the performance -------------------------------------------------------------------------------- /caffe/alexNet.prototxt: -------------------------------------------------------------------------------- 1 | name: "siameseMVCNN" 2 | layer { 3 | name: "inputdata" 4 | type: "Python" 5 | top: "data" 6 | top: "label" 7 | python_param { 8 | module: 'MVCNNDataLayerPreTrain' 9 | layer: 'MVCNNDataLayerPreTrain' 10 | param_str: "{'phase': 'train', 'batch_size': 64, 'view_size': 1,'channel_size': 3, 'data_path': './modelnet40v1'}" 11 | } 12 | include { 13 | phase: TRAIN 14 | } 15 | } 16 | layer { 17 | name: "inputdata" 18 | type: "Python" 19 | top: "data" 20 | top: "label" 21 | python_param { 22 | module: 'MVCNNDataLayerPreTrain' 23 | layer: 'MVCNNDataLayerPreTrain' 24 | param_str: "{'phase': 'test', 'batch_size': 64, 'view_size': 1,'channel_size': 3, 'data_path': './modelnet40v1'}" 25 | } 26 | include { 27 | phase: TEST 28 | } 29 | } 30 | layer { 31 | name: "conv1" 32 | type: "Convolution" 33 | bottom: "data" 34 | top: "conv1" 35 | param { 36 | lr_mult: 1 37 | decay_mult: 1 38 | } 39 | param { 40 | lr_mult: 2 41 | decay_mult: 0 42 | } 43 | convolution_param { 44 | num_output: 96 45 | kernel_size: 11 46 | stride: 4 47 | weight_filler { 48 | type: "gaussian" 49 | std: 0.01 50 | } 51 | bias_filler { 52 | type: "constant" 53 | value: 0 54 | } 55 | } 56 | } 57 | layer { 58 | name: "relu1" 59 | type: "ReLU" 60 | bottom: "conv1" 61 | top: "conv1" 62 | } 63 | layer { 64 | name: "norm1" 65 | type: "LRN" 66 | bottom: "conv1" 67 | top: "norm1" 68 | lrn_param { 69 | local_size: 5 70 | alpha: 0.0001 71 | beta: 0.75 72 | } 73 | } 74 | layer { 75 | name: "pool1" 76 | type: "Pooling" 77 | bottom: "norm1" 78 | top: "pool1" 79 | pooling_param { 80 | pool: MAX 81 | kernel_size: 3 82 | stride: 2 83 | } 84 | } 85 | layer { 86 | name: "conv2" 87 | type: "Convolution" 88 | bottom: "pool1" 89 | top: "conv2" 90 | param { 91 | lr_mult: 1 92 | decay_mult: 1 93 | } 94 | param { 95 | lr_mult: 2 96 | decay_mult: 0 97 | } 98 | convolution_param { 99 | num_output: 256 100 | pad: 2 101 | kernel_size: 5 102 | group: 2 103 | weight_filler { 104 | type: "gaussian" 105 | std: 0.01 106 | } 107 | bias_filler { 108 | type: "constant" 109 | value: 0.1 110 | } 111 | } 112 | } 113 | layer { 114 | name: "relu2" 115 | type: "ReLU" 116 | bottom: "conv2" 117 | top: "conv2" 118 | } 119 | layer { 120 | name: "norm2" 121 | type: "LRN" 122 | bottom: "conv2" 123 | top: "norm2" 124 | lrn_param { 125 | local_size: 5 126 | alpha: 0.0001 127 | beta: 0.75 128 | } 129 | } 130 | layer { 131 | name: "pool2" 132 | type: "Pooling" 133 | bottom: "norm2" 134 | top: "pool2" 135 | pooling_param { 136 | pool: MAX 137 | kernel_size: 3 138 | stride: 2 139 | } 140 | } 141 | layer { 142 | name: "conv3" 143 | type: "Convolution" 144 | bottom: "pool2" 145 | top: "conv3" 146 | param { 147 | lr_mult: 1 148 | decay_mult: 1 149 | } 150 | param { 151 | lr_mult: 2 152 | decay_mult: 0 153 | } 154 | convolution_param { 155 | num_output: 384 156 | pad: 1 157 | kernel_size: 3 158 | weight_filler { 159 | type: "gaussian" 160 | std: 0.01 161 | } 162 | bias_filler { 163 | type: "constant" 164 | value: 0 165 | } 166 | } 167 | } 168 | layer { 169 | name: "relu3" 170 | type: "ReLU" 171 | bottom: "conv3" 172 | top: "conv3" 173 | } 174 | layer { 175 | name: "conv4" 176 | type: "Convolution" 177 | bottom: "conv3" 178 | top: "conv4" 179 | param { 180 | lr_mult: 1 181 | decay_mult: 1 182 | } 183 | param { 184 | lr_mult: 2 185 | decay_mult: 0 186 | } 187 | convolution_param { 188 | num_output: 384 189 | pad: 1 190 | kernel_size: 3 191 | group: 2 192 | weight_filler { 193 | type: "gaussian" 194 | std: 0.01 195 | } 196 | bias_filler { 197 | type: "constant" 198 | value: 0.1 199 | } 200 | } 201 | } 202 | layer { 203 | name: "relu4" 204 | type: "ReLU" 205 | bottom: "conv4" 206 | top: "conv4" 207 | } 208 | layer { 209 | name: "conv5" 210 | type: "Convolution" 211 | bottom: "conv4" 212 | top: "conv5" 213 | param { 214 | lr_mult: 1 215 | decay_mult: 1 216 | } 217 | param { 218 | lr_mult: 2 219 | decay_mult: 0 220 | } 221 | convolution_param { 222 | num_output: 256 223 | pad: 1 224 | kernel_size: 3 225 | group: 2 226 | weight_filler { 227 | type: "gaussian" 228 | std: 0.01 229 | } 230 | bias_filler { 231 | type: "constant" 232 | value: 0.1 233 | } 234 | } 235 | } 236 | layer { 237 | name: "relu5" 238 | type: "ReLU" 239 | bottom: "conv5" 240 | top: "conv5" 241 | } 242 | layer { 243 | name: "pool5" 244 | type: "Pooling" 245 | bottom: "conv5" 246 | top: "pool5" 247 | pooling_param { 248 | pool: MAX 249 | kernel_size: 3 250 | stride: 2 251 | } 252 | } 253 | layer { 254 | name: "fc6" 255 | type: "InnerProduct" 256 | bottom: "pool5" 257 | top: "fc6" 258 | param { 259 | lr_mult: 1 260 | decay_mult: 1 261 | } 262 | param { 263 | lr_mult: 2 264 | decay_mult: 0 265 | } 266 | inner_product_param { 267 | num_output: 4096 268 | weight_filler { 269 | type: "gaussian" 270 | std: 0.005 271 | } 272 | bias_filler { 273 | type: "constant" 274 | value: 0.1 275 | } 276 | } 277 | } 278 | layer { 279 | name: "relu6" 280 | type: "ReLU" 281 | bottom: "fc6" 282 | top: "fc6" 283 | } 284 | layer { 285 | name: "drop6" 286 | type: "Dropout" 287 | bottom: "fc6" 288 | top: "fc6" 289 | dropout_param { 290 | dropout_ratio: 0.5 291 | } 292 | } 293 | layer { 294 | name: "fc7" 295 | type: "InnerProduct" 296 | bottom: "fc6" 297 | top: "fc7" 298 | param { 299 | lr_mult: 1 300 | decay_mult: 1 301 | } 302 | param { 303 | lr_mult: 2 304 | decay_mult: 0 305 | } 306 | inner_product_param { 307 | num_output: 4096 308 | weight_filler { 309 | type: "gaussian" 310 | std: 0.005 311 | } 312 | bias_filler { 313 | type: "constant" 314 | value: 0.1 315 | } 316 | } 317 | } 318 | layer { 319 | name: "relu7" 320 | type: "ReLU" 321 | bottom: "fc7" 322 | top: "fc7" 323 | } 324 | layer { 325 | name: "drop7" 326 | type: "Dropout" 327 | bottom: "fc7" 328 | top: "fc7" 329 | dropout_param { 330 | dropout_ratio: 0.5 331 | } 332 | } 333 | layer { 334 | name: "modelNet40" 335 | type: "InnerProduct" 336 | bottom: "fc7" 337 | top: "modelNet40" 338 | param { 339 | lr_mult: 1 340 | decay_mult: 1 341 | } 342 | param { 343 | lr_mult: 2 344 | decay_mult: 0 345 | } 346 | inner_product_param { 347 | num_output: 40 348 | weight_filler { 349 | type: "gaussian" 350 | std: 0.005 351 | } 352 | bias_filler { 353 | type: "constant" 354 | value: 0 355 | } 356 | } 357 | } 358 | #layer { 359 | # name: "accuracy" 360 | # type: "Accuracy" 361 | # bottom: "modelNet40" 362 | # bottom: "label" 363 | # top: "accuracy" 364 | # include { 365 | # phase: TEST 366 | # } 367 | #} 368 | layer { 369 | name: "loss" 370 | type: "SoftmaxWithLoss" 371 | bottom: "modelNet40" 372 | bottom: "label" 373 | top: "loss" 374 | } 375 | -------------------------------------------------------------------------------- /caffe/ilsvrc_2012_mean.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/caffe/ilsvrc_2012_mean.npy -------------------------------------------------------------------------------- /caffe/mvcnn_PreTrain.prototxt: -------------------------------------------------------------------------------- 1 | net: "alexNet.prototxt" 2 | test_iter: 100 3 | test_interval: 1000 4 | base_lr: 0.001 5 | lr_policy: "step" 6 | gamma: 0.1 7 | stepsize: 100000 8 | display: 20 9 | max_iter: 450000 10 | momentum: 0.9 11 | weight_decay: 0.0005 12 | snapshot: 10000 13 | solver_mode: GPU 14 | snapshot_prefix: "mvcnn_pre" 15 | 16 | 17 | -------------------------------------------------------------------------------- /caffe/mvcnn_Train.prototxt: -------------------------------------------------------------------------------- 1 | net: "mvccn_12view.prototxt" 2 | test_iter: 100 3 | test_interval: 1000 4 | base_lr: 0.01 5 | lr_policy: "step" 6 | gamma: 0.1 7 | stepsize: 100000 8 | display: 20 9 | max_iter: 450000 10 | momentum: 0.9 11 | weight_decay: 0.0005 12 | snapshot: 10000 13 | solver_mode: GPU 14 | snapshot_prefix: "mvcnn" 15 | iter_size : 32 16 | 17 | -------------------------------------------------------------------------------- /caffe/trainAlex.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | caffe_root = './caffe/' 4 | sys.path.insert(0, caffe_root + 'python') 5 | import caffe 6 | import scipy.io as sio 7 | import h5py 8 | import os 9 | caffe.set_mode_gpu() 10 | caffe.set_device(0) 11 | solver = caffe.SGDSolver('mvcnn_PreTrain.prototxt') 12 | solver.net.copy_from('bvlc_reference_caffenet.caffemodel') 13 | solver.solve() 14 | -------------------------------------------------------------------------------- /caffe/trainCNN.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | caffe_root = '/home/jianbing/caffe/' 4 | sys.path.insert(0, caffe_root + 'python') 5 | import caffe 6 | import scipy.io as sio 7 | import h5py 8 | import os 9 | caffe.set_mode_gpu() 10 | caffe.set_device(0) 11 | solver = caffe.SGDSolver('mvcnn_Train.prototxt') 12 | solver.net.copy_from('bvlc_alexnet.caffemodel') 13 | solver.solve() 14 | -------------------------------------------------------------------------------- /caffe/trainMVCNN.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import sys 3 | caffe_root = './caffe/' 4 | sys.path.insert(0, caffe_root + 'python') 5 | import caffe 6 | import scipy.io as sio 7 | import h5py 8 | import os 9 | caffe.set_mode_gpu() 10 | caffe.set_device(0) 11 | solver = caffe.SGDSolver('mvcnn_Train.prototxt') 12 | solver.net.copy_from('bvlc_reference_caffenet.caffemodel') 13 | solver.solve() 14 | -------------------------------------------------------------------------------- /cnn_shape.m: -------------------------------------------------------------------------------- 1 | function net = cnn_shape(dataName, varargin) 2 | %CNN_SHAPE Train an MVCNN on a provided dataset 3 | % 4 | % dataName:: 5 | % must be name of a folder under data/ 6 | % `baseModel`:: 'imagenet-matconvnet-vgg-m' 7 | % learning starting point 8 | % `fromScratch`:: false 9 | % if false, only the last layer is initialized randomly 10 | % if true, all the weight layers are initialized randomly 11 | % `numFetchThreads`:: 12 | % #threads for vl_imreadjpeg 13 | % `aug`:: 'none' 14 | % specifies the operations (fliping, perturbation, etc.) used 15 | % to get sub-regions 16 | % `viewpoolPos` :: 'relu5' 17 | % location of the viewpool layer, only used when multiview is true 18 | % `includeVal`:: false 19 | % if true, validation set is also used for training 20 | % `useUprightAssumption`:: true 21 | % if true, 12 views will be used to render meshes, 22 | % otherwise 80 views based on a dodecahedron 23 | % 24 | % `train` 25 | % training parameters: 26 | % `learningRate`:: [0.001*ones(1, 10) 0.0001*ones(1, 10) 0.00001*ones(1,10)] 27 | % learning rate 28 | % `batchSize`: 128 29 | % set to a smaller number on limited memory 30 | % `momentum`:: 0.9 31 | % learning momentum 32 | % `gpus` :: [] 33 | % a list of available gpus 34 | % 35 | % Hang Su 36 | 37 | opts.networkType = 'simplenn'; % only simplenn is supported currently 38 | opts.baseModel = 'imagenet-matconvnet-vgg-m'; 39 | opts.fromScratch = false; 40 | opts.dataRoot = 'data' ; 41 | opts.imageExt = '.jpg'; 42 | opts.numFetchThreads = 0 ; 43 | opts.multiview = true; 44 | opts.viewpoolPos = 'relu5'; 45 | opts.useUprightAssumption = true; 46 | opts.aug = 'stretch'; 47 | opts.pad = 0; 48 | opts.border = 0; 49 | opts.numEpochs = [5 10 20]; 50 | opts.includeVal = false; 51 | [opts, varargin] = vl_argparse(opts, varargin) ; 52 | 53 | if strcmpi(opts.baseModel(end-3:end),'.mat'), 54 | [~,modelNameStr] = fileparts(opts.baseModel); 55 | opts.baseModel = load(opts.baseModel); 56 | else 57 | modelNameStr = opts.baseModel; 58 | end 59 | 60 | if opts.multiview, 61 | opts.expDir = sprintf('%s-ft-%s-%s-%s', ... 62 | modelNameStr, ... 63 | dataName, ... 64 | opts.viewpoolPos, ... 65 | opts.networkType); 66 | else 67 | opts.expDir = sprintf('%s-ft-%s-%s', ... 68 | modelNameStr, ... 69 | dataName, ... 70 | opts.networkType); 71 | end 72 | opts.expDir = fullfile(opts.dataRoot, opts.expDir); 73 | [opts, varargin] = vl_argparse(opts,varargin) ; 74 | 75 | opts.train.learningRate = [0.005*ones(1, 5) 0.001*ones(1, 5) 0.0001*ones(1,10) 0.00001*ones(1,10)]; 76 | opts.train.momentum = 0.9; 77 | opts.train.batchSize = 256; 78 | opts.train.maxIterPerEpoch = [Inf, Inf]; 79 | opts.train.balancingFunction = {[], []}; 80 | opts.train.gpus = []; 81 | opts.train = vl_argparse(opts.train, varargin) ; 82 | 83 | if ~exist(opts.expDir, 'dir'), vl_xmkdir(opts.expDir) ; end 84 | 85 | assert(strcmp(opts.networkType,'simplenn'), 'Only simplenn is supported currently'); 86 | 87 | % ------------------------------------------------------------------------- 88 | % Prepare data 89 | % ------------------------------------------------------------------------- 90 | imdb = get_imdb(dataName); 91 | if ~opts.multiview, 92 | nViews = 1; 93 | else 94 | nShapes = length(unique(imdb.images.sid)); 95 | nViews = length(imdb.images.id)/nShapes; 96 | end 97 | imdb.meta.nViews = nViews; 98 | 99 | opts.train.train = find(imdb.images.set==1); 100 | opts.train.val = find(imdb.images.set==2); 101 | if opts.includeVal, 102 | opts.train.train = [opts.train.train opts.train.val]; 103 | opts.train.val = []; 104 | end 105 | opts.train.train = opts.train.train(1:nViews:end); 106 | opts.train.val = opts.train.val(1:nViews:end); 107 | 108 | % ------------------------------------------------------------------------- 109 | % Prepare model 110 | % ------------------------------------------------------------------------- 111 | net = cnn_shape_init(imdb.meta.classes, ... 112 | 'base', opts.baseModel, ... 113 | 'restart', opts.fromScratch, ... 114 | 'nViews', nViews, ... 115 | 'viewpoolPos', opts.viewpoolPos, ... 116 | 'networkType', opts.networkType); 117 | 118 | % ------------------------------------------------------------------------- 119 | % Learn 120 | % ------------------------------------------------------------------------- 121 | switch opts.networkType 122 | case 'simplenn', trainFn = @cnn_shape_train ; 123 | case 'dagnn', trainFn = @cnn_train_dag ; 124 | end 125 | 126 | trainable_layers = find(cellfun(@(l) isfield(l,'weights'),net.layers)); 127 | fc_layers = find(cellfun(@(s) numel(s.name)>=2 && strcmp(s.name(1:2),'fc'),net.layers)); 128 | fc_layers = intersect(fc_layers, trainable_layers); 129 | lr = cellfun(@(l) l.learningRate, net.layers(trainable_layers),'UniformOutput',false); 130 | layers_for_update = {trainable_layers(end), fc_layers, trainable_layers}; 131 | 132 | for s=1:numel(opts.numEpochs), 133 | if opts.numEpochs(s)<1, continue; end 134 | for i=1:numel(trainable_layers), 135 | l = trainable_layers(i); 136 | if ismember(l,layers_for_update{s}), 137 | net.layers{l}.learningRate = lr{i}; 138 | else 139 | net.layers{l}.learningRate = lr{i}*0; 140 | end 141 | end 142 | net = trainFn(net, imdb, getBatchFn(opts, net.meta), ... 143 | 'expDir', opts.expDir, ... 144 | net.meta.trainOpts, ... 145 | opts.train, ... 146 | 'numEpochs', sum(opts.numEpochs(1:s))) ; 147 | end 148 | 149 | % ------------------------------------------------------------------------- 150 | % Deploy 151 | % ------------------------------------------------------------------------- 152 | net = cnn_imagenet_deploy(net) ; 153 | modelPath = fullfile(opts.expDir, 'net-deployed.mat'); 154 | 155 | switch opts.networkType 156 | case 'simplenn' 157 | save(modelPath, '-struct', 'net') ; 158 | case 'dagnn' 159 | net_ = net.saveobj() ; 160 | save(modelPath, '-struct', 'net_') ; 161 | clear net_ ; 162 | end 163 | 164 | % ------------------------------------------------------------------------- 165 | function fn = getBatchFn(opts, meta) 166 | % ------------------------------------------------------------------------- 167 | bopts.numThreads = opts.numFetchThreads ; 168 | bopts.pad = opts.pad; 169 | bopts.border = opts.border ; 170 | bopts.transformation = opts.aug ; 171 | bopts.imageSize = meta.normalization.imageSize ; 172 | bopts.averageImage = meta.normalization.averageImage ; 173 | bopts.rgbVariance = meta.augmentation.rgbVariance ; 174 | % bopts.transformation = meta.augmentation.transformation ; 175 | 176 | switch lower(opts.networkType) 177 | case 'simplenn' 178 | fn = @(x,y) getSimpleNNBatch(bopts,x,y) ; 179 | case 'dagnn' 180 | error('dagnn version not yet implemented'); 181 | end 182 | 183 | % ------------------------------------------------------------------------- 184 | function [im,labels] = getSimpleNNBatch(opts, imdb, batch) 185 | % ------------------------------------------------------------------------- 186 | if nargout > 1, labels = imdb.images.class(batch); end 187 | isVal = ~isempty(batch) && imdb.images.set(batch(1)) ~= 1 ; 188 | nViews = imdb.meta.nViews; 189 | 190 | batch = bsxfun(@plus,repmat(batch(:)',[nViews 1]),(0:nViews-1)'); 191 | batch = batch(:)'; 192 | 193 | images = strcat([imdb.imageDir filesep], imdb.images.name(batch)) ; 194 | 195 | if ~isVal, % training 196 | im = cnn_shape_get_batch(images, opts, ... 197 | 'prefetch', nargout == 0, ... 198 | 'nViews', nViews); 199 | else 200 | im = cnn_shape_get_batch(images, opts, ... 201 | 'prefetch', nargout == 0, ... 202 | 'nViews', nViews, ... 203 | 'transformation', 'none'); 204 | end 205 | 206 | nAugs = size(im,4)/numel(images); 207 | if nargout > 1, labels = repmat(labels(:)',[1 nAugs]); end 208 | 209 | -------------------------------------------------------------------------------- /cnn_shape_get_batch.m: -------------------------------------------------------------------------------- 1 | function imo = cnn_shape_get_batch(images, varargin) 2 | % Modified from CNN_IMAGENET_GET_BATCH 3 | % 4 | % - added `pad` option 5 | % - deals with images of types other than jpeg 6 | % - augmentation made consistent across views 7 | 8 | opts.imageSize = [227, 227] ; 9 | opts.border = [29, 29] ; 10 | opts.pad = 0; % [TOP BOTTOM LEFT RIGHT] 11 | opts.nViews = 1; 12 | opts.keepAspect = true ; 13 | opts.numAugments = 1 ; 14 | opts.transformation = 'none' ; 15 | opts.averageImage = [] ; 16 | opts.rgbVariance = zeros(0,3,'single') ; 17 | opts.interpolation = 'bilinear' ; 18 | opts.numThreads = 1 ; 19 | opts.prefetch = false ; 20 | opts = vl_argparse(opts, varargin); 21 | 22 | % if only one value is given, apply the same amount of padding to all borders 23 | if numel(opts.pad)==1, opts.pad = repmat(opts.pad,[1 4]); end 24 | if numel(opts.border)==1, opts.border = repmat(opts.border,[1 2]); end 25 | 26 | % fetch is true if images is a list of filenames (instead of 27 | % a cell array of images) 28 | fetch = numel(images) >= 1 && ischar(images{1}) ; 29 | 30 | % isjpg is true if all images to fetch are of jpeg format 31 | isjpg = fetch && strcmpi(images{1}(end-3:end),'.jpg'); 32 | 33 | assert(mod(numel(images),opts.nViews)==0, '''nViews'' is incompatible with input'); 34 | nViews = opts.nViews; 35 | nShapes = numel(images)/nViews; 36 | 37 | if opts.prefetch 38 | if isjpg, vl_imreadjpeg(images, 'numThreads', opts.numThreads, 'prefetch'); end 39 | imo = [] ; 40 | return ; 41 | end 42 | 43 | if fetch 44 | if isjpg, 45 | im = vl_imreadjpeg(images,'numThreads', opts.numThreads) ; 46 | else 47 | im = cell(size(images)); 48 | end 49 | else 50 | im = images ; 51 | end 52 | 53 | tfs = [] ; 54 | switch opts.transformation 55 | case 'none' 56 | tfs = [ 57 | .5 ; 58 | .5 ; 59 | 0 ] ; 60 | case 'f5' 61 | tfs = [... 62 | .5 0 0 1 1 .5 0 0 1 1 ; 63 | .5 0 1 0 1 .5 0 1 0 1 ; 64 | 0 0 0 0 0 1 1 1 1 1] ; 65 | case 'f25' 66 | [tx,ty] = meshgrid(linspace(0,1,5)) ; 67 | tfs = [tx(:)' ; ty(:)' ; zeros(1,numel(tx))] ; 68 | tfs_ = tfs ; 69 | tfs_(3,:) = 1 ; 70 | tfs = [tfs,tfs_] ; 71 | case 'stretch' 72 | otherwise 73 | error('Uknown transformations %s', opts.transformation) ; 74 | end 75 | [~,transformations] = sort(rand(size(tfs,2), numel(images)), 1) ; 76 | 77 | if ~isempty(opts.rgbVariance) && isempty(opts.averageImage) 78 | opts.averageImage = zeros(1,1,3) ; 79 | end 80 | if numel(opts.averageImage) == 3 81 | opts.averageImage = reshape(opts.averageImage, 1,1,3) ; 82 | end 83 | 84 | imo = zeros(opts.imageSize(1), opts.imageSize(2), 3, ... 85 | numel(images)*opts.numAugments, 'single') ; 86 | 87 | for i=1:nShapes, 88 | for j=1:nViews, 89 | % acquire image 90 | idx = (i-1)*nViews + j; 91 | if isempty(im{idx}) 92 | imt = imread(images{idx}) ; 93 | imt = single(imt) ; % faster than im2single (and multiplies by 255) 94 | else 95 | imt = im{idx} ; 96 | end 97 | if size(imt,3) == 1 98 | imt = cat(3, imt, imt, imt) ; 99 | end 100 | if j==1, 101 | imArr = zeros(size(imt,1),size(imt,2),3,nViews,'single'); 102 | end 103 | imArr(:,:,:,j) = imt; 104 | end 105 | 106 | % pad 107 | if ~isempty(opts.pad) && any(opts.pad>0), 108 | w = size(imArr,2); 109 | h = size(imArr,1); 110 | imArrTmp = imArr; 111 | imArr = 255*ones(h+sum(opts.pad(1:2)), w+sum(opts.pad(3:4)), 3, nViews, 'single'); 112 | imArr(opts.pad(1)+(1:h), opts.pad(3)+(1:w),:,:) = imArrTmp; 113 | end 114 | 115 | % resize 116 | w = size(imArr,2) ; 117 | h = size(imArr,1) ; 118 | factor = [(opts.imageSize(1)+opts.border(1))/h ... 119 | (opts.imageSize(2)+opts.border(2))/w]; 120 | 121 | if opts.keepAspect 122 | factor = max(factor) ; 123 | end 124 | if any(abs(factor - 1) > 0.0001) 125 | imArr = imresize(imArr, ... 126 | 'scale', factor, ... 127 | 'method', opts.interpolation) ; 128 | end 129 | 130 | % crop & flip 131 | w = size(imArr,2) ; 132 | h = size(imArr,1) ; 133 | for ai = 1:opts.numAugments 134 | switch opts.transformation 135 | case 'stretch' 136 | sz = round(min(opts.imageSize(1:2)' .* (1-0.1+0.2*rand(2,1)), [h;w])) ; 137 | dx = randi(w - sz(2) + 1, 1) ; 138 | dy = randi(h - sz(1) + 1, 1) ; 139 | flip = rand > 0.5 ; 140 | otherwise 141 | tf = tfs(:, transformations(mod(ai-1, numel(transformations)) + 1)) ; 142 | sz = opts.imageSize(1:2) ; 143 | dx = floor((w - sz(2)) * tf(2)) + 1 ; 144 | dy = floor((h - sz(1)) * tf(1)) + 1 ; 145 | flip = tf(3) ; 146 | end 147 | sx = round(linspace(dx, sz(2)+dx-1, opts.imageSize(2))) ; 148 | sy = round(linspace(dy, sz(1)+dy-1, opts.imageSize(1))) ; 149 | if flip, sx = fliplr(sx) ; end 150 | 151 | if ~isempty(opts.averageImage) 152 | offset = opts.averageImage ; 153 | if ~isempty(opts.rgbVariance) 154 | offset = bsxfun(@plus, offset, reshape(opts.rgbVariance * randn(3,1), 1,1,3)) ; 155 | end 156 | imo(:,:,:,(ai-1)*numel(images)+(i-1)*nViews+(1:nViews)) = bsxfun(@minus, imArr(sy,sx,:,:), offset) ; 157 | else 158 | imo(:,:,:,(ai-1)*numel(images)+(i-1)*nViews+(1:nViews)) = imArr(sy,sx,:,:) ; 159 | end 160 | end 161 | end 162 | -------------------------------------------------------------------------------- /cnn_shape_init.m: -------------------------------------------------------------------------------- 1 | function net = cnn_shape_init(classNames, varargin) 2 | opts.base = 'imagenet-matconvnet-vgg-m'; 3 | opts.restart = false; 4 | opts.nViews = 12; 5 | opts.viewpoolPos = 'relu5'; 6 | opts.viewpoolType = 'max'; 7 | opts.weightInitMethod = 'xavierimproved'; 8 | opts.scale = 1; 9 | opts.networkType = 'simplenn'; % only simplenn is supported currently 10 | opts = vl_argparse(opts, varargin); 11 | 12 | assert(strcmp(opts.networkType,'simplenn'), 'Only simplenn is supported currently'); 13 | 14 | init_bias = 0.1; 15 | nClass = length(classNames); 16 | 17 | % Load model, try to download it if not readily available 18 | if ~ischar(opts.base), 19 | net = opts.base; 20 | else 21 | netFilePath = fullfile('data','models', [opts.base '.mat']); 22 | if ~exist(netFilePath,'file'), 23 | fprintf('Downloading model (%s) ...', opts.base) ; 24 | vl_xmkdir(fullfile('data','models')) ; 25 | urlwrite(fullfile('http://www.vlfeat.org/matconvnet/models/', ... 26 | [opts.base '.mat']), netFilePath) ; 27 | fprintf(' done!\n'); 28 | end 29 | net = load(netFilePath); 30 | end 31 | assert(strcmp(net.layers{end}.type, 'softmax'), 'Wrong network format'); 32 | dataTyp = class(net.layers{end-1}.weights{1}); 33 | 34 | % Initiate the last but one layer w/ random weights 35 | widthPrev = size(net.layers{end-1}.weights{1}, 3); 36 | nClass0 = size(net.layers{end-1}.weights{1},4); 37 | if nClass0 ~= nClass || opts.restart, 38 | net.layers{end-1}.weights{1} = init_weight(opts, 1, 1, widthPrev, nClass, dataTyp); 39 | net.layers{end-1}.weights{2} = zeros(nClass, 1, dataTyp); 40 | end 41 | 42 | % Initiate other layers w/ random weights if training from scratch is desired 43 | if opts.restart, 44 | w_layers = find(cellfun(@(c) isfield(c,'weights'),net.layers)); 45 | for i=w_layers(1:end-1), 46 | sz = size(net.layers{i}.weights{1}); 47 | net.layers{i}.weights{1} = init_weight(opts, sz(1), sz(2), sz(3), sz(4), dataTyp); 48 | net.layers{i}.weights{2} = zeros(sz(4), 1, dataTyp); 49 | end 50 | end 51 | 52 | % Swap softmax w/ softmaxloss 53 | net.layers{end} = struct('type', 'softmaxloss', 'name', 'loss') ; 54 | 55 | % Insert viewpooling 56 | if opts.nViews>1, 57 | viewpoolLayer = struct('name', 'viewpool', ... 58 | 'type', 'custom', ... 59 | 'vstride', opts.nViews, ... 60 | 'method', opts.viewpoolType, ... 61 | 'forward', @viewpool_fw, ... 62 | 'backward', @viewpool_bw); 63 | net = modify_net(net, viewpoolLayer, ... 64 | 'mode','add_layer', ... 65 | 'loc',opts.viewpoolPos); 66 | 67 | if strcmp(opts.viewpoolType, 'cat'), 68 | loc = find(cellfun(@(c) strcmp(c.name,'viewpool'), net.layers)); 69 | assert(numel(loc)==1); 70 | w_layers = find(cellfun(@(c) isfield(c,'weights'), net.layers)); 71 | loc = w_layers(find((w_layers-loc)>0,1)); % location of the adjacent weight layer 72 | if ~isempty(loc), 73 | sz = size(net.layers{loc}.weights{1}); 74 | if length(sz)<4, sz = [sz ones(1,4-length(sz))]; end 75 | net.layers{loc}.weights{1} = init_weight(opts, sz(1), sz(2), sz(3)*opts.nViews, sz(4), dataTyp); 76 | net.layers{loc}.weights{2} = zeros(sz(4), 1, dataTyp); 77 | % random initialize layers after 78 | w_layers = w_layers(w_layers>loc); 79 | for i=w_layers(1:end-1), 80 | sz = size(net.layers{i}.weights{1}); 81 | if length(sz)<4, sz = [sz ones(1,4-length(sz))]; end 82 | net.layers{i}.weights{1} = init_weight(opts, sz(1), sz(2), sz(3), sz(4), dataTyp); 83 | net.layers{i}.weights{2} = zeros(sz(4), 1, dataTyp); 84 | end 85 | end 86 | end 87 | 88 | end 89 | 90 | % update meta data 91 | net.meta.classes.name = classNames; 92 | net.meta.classes.description = classNames; 93 | 94 | % speial case: when no class names specified, remove fc8/prob layers 95 | if nClass==0, 96 | net.layers = net.layers(1:end-2); 97 | end 98 | 99 | end 100 | 101 | 102 | % ------------------------------------------------------------------------- 103 | function weights = init_weight(opts, h, w, in, out, type) 104 | % ------------------------------------------------------------------------- 105 | % See K. He, X. Zhang, S. Ren, and J. Sun. Delving deep into 106 | % rectifiers: Surpassing human-level performance on imagenet 107 | % classification. CoRR, (arXiv:1502.01852v1), 2015. 108 | 109 | switch lower(opts.weightInitMethod) 110 | case 'gaussian' 111 | sc = 0.01/opts.scale ; 112 | weights = randn(h, w, in, out, type)*sc; 113 | case 'xavier' 114 | sc = sqrt(3/(h*w*in)) ; 115 | weights = (rand(h, w, in, out, type)*2 - 1)*sc ; 116 | case 'xavierimproved' 117 | sc = sqrt(2/(h*w*out)) ; 118 | weights = randn(h, w, in, out, type)*sc ; 119 | otherwise 120 | error('Unknown weight initialization method''%s''', opts.weightInitMethod) ; 121 | end 122 | 123 | end 124 | 125 | 126 | % ------------------------------------------------------------------------- 127 | function res_ip1 = viewpool_fw(layer, res_i, res_ip1) 128 | % ------------------------------------------------------------------------- 129 | [sz1, sz2, sz3, sz4] = size(res_i.x); 130 | if mod(sz4,layer.vstride)~=0, 131 | error('all shapes should have same number of views'); 132 | end 133 | if strcmp(layer.method, 'avg'), 134 | res_ip1.x = permute(... 135 | mean(reshape(res_i.x,[sz1 sz2 sz3 layer.vstride sz4/layer.vstride]), 4), ... 136 | [1,2,3,5,4]); 137 | elseif strcmp(layer.method, 'max'), 138 | res_ip1.x = permute(... 139 | max(reshape(res_i.x,[sz1 sz2 sz3 layer.vstride sz4/layer.vstride]), [], 4), ... 140 | [1,2,3,5,4]); 141 | elseif strcmp(layer.method, 'cat'), 142 | res_ip1.x = reshape(res_i.x,[sz1 sz2 sz3*layer.vstride sz4/layer.vstride]); 143 | else 144 | error('Unknown viewpool method: %s', layer.method); 145 | end 146 | 147 | end 148 | 149 | 150 | % ------------------------------------------------------------------------- 151 | function res_i = viewpool_bw(layer, res_i, res_ip1) 152 | % ------------------------------------------------------------------------- 153 | [sz1, sz2, sz3, sz4] = size(res_ip1.dzdx); 154 | if strcmp(layer.method, 'avg'), 155 | res_i.dzdx = ... 156 | reshape(repmat(reshape(res_ip1.dzdx / layer.vstride, ... 157 | [sz1 sz2 sz3 1 sz4]), ... 158 | [1 1 1 layer.vstride 1]),... 159 | [sz1 sz2 sz3 layer.vstride*sz4]); 160 | elseif strcmp(layer.method, 'max'), 161 | [~,I] = max(reshape(permute(res_i.x,[4 1 2 3]), ... 162 | [layer.vstride, sz4*sz1*sz2*sz3]),[],1); 163 | Ind = zeros(layer.vstride,sz4*sz1*sz2*sz3, 'single'); 164 | Ind(sub2ind(size(Ind),I,1:length(I))) = 1; 165 | Ind = permute(reshape(Ind,[layer.vstride*sz4,sz1,sz2,sz3]),[2 3 4 1]); 166 | res_i.dzdx = ... 167 | reshape(repmat(reshape(res_ip1.dzdx, ... 168 | [sz1 sz2 sz3 1 sz4]), ... 169 | [1 1 1 layer.vstride 1]),... 170 | [sz1 sz2 sz3 layer.vstride*sz4]) .* Ind; 171 | elseif strcmp(layer.method, 'cat'), 172 | res_i.dzdx = reshape(res_ip1.dzdx, [sz1 sz2 sz3/layer.vstride sz4*layer.vstride]); 173 | else 174 | error('Unknown viewpool method: %s', layer.method); 175 | end 176 | 177 | end 178 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | Subhransu Maji 2 | Hang Su 3 | Evangelos Kalogerakis 4 | Yanjie Li -------------------------------------------------------------------------------- /data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !imdb*.mat 4 | -------------------------------------------------------------------------------- /dataset/setup_imdb_generic.m: -------------------------------------------------------------------------------- 1 | function imdb = setup_imdb_generic(datasetDir, varargin) 2 | 3 | error('%s not yet implemented',mfilename); 4 | -------------------------------------------------------------------------------- /dataset/setup_imdb_shapenet.m: -------------------------------------------------------------------------------- 1 | function imdb = setup_imdb_shapenet(datasetDir, varargin) 2 | 3 | opts.ext = '.jpg'; % extension of target files 4 | opts.nViews = 80; 5 | opts.useSubclass = false; 6 | opts.trainFile = fullfile(datasetDir,'train.csv'); 7 | opts.valFile = fullfile(datasetDir,'val.csv'); 8 | opts = vl_argparse(opts, varargin); 9 | 10 | imdb.imageDir = datasetDir; 11 | 12 | trainAnno = csvread(opts.trainFile, 1); 13 | valAnno = csvread(opts.valFile, 1); 14 | assert(numel(unique([trainAnno(:,1);valAnno(:,1)])) ... 15 | ==size(trainAnno,1)+size(valAnno,1)); 16 | 17 | if opts.useSubclass, 18 | labelCol = 3; 19 | else 20 | labelCol = 2; 21 | end 22 | 23 | classIds = sort(unique([trainAnno(:,labelCol); valAnno(:,labelCol)])'); 24 | [~,trainAnno(:,labelCol)] = ismember(trainAnno(:,labelCol),classIds); 25 | [~,valAnno(:,labelCol)] = ismember(valAnno(:,labelCol),classIds); 26 | 27 | % meta 28 | imdb.meta.classes = arrayfun(@(i) sprintf('%08d',i),classIds,'UniformOutput',false); 29 | imdb.meta.sets = {'train', 'val', 'test'}; 30 | 31 | % images 32 | imdb.images.name = {}; 33 | imdb.images.class = []; 34 | imdb.images.set = []; 35 | imdb.images.sid = []; 36 | 37 | % train 38 | fprintf('Scanning for training images ... '); 39 | [imdb, nTrainShapes] = add_to_imdb(imdb, 'train', 1, trainAnno, opts); 40 | fprintf('done! %d shapes found.\n', nTrainShapes); 41 | % val 42 | fprintf('Scanning for validation images ... '); 43 | [imdb, nValShapes] = add_to_imdb(imdb, 'val', 2, valAnno, opts); 44 | fprintf('done! %d shapes found.\n', nValShapes); 45 | % test 46 | fprintf('Scanning for testing images ... '); 47 | [imdb, nTestShapes] = add_to_imdb(imdb, 'test', 3, [], opts); 48 | fprintf('done! %d shapes found.\n', nTestShapes); 49 | 50 | imdb.images.id = 1:numel(imdb.images.name); 51 | 52 | function [imdb, nShapesAdded] = add_to_imdb(imdb, subDir, setId, anno, opts); 53 | if opts.useSubclass, 54 | labelCol = 3; 55 | else 56 | labelCol = 2; 57 | end 58 | files = dir(fullfile(imdb.imageDir,subDir,['*' opts.ext])); 59 | files = {files.name}; 60 | sids = cellfun(@get_shape_id, files); 61 | vids = cellfun(@get_shape_vid, files); 62 | [~, I] = sort(vids); 63 | files = files(I); 64 | [sids, I] = sort(sids(I)); 65 | files = files(I); 66 | sids0 = sids(1:opts.nViews:end); 67 | assert(numel(sids0)==numel(unique(sids))); 68 | if isempty(anno), % test 69 | nShapesAdded = numel(sids0); 70 | label = -1*ones(1,opts.nViews*nShapesAdded); 71 | else 72 | [I I2] = ismember(sids0,anno(:,1)'); 73 | nShapesAdded = sum(I); 74 | label = anno(I2(I),labelCol); 75 | label = repmat(label', [opts.nViews 1]); 76 | label = label(:)'; 77 | I = repmat(I, [opts.nViews 1]); 78 | I = I(:)'; 79 | files = files(I); 80 | sids = sids(I); 81 | end 82 | imdb.images.name = [imdb.images.name cellfun(@(s) fullfile(subDir,s), files, 'UniformOutput', false)]; 83 | imdb.images.sid = [imdb.images.sid sids]; 84 | imdb.images.class = [imdb.images.class label]; 85 | imdb.images.set = [imdb.images.set setId*ones(1,opts.nViews*nShapesAdded)]; 86 | if ~isempty(anno) && size(anno,1)~=nShapesAdded, 87 | warning('%d %s shapes not found', size(anno,1)-nShapesAdded, subDir); 88 | end 89 | 90 | function sid = get_shape_id(filename) 91 | suffix_idx = strfind(filename,'_'); 92 | if numel(suffix_idx)~=2, 93 | sid = []; 94 | else 95 | sid = str2double(filename(suffix_idx(1)+1:suffix_idx(2)-1)); 96 | end 97 | 98 | function vid = get_shape_vid(filename) 99 | suffix_idx = strfind(filename,'_'); 100 | ext_idx = strfind(filename,'.'); 101 | if isempty(suffix_idx) || isempty(ext_idx), 102 | vid = []; 103 | else 104 | vid = str2double(filename(suffix_idx(end)+1:ext_idx(end)-1)); 105 | end 106 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.mexa64 4 | predict 5 | train 6 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/COPYRIGHT: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2007-2014 The LIBLINEAR Project. 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | 3. Neither name of copyright holders nor the names of its contributors 17 | may be used to endorse or promote products derived from this software 18 | without specific prior written permission. 19 | 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 25 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CC ?= gcc 3 | CFLAGS = -Wall -Wconversion -O3 -fPIC 4 | LIBS = blas/blas.a 5 | SHVER = 2 6 | OS = $(shell uname) 7 | #LIBS = -lblas 8 | 9 | all: train predict 10 | 11 | lib: linear.o tron.o blas/blas.a 12 | if [ "$(OS)" = "Darwin" ]; then \ 13 | SHARED_LIB_FLAG="-dynamiclib -Wl,-install_name,liblinear.so.$(SHVER)"; \ 14 | else \ 15 | SHARED_LIB_FLAG="-shared -Wl,-soname,liblinear.so.$(SHVER)"; \ 16 | fi; \ 17 | $(CXX) $${SHARED_LIB_FLAG} linear.o tron.o blas/blas.a -o liblinear.so.$(SHVER) 18 | 19 | train: tron.o linear.o train.c blas/blas.a 20 | $(CXX) $(CFLAGS) -o train train.c tron.o linear.o $(LIBS) 21 | 22 | predict: tron.o linear.o predict.c blas/blas.a 23 | $(CXX) $(CFLAGS) -o predict predict.c tron.o linear.o $(LIBS) 24 | 25 | tron.o: tron.cpp tron.h 26 | $(CXX) $(CFLAGS) -c -o tron.o tron.cpp 27 | 28 | linear.o: linear.cpp linear.h 29 | $(CXX) $(CFLAGS) -c -o linear.o linear.cpp 30 | 31 | blas/blas.a: blas/*.c blas/*.h 32 | make -C blas OPTFLAGS='$(CFLAGS)' CC='$(CC)'; 33 | 34 | clean: 35 | make -C blas clean 36 | make -C matlab clean 37 | rm -f *~ tron.o linear.o train predict liblinear.so.$(SHVER) 38 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/Makefile.win: -------------------------------------------------------------------------------- 1 | #You must ensure nmake.exe, cl.exe, link.exe are in system path. 2 | #VCVARS32.bat 3 | #Under dosbox prompt 4 | #nmake -f Makefile.win 5 | 6 | ########################################## 7 | CXX = cl.exe 8 | CFLAGS = /nologo /O2 /EHsc /I. /D _WIN32 /D _CRT_SECURE_NO_DEPRECATE 9 | TARGET = windows 10 | 11 | all: $(TARGET)\train.exe $(TARGET)\predict.exe 12 | 13 | $(TARGET)\train.exe: tron.obj linear.obj train.c blas\*.c 14 | $(CXX) $(CFLAGS) -Fe$(TARGET)\train.exe tron.obj linear.obj train.c blas\*.c 15 | 16 | $(TARGET)\predict.exe: tron.obj linear.obj predict.c blas\*.c 17 | $(CXX) $(CFLAGS) -Fe$(TARGET)\predict.exe tron.obj linear.obj predict.c blas\*.c 18 | 19 | linear.obj: linear.cpp linear.h 20 | $(CXX) $(CFLAGS) -c linear.cpp 21 | 22 | tron.obj: tron.cpp tron.h 23 | $(CXX) $(CFLAGS) -c tron.cpp 24 | 25 | lib: linear.cpp linear.h linear.def tron.obj 26 | $(CXX) $(CFLAGS) -LD linear.cpp tron.obj blas\*.c -Fe$(TARGET)\liblinear -link -DEF:linear.def 27 | 28 | clean: 29 | -erase /Q *.obj $(TARGET)\. 30 | 31 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/Makefile: -------------------------------------------------------------------------------- 1 | AR = ar rcv 2 | RANLIB = ranlib 3 | 4 | HEADERS = blas.h blasp.h 5 | FILES = dnrm2.o daxpy.o ddot.o dscal.o 6 | 7 | CFLAGS = $(OPTFLAGS) 8 | FFLAGS = $(OPTFLAGS) 9 | 10 | blas: $(FILES) $(HEADERS) 11 | $(AR) blas.a $(FILES) 12 | $(RANLIB) blas.a 13 | 14 | clean: 15 | - rm -f *.o 16 | - rm -f *.a 17 | - rm -f *~ 18 | 19 | .c.o: 20 | $(CC) $(CFLAGS) -c $*.c 21 | 22 | 23 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/blas.h: -------------------------------------------------------------------------------- 1 | /* blas.h -- C header file for BLAS Ver 1.0 */ 2 | /* Jesse Bennett March 23, 2000 */ 3 | 4 | /** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." 5 | 6 | - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */ 7 | 8 | #ifndef BLAS_INCLUDE 9 | #define BLAS_INCLUDE 10 | 11 | /* Data types specific to BLAS implementation */ 12 | typedef struct { float r, i; } fcomplex; 13 | typedef struct { double r, i; } dcomplex; 14 | typedef int blasbool; 15 | 16 | #include "blasp.h" /* Prototypes for all BLAS functions */ 17 | 18 | #define FALSE 0 19 | #define TRUE 1 20 | 21 | /* Macro functions */ 22 | #define MIN(a,b) ((a) <= (b) ? (a) : (b)) 23 | #define MAX(a,b) ((a) >= (b) ? (a) : (b)) 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/daxpy.c: -------------------------------------------------------------------------------- 1 | #include "blas.h" 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | int daxpy_(int *n, double *sa, double *sx, int *incx, double *sy, 8 | int *incy) 9 | { 10 | long int i, m, ix, iy, nn, iincx, iincy; 11 | register double ssa; 12 | 13 | /* constant times a vector plus a vector. 14 | uses unrolled loop for increments equal to one. 15 | jack dongarra, linpack, 3/11/78. 16 | modified 12/3/93, array(1) declarations changed to array(*) */ 17 | 18 | /* Dereference inputs */ 19 | nn = *n; 20 | ssa = *sa; 21 | iincx = *incx; 22 | iincy = *incy; 23 | 24 | if( nn > 0 && ssa != 0.0 ) 25 | { 26 | if (iincx == 1 && iincy == 1) /* code for both increments equal to 1 */ 27 | { 28 | m = nn-3; 29 | for (i = 0; i < m; i += 4) 30 | { 31 | sy[i] += ssa * sx[i]; 32 | sy[i+1] += ssa * sx[i+1]; 33 | sy[i+2] += ssa * sx[i+2]; 34 | sy[i+3] += ssa * sx[i+3]; 35 | } 36 | for ( ; i < nn; ++i) /* clean-up loop */ 37 | sy[i] += ssa * sx[i]; 38 | } 39 | else /* code for unequal increments or equal increments not equal to 1 */ 40 | { 41 | ix = iincx >= 0 ? 0 : (1 - nn) * iincx; 42 | iy = iincy >= 0 ? 0 : (1 - nn) * iincy; 43 | for (i = 0; i < nn; i++) 44 | { 45 | sy[iy] += ssa * sx[ix]; 46 | ix += iincx; 47 | iy += iincy; 48 | } 49 | } 50 | } 51 | 52 | return 0; 53 | } /* daxpy_ */ 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/ddot.c: -------------------------------------------------------------------------------- 1 | #include "blas.h" 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | double ddot_(int *n, double *sx, int *incx, double *sy, int *incy) 8 | { 9 | long int i, m, nn, iincx, iincy; 10 | double stemp; 11 | long int ix, iy; 12 | 13 | /* forms the dot product of two vectors. 14 | uses unrolled loops for increments equal to one. 15 | jack dongarra, linpack, 3/11/78. 16 | modified 12/3/93, array(1) declarations changed to array(*) */ 17 | 18 | /* Dereference inputs */ 19 | nn = *n; 20 | iincx = *incx; 21 | iincy = *incy; 22 | 23 | stemp = 0.0; 24 | if (nn > 0) 25 | { 26 | if (iincx == 1 && iincy == 1) /* code for both increments equal to 1 */ 27 | { 28 | m = nn-4; 29 | for (i = 0; i < m; i += 5) 30 | stemp += sx[i] * sy[i] + sx[i+1] * sy[i+1] + sx[i+2] * sy[i+2] + 31 | sx[i+3] * sy[i+3] + sx[i+4] * sy[i+4]; 32 | 33 | for ( ; i < nn; i++) /* clean-up loop */ 34 | stemp += sx[i] * sy[i]; 35 | } 36 | else /* code for unequal increments or equal increments not equal to 1 */ 37 | { 38 | ix = 0; 39 | iy = 0; 40 | if (iincx < 0) 41 | ix = (1 - nn) * iincx; 42 | if (iincy < 0) 43 | iy = (1 - nn) * iincy; 44 | for (i = 0; i < nn; i++) 45 | { 46 | stemp += sx[ix] * sy[iy]; 47 | ix += iincx; 48 | iy += iincy; 49 | } 50 | } 51 | } 52 | 53 | return stemp; 54 | } /* ddot_ */ 55 | 56 | #ifdef __cplusplus 57 | } 58 | #endif 59 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/dnrm2.c: -------------------------------------------------------------------------------- 1 | #include /* Needed for fabs() and sqrt() */ 2 | #include "blas.h" 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | double dnrm2_(int *n, double *x, int *incx) 9 | { 10 | long int ix, nn, iincx; 11 | double norm, scale, absxi, ssq, temp; 12 | 13 | /* DNRM2 returns the euclidean norm of a vector via the function 14 | name, so that 15 | 16 | DNRM2 := sqrt( x'*x ) 17 | 18 | -- This version written on 25-October-1982. 19 | Modified on 14-October-1993 to inline the call to SLASSQ. 20 | Sven Hammarling, Nag Ltd. */ 21 | 22 | /* Dereference inputs */ 23 | nn = *n; 24 | iincx = *incx; 25 | 26 | if( nn > 0 && iincx > 0 ) 27 | { 28 | if (nn == 1) 29 | { 30 | norm = fabs(x[0]); 31 | } 32 | else 33 | { 34 | scale = 0.0; 35 | ssq = 1.0; 36 | 37 | /* The following loop is equivalent to this call to the LAPACK 38 | auxiliary routine: CALL SLASSQ( N, X, INCX, SCALE, SSQ ) */ 39 | 40 | for (ix=(nn-1)*iincx; ix>=0; ix-=iincx) 41 | { 42 | if (x[ix] != 0.0) 43 | { 44 | absxi = fabs(x[ix]); 45 | if (scale < absxi) 46 | { 47 | temp = scale / absxi; 48 | ssq = ssq * (temp * temp) + 1.0; 49 | scale = absxi; 50 | } 51 | else 52 | { 53 | temp = absxi / scale; 54 | ssq += temp * temp; 55 | } 56 | } 57 | } 58 | norm = scale * sqrt(ssq); 59 | } 60 | } 61 | else 62 | norm = 0.0; 63 | 64 | return norm; 65 | 66 | } /* dnrm2_ */ 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/blas/dscal.c: -------------------------------------------------------------------------------- 1 | #include "blas.h" 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | int dscal_(int *n, double *sa, double *sx, int *incx) 8 | { 9 | long int i, m, nincx, nn, iincx; 10 | double ssa; 11 | 12 | /* scales a vector by a constant. 13 | uses unrolled loops for increment equal to 1. 14 | jack dongarra, linpack, 3/11/78. 15 | modified 3/93 to return if incx .le. 0. 16 | modified 12/3/93, array(1) declarations changed to array(*) */ 17 | 18 | /* Dereference inputs */ 19 | nn = *n; 20 | iincx = *incx; 21 | ssa = *sa; 22 | 23 | if (nn > 0 && iincx > 0) 24 | { 25 | if (iincx == 1) /* code for increment equal to 1 */ 26 | { 27 | m = nn-4; 28 | for (i = 0; i < m; i += 5) 29 | { 30 | sx[i] = ssa * sx[i]; 31 | sx[i+1] = ssa * sx[i+1]; 32 | sx[i+2] = ssa * sx[i+2]; 33 | sx[i+3] = ssa * sx[i+3]; 34 | sx[i+4] = ssa * sx[i+4]; 35 | } 36 | for ( ; i < nn; ++i) /* clean-up loop */ 37 | sx[i] = ssa * sx[i]; 38 | } 39 | else /* code for increment not equal to 1 */ 40 | { 41 | nincx = nn * iincx; 42 | for (i = 0; i < nincx; i += iincx) 43 | sx[i] = ssa * sx[i]; 44 | } 45 | } 46 | 47 | return 0; 48 | } /* dscal_ */ 49 | 50 | #ifdef __cplusplus 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/linear.def: -------------------------------------------------------------------------------- 1 | LIBRARY liblinear 2 | EXPORTS 3 | train @1 4 | cross_validation @2 5 | save_model @3 6 | load_model @4 7 | get_nr_feature @5 8 | get_nr_class @6 9 | get_labels @7 10 | predict_values @8 11 | predict @9 12 | predict_probability @10 13 | free_and_destroy_model @11 14 | free_model_content @12 15 | destroy_param @13 16 | check_parameter @14 17 | check_probability_model @15 18 | set_print_string_function @16 19 | get_decfun_coef @17 20 | get_decfun_bias @18 21 | check_regression_model @19 22 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/linear.h: -------------------------------------------------------------------------------- 1 | #ifndef _LIBLINEAR_H 2 | #define _LIBLINEAR_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | struct feature_node 9 | { 10 | int index; 11 | double value; 12 | }; 13 | 14 | struct problem 15 | { 16 | int l, n; 17 | double *y; 18 | struct feature_node **x; 19 | double bias; /* < 0 if no bias term */ 20 | }; 21 | 22 | enum { L2R_LR, L2R_L2LOSS_SVC_DUAL, L2R_L2LOSS_SVC, L2R_L1LOSS_SVC_DUAL, MCSVM_CS, L1R_L2LOSS_SVC, L1R_LR, L2R_LR_DUAL, L2R_L2LOSS_SVR = 11, L2R_L2LOSS_SVR_DUAL, L2R_L1LOSS_SVR_DUAL }; /* solver_type */ 23 | 24 | struct parameter 25 | { 26 | int solver_type; 27 | 28 | /* these are for training only */ 29 | double eps; /* stopping criteria */ 30 | double C; 31 | int nr_weight; 32 | int *weight_label; 33 | double* weight; 34 | double p; 35 | }; 36 | 37 | struct model 38 | { 39 | struct parameter param; 40 | int nr_class; /* number of classes */ 41 | int nr_feature; 42 | double *w; 43 | int *label; /* label of each class */ 44 | double bias; 45 | }; 46 | 47 | struct model* train(const struct problem *prob, const struct parameter *param); 48 | void cross_validation(const struct problem *prob, const struct parameter *param, int nr_fold, double *target); 49 | 50 | double predict_values(const struct model *model_, const struct feature_node *x, double* dec_values); 51 | double predict(const struct model *model_, const struct feature_node *x); 52 | double predict_probability(const struct model *model_, const struct feature_node *x, double* prob_estimates); 53 | 54 | int save_model(const char *model_file_name, const struct model *model_); 55 | struct model *load_model(const char *model_file_name); 56 | 57 | int get_nr_feature(const struct model *model_); 58 | int get_nr_class(const struct model *model_); 59 | void get_labels(const struct model *model_, int* label); 60 | double get_decfun_coef(const struct model *model_, int feat_idx, int label_idx); 61 | double get_decfun_bias(const struct model *model_, int label_idx); 62 | 63 | void free_model_content(struct model *model_ptr); 64 | void free_and_destroy_model(struct model **model_ptr_ptr); 65 | void destroy_param(struct parameter *param); 66 | 67 | const char *check_parameter(const struct problem *prob, const struct parameter *param); 68 | int check_probability_model(const struct model *model); 69 | int check_regression_model(const struct model *model); 70 | void set_print_string_function(void (*print_func) (const char*)); 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif /* _LIBLINEAR_H */ 77 | 78 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile is used under Linux 2 | 3 | MATLABDIR ?= /usr/local/matlab 4 | CXX ?= g++ 5 | #CXX = g++-3.3 6 | CC ?= gcc 7 | CFLAGS = -Wall -Wconversion -O3 -fPIC -I$(MATLABDIR)/extern/include -I.. 8 | 9 | MEX = $(MATLABDIR)/bin/mex 10 | MEX_OPTION = CC="$(CXX)" CXX="$(CXX)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CFLAGS)" 11 | # comment the following line if you use MATLAB on a 32-bit computer 12 | MEX_OPTION += -largeArrayDims 13 | MEX_EXT = $(shell $(MATLABDIR)/bin/mexext) 14 | 15 | all: matlab 16 | 17 | matlab: binary 18 | 19 | octave: 20 | @echo "please type make under Octave" 21 | binary: train.$(MEX_EXT) predict.$(MEX_EXT) libsvmread.$(MEX_EXT) libsvmwrite.$(MEX_EXT) 22 | 23 | train.$(MEX_EXT): train.c ../linear.h ../tron.o ../linear.o linear_model_matlab.o ../blas/blas.a 24 | $(MEX) $(MEX_OPTION) train.c ../tron.o ../linear.o linear_model_matlab.o ../blas/blas.a 25 | 26 | predict.$(MEX_EXT): predict.c ../linear.h ../tron.o ../linear.o linear_model_matlab.o ../blas/blas.a 27 | $(MEX) $(MEX_OPTION) predict.c ../tron.o ../linear.o linear_model_matlab.o ../blas/blas.a 28 | 29 | libsvmread.$(MEX_EXT): libsvmread.c 30 | $(MEX) $(MEX_OPTION) libsvmread.c 31 | 32 | libsvmwrite.$(MEX_EXT): libsvmwrite.c 33 | $(MEX) $(MEX_OPTION) libsvmwrite.c 34 | 35 | linear_model_matlab.o: linear_model_matlab.c ../linear.h 36 | $(CXX) $(CFLAGS) -c linear_model_matlab.c 37 | 38 | ../linear.o: ../linear.cpp ../linear.h 39 | make -C .. linear.o 40 | 41 | ../tron.o: ../tron.cpp ../tron.h 42 | make -C .. tron.o 43 | 44 | ../blas/blas.a: ../blas/*.c ../blas/*.h 45 | make -C ../blas OPTFLAGS='$(CFLAGS)' CC='$(CC)'; 46 | 47 | clean: 48 | make -C ../blas clean 49 | rm -f *~ *.o *.mex* *.obj ../linear.o ../tron.o 50 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/README: -------------------------------------------------------------------------------- 1 | -------------------------------------------- 2 | --- MATLAB/OCTAVE interface of LIBLINEAR --- 3 | -------------------------------------------- 4 | 5 | Table of Contents 6 | ================= 7 | 8 | - Introduction 9 | - Installation 10 | - Usage 11 | - Returned Model Structure 12 | - Other Utilities 13 | - Examples 14 | - Additional Information 15 | 16 | 17 | Introduction 18 | ============ 19 | 20 | This tool provides a simple interface to LIBLINEAR, a library for 21 | large-scale regularized linear classification and regression 22 | (http://www.csie.ntu.edu.tw/~cjlin/liblinear). It is very easy to use 23 | as the usage and the way of specifying parameters are the same as that 24 | of LIBLINEAR. 25 | 26 | Installation 27 | ============ 28 | 29 | On Windows systems, pre-built binary files are already in the 30 | directory '..\windows', so no need to conduct installation. Now we 31 | provide binary files only for 64bit MATLAB on Windows. If you would 32 | like to re-build the package, please rely on the following steps. 33 | 34 | We recommend using make.m on both MATLAB and OCTAVE. Just type 'make' 35 | to build 'libsvmread.mex', 'libsvmwrite.mex', 'train.mex', and 36 | 'predict.mex'. 37 | 38 | On MATLAB or Octave: 39 | 40 | >> make 41 | 42 | If make.m does not work on MATLAB (especially for Windows), try 'mex 43 | -setup' to choose a suitable compiler for mex. Make sure your compiler 44 | is accessible and workable. Then type 'make' to start the 45 | installation. 46 | 47 | Example: 48 | 49 | matlab>> mex -setup 50 | (ps: MATLAB will show the following messages to setup default compiler.) 51 | Please choose your compiler for building external interface (MEX) files: 52 | Would you like mex to locate installed compilers [y]/n? y 53 | Select a compiler: 54 | [1] Microsoft Visual C/C++ version 7.1 in C:\Program Files\Microsoft Visual Studio 55 | [0] None 56 | Compiler: 1 57 | Please verify your choices: 58 | Compiler: Microsoft Visual C/C++ 7.1 59 | Location: C:\Program Files\Microsoft Visual Studio 60 | Are these correct?([y]/n): y 61 | 62 | matlab>> make 63 | 64 | On Unix systems, if neither make.m nor 'mex -setup' works, please use 65 | Makefile and type 'make' in a command window. Note that we assume 66 | your MATLAB is installed in '/usr/local/matlab'. If not, please change 67 | MATLABDIR in Makefile. 68 | 69 | Example: 70 | linux> make 71 | 72 | To use octave, type 'make octave': 73 | 74 | Example: 75 | linux> make octave 76 | 77 | For a list of supported/compatible compilers for MATLAB, please check 78 | the following page: 79 | 80 | http://www.mathworks.com/support/compilers/current_release/ 81 | 82 | Usage 83 | ===== 84 | 85 | matlab> model = train(training_label_vector, training_instance_matrix [,'liblinear_options', 'col']); 86 | 87 | -training_label_vector: 88 | An m by 1 vector of training labels. (type must be double) 89 | -training_instance_matrix: 90 | An m by n matrix of m training instances with n features. 91 | It must be a sparse matrix. (type must be double) 92 | -liblinear_options: 93 | A string of training options in the same format as that of LIBLINEAR. 94 | -col: 95 | if 'col' is set, each column of training_instance_matrix is a data instance. Otherwise each row is a data instance. 96 | 97 | matlab> [predicted_label, accuracy, decision_values/prob_estimates] = predict(testing_label_vector, testing_instance_matrix, model [, 'liblinear_options', 'col']); 98 | matlab> [predicted_label] = predict(testing_label_vector, testing_instance_matrix, model [, 'liblinear_options', 'col']); 99 | 100 | -testing_label_vector: 101 | An m by 1 vector of prediction labels. If labels of test 102 | data are unknown, simply use any random values. (type must be double) 103 | -testing_instance_matrix: 104 | An m by n matrix of m testing instances with n features. 105 | It must be a sparse matrix. (type must be double) 106 | -model: 107 | The output of train. 108 | -liblinear_options: 109 | A string of testing options in the same format as that of LIBLINEAR. 110 | -col: 111 | if 'col' is set, each column of testing_instance_matrix is a data instance. Otherwise each row is a data instance. 112 | 113 | Returned Model Structure 114 | ======================== 115 | 116 | The 'train' function returns a model which can be used for future 117 | prediction. It is a structure and is organized as [Parameters, nr_class, 118 | nr_feature, bias, Label, w]: 119 | 120 | -Parameters: Parameters 121 | -nr_class: number of classes; = 2 for regression 122 | -nr_feature: number of features in training data (without including the bias term) 123 | -bias: If >= 0, we assume one additional feature is added to the end 124 | of each data instance. 125 | -Label: label of each class; empty for regression 126 | -w: a nr_w-by-n matrix for the weights, where n is nr_feature 127 | or nr_feature+1 depending on the existence of the bias term. 128 | nr_w is 1 if nr_class=2 and -s is not 4 (i.e., not 129 | multi-class svm by Crammer and Singer). It is 130 | nr_class otherwise. 131 | 132 | If the '-v' option is specified, cross validation is conducted and the 133 | returned model is just a scalar: cross-validation accuracy for 134 | classification and mean-squared error for regression. 135 | 136 | Result of Prediction 137 | ==================== 138 | 139 | The function 'predict' has three outputs. The first one, 140 | predicted_label, is a vector of predicted labels. The second output, 141 | accuracy, is a vector including accuracy (for classification), mean 142 | squared error, and squared correlation coefficient (for regression). 143 | The third is a matrix containing decision values or probability 144 | estimates (if '-b 1' is specified). If k is the number of classes 145 | and k' is the number of classifiers (k'=1 if k=2, otherwise k'=k), for decision values, 146 | each row includes results of k' binary linear classifiers. For probabilities, 147 | each row contains k values indicating the probability that the testing instance is in 148 | each class. Note that the order of classes here is the same as 'Label' 149 | field in the model structure. 150 | 151 | Other Utilities 152 | =============== 153 | 154 | A matlab function libsvmread reads files in LIBSVM format: 155 | 156 | [label_vector, instance_matrix] = libsvmread('data.txt'); 157 | 158 | Two outputs are labels and instances, which can then be used as inputs 159 | of svmtrain or svmpredict. 160 | 161 | A matlab function libsvmwrite writes Matlab matrix to a file in LIBSVM format: 162 | 163 | libsvmwrite('data.txt', label_vector, instance_matrix] 164 | 165 | The instance_matrix must be a sparse matrix. (type must be double) 166 | For windows, `libsvmread.mexw64' and `libsvmwrite.mexw64' are ready in 167 | the directory `..\windows'. 168 | 169 | These codes are prepared by Rong-En Fan and Kai-Wei Chang from National 170 | Taiwan University. 171 | 172 | Examples 173 | ======== 174 | 175 | Train and test on the provided data heart_scale: 176 | 177 | matlab> [heart_scale_label, heart_scale_inst] = libsvmread('../heart_scale'); 178 | matlab> model = train(heart_scale_label, heart_scale_inst, '-c 1'); 179 | matlab> [predict_label, accuracy, dec_values] = predict(heart_scale_label, heart_scale_inst, model); % test the training data 180 | 181 | Note that for testing, you can put anything in the testing_label_vector. 182 | 183 | For probability estimates, you need '-b 1' only in the testing phase: 184 | 185 | matlab> [predict_label, accuracy, prob_estimates] = predict(heart_scale_label, heart_scale_inst, model, '-b 1'); 186 | 187 | Additional Information 188 | ====================== 189 | 190 | Please cite LIBLINEAR as follows 191 | 192 | R.-E. Fan, K.-W. Chang, C.-J. Hsieh, X.-R. Wang, and C.-J. Lin. 193 | LIBLINEAR: A Library for Large Linear Classification, Journal of 194 | Machine Learning Research 9(2008), 1871-1874.Software available at 195 | http://www.csie.ntu.edu.tw/~cjlin/liblinear 196 | 197 | For any question, please contact Chih-Jen Lin . 198 | 199 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/libsvmread.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mex.h" 8 | 9 | #ifdef MX_API_VER 10 | #if MX_API_VER < 0x07030000 11 | typedef int mwIndex; 12 | #endif 13 | #endif 14 | #ifndef max 15 | #define max(x,y) (((x)>(y))?(x):(y)) 16 | #endif 17 | #ifndef min 18 | #define min(x,y) (((x)<(y))?(x):(y)) 19 | #endif 20 | 21 | void exit_with_help() 22 | { 23 | mexPrintf( 24 | "Usage: [label_vector, instance_matrix] = libsvmread('filename');\n" 25 | ); 26 | } 27 | 28 | static void fake_answer(int nlhs, mxArray *plhs[]) 29 | { 30 | int i; 31 | for(i=0;i start from 0 86 | strtok(line," \t"); // label 87 | while (1) 88 | { 89 | idx = strtok(NULL,":"); // index:value 90 | val = strtok(NULL," \t"); 91 | if(val == NULL) 92 | break; 93 | 94 | errno = 0; 95 | index = (int) strtol(idx,&endptr,10); 96 | if(endptr == idx || errno != 0 || *endptr != '\0' || index <= inst_max_index) 97 | { 98 | mexPrintf("Wrong input format at line %d\n",l+1); 99 | fake_answer(nlhs, plhs); 100 | return; 101 | } 102 | else 103 | inst_max_index = index; 104 | 105 | min_index = min(min_index, index); 106 | elements++; 107 | } 108 | max_index = max(max_index, inst_max_index); 109 | l++; 110 | } 111 | rewind(fp); 112 | 113 | // y 114 | plhs[0] = mxCreateDoubleMatrix(l, 1, mxREAL); 115 | // x^T 116 | if (min_index <= 0) 117 | plhs[1] = mxCreateSparse(max_index-min_index+1, l, elements, mxREAL); 118 | else 119 | plhs[1] = mxCreateSparse(max_index, l, elements, mxREAL); 120 | 121 | labels = mxGetPr(plhs[0]); 122 | samples = mxGetPr(plhs[1]); 123 | ir = mxGetIr(plhs[1]); 124 | jc = mxGetJc(plhs[1]); 125 | 126 | k=0; 127 | for(i=0;i start from 0 158 | 159 | errno = 0; 160 | samples[k] = strtod(val,&endptr); 161 | if (endptr == val || errno != 0 || (*endptr != '\0' && !isspace(*endptr))) 162 | { 163 | mexPrintf("Wrong input format at line %d\n",i+1); 164 | fake_answer(nlhs, plhs); 165 | return; 166 | } 167 | ++k; 168 | } 169 | } 170 | jc[l] = k; 171 | 172 | fclose(fp); 173 | free(line); 174 | 175 | { 176 | mxArray *rhs[1], *lhs[1]; 177 | rhs[0] = plhs[1]; 178 | if(mexCallMATLAB(1, lhs, 1, rhs, "transpose")) 179 | { 180 | mexPrintf("Error: cannot transpose problem\n"); 181 | fake_answer(nlhs, plhs); 182 | return; 183 | } 184 | plhs[1] = lhs[0]; 185 | } 186 | } 187 | 188 | void mexFunction( int nlhs, mxArray *plhs[], 189 | int nrhs, const mxArray *prhs[] ) 190 | { 191 | char filename[256]; 192 | 193 | if(nrhs != 1 || nlhs != 2) 194 | { 195 | exit_with_help(); 196 | fake_answer(nlhs, plhs); 197 | return; 198 | } 199 | 200 | mxGetString(prhs[0], filename, mxGetN(prhs[0]) + 1); 201 | 202 | if(filename == NULL) 203 | { 204 | mexPrintf("Error: filename is NULL\n"); 205 | return; 206 | } 207 | 208 | read_problem(filename, nlhs, plhs); 209 | 210 | return; 211 | } 212 | 213 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/libsvmwrite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mex.h" 5 | 6 | #ifdef MX_API_VER 7 | #if MX_API_VER < 0x07030000 8 | typedef int mwIndex; 9 | #endif 10 | #endif 11 | 12 | void exit_with_help() 13 | { 14 | mexPrintf( 15 | "Usage: libsvmwrite('filename', label_vector, instance_matrix);\n" 16 | ); 17 | } 18 | 19 | static void fake_answer(int nlhs, mxArray *plhs[]) 20 | { 21 | int i; 22 | for(i=0;i 0) 88 | { 89 | exit_with_help(); 90 | fake_answer(nlhs, plhs); 91 | return; 92 | } 93 | 94 | // Transform the input Matrix to libsvm format 95 | if(nrhs == 3) 96 | { 97 | char filename[256]; 98 | if(!mxIsDouble(prhs[1]) || !mxIsDouble(prhs[2])) 99 | { 100 | mexPrintf("Error: label vector and instance matrix must be double\n"); 101 | return; 102 | } 103 | 104 | mxGetString(prhs[0], filename, mxGetN(prhs[0])+1); 105 | 106 | if(mxIsSparse(prhs[2])) 107 | libsvmwrite(filename, prhs[1], prhs[2]); 108 | else 109 | { 110 | mexPrintf("Instance_matrix must be sparse\n"); 111 | return; 112 | } 113 | } 114 | else 115 | { 116 | exit_with_help(); 117 | return; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/linear_model_matlab.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "../linear.h" 4 | 5 | #include "mex.h" 6 | 7 | #ifdef MX_API_VER 8 | #if MX_API_VER < 0x07030000 9 | typedef int mwIndex; 10 | #endif 11 | #endif 12 | 13 | #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) 14 | 15 | #define NUM_OF_RETURN_FIELD 6 16 | 17 | static const char *field_names[] = { 18 | "Parameters", 19 | "nr_class", 20 | "nr_feature", 21 | "bias", 22 | "Label", 23 | "w", 24 | }; 25 | 26 | const char *model_to_matlab_structure(mxArray *plhs[], struct model *model_) 27 | { 28 | int i; 29 | int nr_w; 30 | double *ptr; 31 | mxArray *return_model, **rhs; 32 | int out_id = 0; 33 | int n, w_size; 34 | 35 | rhs = (mxArray **)mxMalloc(sizeof(mxArray *)*NUM_OF_RETURN_FIELD); 36 | 37 | // Parameters 38 | // for now, only solver_type is needed 39 | rhs[out_id] = mxCreateDoubleMatrix(1, 1, mxREAL); 40 | ptr = mxGetPr(rhs[out_id]); 41 | ptr[0] = model_->param.solver_type; 42 | out_id++; 43 | 44 | // nr_class 45 | rhs[out_id] = mxCreateDoubleMatrix(1, 1, mxREAL); 46 | ptr = mxGetPr(rhs[out_id]); 47 | ptr[0] = model_->nr_class; 48 | out_id++; 49 | 50 | if(model_->nr_class==2 && model_->param.solver_type != MCSVM_CS) 51 | nr_w=1; 52 | else 53 | nr_w=model_->nr_class; 54 | 55 | // nr_feature 56 | rhs[out_id] = mxCreateDoubleMatrix(1, 1, mxREAL); 57 | ptr = mxGetPr(rhs[out_id]); 58 | ptr[0] = model_->nr_feature; 59 | out_id++; 60 | 61 | // bias 62 | rhs[out_id] = mxCreateDoubleMatrix(1, 1, mxREAL); 63 | ptr = mxGetPr(rhs[out_id]); 64 | ptr[0] = model_->bias; 65 | out_id++; 66 | 67 | if(model_->bias>=0) 68 | n=model_->nr_feature+1; 69 | else 70 | n=model_->nr_feature; 71 | 72 | w_size = n; 73 | // Label 74 | if(model_->label) 75 | { 76 | rhs[out_id] = mxCreateDoubleMatrix(model_->nr_class, 1, mxREAL); 77 | ptr = mxGetPr(rhs[out_id]); 78 | for(i = 0; i < model_->nr_class; i++) 79 | ptr[i] = model_->label[i]; 80 | } 81 | else 82 | rhs[out_id] = mxCreateDoubleMatrix(0, 0, mxREAL); 83 | out_id++; 84 | 85 | // w 86 | rhs[out_id] = mxCreateDoubleMatrix(nr_w, w_size, mxREAL); 87 | ptr = mxGetPr(rhs[out_id]); 88 | for(i = 0; i < w_size*nr_w; i++) 89 | ptr[i]=model_->w[i]; 90 | out_id++; 91 | 92 | /* Create a struct matrix contains NUM_OF_RETURN_FIELD fields */ 93 | return_model = mxCreateStructMatrix(1, 1, NUM_OF_RETURN_FIELD, field_names); 94 | 95 | /* Fill struct matrix with input arguments */ 96 | for(i = 0; i < NUM_OF_RETURN_FIELD; i++) 97 | mxSetField(return_model,0,field_names[i],mxDuplicateArray(rhs[i])); 98 | /* return */ 99 | plhs[0] = return_model; 100 | mxFree(rhs); 101 | 102 | return NULL; 103 | } 104 | 105 | const char *matlab_matrix_to_model(struct model *model_, const mxArray *matlab_struct) 106 | { 107 | int i, num_of_fields; 108 | int nr_w; 109 | double *ptr; 110 | int id = 0; 111 | int n, w_size; 112 | mxArray **rhs; 113 | 114 | num_of_fields = mxGetNumberOfFields(matlab_struct); 115 | rhs = (mxArray **) mxMalloc(sizeof(mxArray *)*num_of_fields); 116 | 117 | for(i=0;inr_class=0; 121 | nr_w=0; 122 | model_->nr_feature=0; 123 | model_->w=NULL; 124 | model_->label=NULL; 125 | 126 | // Parameters 127 | ptr = mxGetPr(rhs[id]); 128 | model_->param.solver_type = (int)ptr[0]; 129 | id++; 130 | 131 | // nr_class 132 | ptr = mxGetPr(rhs[id]); 133 | model_->nr_class = (int)ptr[0]; 134 | id++; 135 | 136 | if(model_->nr_class==2 && model_->param.solver_type != MCSVM_CS) 137 | nr_w=1; 138 | else 139 | nr_w=model_->nr_class; 140 | 141 | // nr_feature 142 | ptr = mxGetPr(rhs[id]); 143 | model_->nr_feature = (int)ptr[0]; 144 | id++; 145 | 146 | // bias 147 | ptr = mxGetPr(rhs[id]); 148 | model_->bias = ptr[0]; 149 | id++; 150 | 151 | if(model_->bias>=0) 152 | n=model_->nr_feature+1; 153 | else 154 | n=model_->nr_feature; 155 | w_size = n; 156 | 157 | // Label 158 | if(mxIsEmpty(rhs[id]) == 0) 159 | { 160 | model_->label = Malloc(int, model_->nr_class); 161 | ptr = mxGetPr(rhs[id]); 162 | for(i=0;inr_class;i++) 163 | model_->label[i] = (int)ptr[i]; 164 | } 165 | id++; 166 | 167 | ptr = mxGetPr(rhs[id]); 168 | model_->w=Malloc(double, w_size*nr_w); 169 | for(i = 0; i < w_size*nr_w; i++) 170 | model_->w[i]=ptr[i]; 171 | id++; 172 | mxFree(rhs); 173 | 174 | return NULL; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/linear_model_matlab.h: -------------------------------------------------------------------------------- 1 | const char *model_to_matlab_structure(mxArray *plhs[], struct model *model_); 2 | const char *matlab_matrix_to_model(struct model *model_, const mxArray *matlab_struct); 3 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/matlab/make.m: -------------------------------------------------------------------------------- 1 | % This make.m is for MATLAB and OCTAVE under Windows, Mac, and Unix 2 | 3 | try 4 | Type = ver; 5 | % This part is for OCTAVE 6 | if(strcmp(Type(1).Name, 'Octave') == 1) 7 | mex libsvmread.c 8 | mex libsvmwrite.c 9 | mex train.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/daxpy.c ../blas/ddot.c ../blas/dnrm2.c ../blas/dscal.c 10 | mex predict.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/daxpy.c ../blas/ddot.c ../blas/dnrm2.c ../blas/dscal.c 11 | % This part is for MATLAB 12 | % Add -largeArrayDims on 64-bit machines of MATLAB 13 | else 14 | mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims libsvmread.c 15 | mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims libsvmwrite.c 16 | mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims train.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/daxpy.c ../blas/ddot.c ../blas/dnrm2.c ../blas/dscal.c -output liblinear_train 17 | mex CFLAGS="\$CFLAGS -std=c99" -largeArrayDims predict.c linear_model_matlab.c ../linear.cpp ../tron.cpp ../blas/daxpy.c ../blas/ddot.c ../blas/dnrm2.c ../blas/dscal.c -output liblinear_predict 18 | end 19 | catch 20 | fprintf('If make.m fails, please check README about detailed instructions.\n'); 21 | end 22 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/predict.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "linear.h" 7 | 8 | int print_null(const char *s,...) {return 0;} 9 | 10 | static int (*info)(const char *fmt,...) = &printf; 11 | 12 | struct feature_node *x; 13 | int max_nr_attr = 64; 14 | 15 | struct model* model_; 16 | int flag_predict_probability=0; 17 | 18 | void exit_input_error(int line_num) 19 | { 20 | fprintf(stderr,"Wrong input format at line %d\n", line_num); 21 | exit(1); 22 | } 23 | 24 | static char *line = NULL; 25 | static int max_line_len; 26 | 27 | static char* readline(FILE *input) 28 | { 29 | int len; 30 | 31 | if(fgets(line,max_line_len,input) == NULL) 32 | return NULL; 33 | 34 | while(strrchr(line,'\n') == NULL) 35 | { 36 | max_line_len *= 2; 37 | line = (char *) realloc(line,max_line_len); 38 | len = (int) strlen(line); 39 | if(fgets(line+len,max_line_len-len,input) == NULL) 40 | break; 41 | } 42 | return line; 43 | } 44 | 45 | void do_predict(FILE *input, FILE *output) 46 | { 47 | int correct = 0; 48 | int total = 0; 49 | double error = 0; 50 | double sump = 0, sumt = 0, sumpp = 0, sumtt = 0, sumpt = 0; 51 | 52 | int nr_class=get_nr_class(model_); 53 | double *prob_estimates=NULL; 54 | int j, n; 55 | int nr_feature=get_nr_feature(model_); 56 | if(model_->bias>=0) 57 | n=nr_feature+1; 58 | else 59 | n=nr_feature; 60 | 61 | if(flag_predict_probability) 62 | { 63 | int *labels; 64 | 65 | if(!check_probability_model(model_)) 66 | { 67 | fprintf(stderr, "probability output is only supported for logistic regression\n"); 68 | exit(1); 69 | } 70 | 71 | labels=(int *) malloc(nr_class*sizeof(int)); 72 | get_labels(model_,labels); 73 | prob_estimates = (double *) malloc(nr_class*sizeof(double)); 74 | fprintf(output,"labels"); 75 | for(j=0;j=max_nr_attr-2) // need one more for index = -1 101 | { 102 | max_nr_attr *= 2; 103 | x = (struct feature_node *) realloc(x,max_nr_attr*sizeof(struct feature_node)); 104 | } 105 | 106 | idx = strtok(NULL,":"); 107 | val = strtok(NULL," \t"); 108 | 109 | if(val == NULL) 110 | break; 111 | errno = 0; 112 | x[i].index = (int) strtol(idx,&endptr,10); 113 | if(endptr == idx || errno != 0 || *endptr != '\0' || x[i].index <= inst_max_index) 114 | exit_input_error(total+1); 115 | else 116 | inst_max_index = x[i].index; 117 | 118 | errno = 0; 119 | x[i].value = strtod(val,&endptr); 120 | if(endptr == val || errno != 0 || (*endptr != '\0' && !isspace(*endptr))) 121 | exit_input_error(total+1); 122 | 123 | // feature indices larger than those in training are not used 124 | if(x[i].index <= nr_feature) 125 | ++i; 126 | } 127 | 128 | if(model_->bias>=0) 129 | { 130 | x[i].index = n; 131 | x[i].value = model_->bias; 132 | i++; 133 | } 134 | x[i].index = -1; 135 | 136 | if(flag_predict_probability) 137 | { 138 | int j; 139 | predict_label = predict_probability(model_,x,prob_estimates); 140 | fprintf(output,"%g",predict_label); 141 | for(j=0;jnr_class;j++) 142 | fprintf(output," %g",prob_estimates[j]); 143 | fprintf(output,"\n"); 144 | } 145 | else 146 | { 147 | predict_label = predict(model_,x); 148 | fprintf(output,"%g\n",predict_label); 149 | } 150 | 151 | if(predict_label == target_label) 152 | ++correct; 153 | error += (predict_label-target_label)*(predict_label-target_label); 154 | sump += predict_label; 155 | sumt += target_label; 156 | sumpp += predict_label*predict_label; 157 | sumtt += target_label*target_label; 158 | sumpt += predict_label*target_label; 159 | ++total; 160 | } 161 | if(check_regression_model(model_)) 162 | { 163 | info("Mean squared error = %g (regression)\n",error/total); 164 | info("Squared correlation coefficient = %g (regression)\n", 165 | ((total*sumpt-sump*sumt)*(total*sumpt-sump*sumt))/ 166 | ((total*sumpp-sump*sump)*(total*sumtt-sumt*sumt)) 167 | ); 168 | } 169 | else 170 | info("Accuracy = %g%% (%d/%d)\n",(double) correct/total*100,correct,total); 171 | if(flag_predict_probability) 172 | free(prob_estimates); 173 | } 174 | 175 | void exit_with_help() 176 | { 177 | printf( 178 | "Usage: predict [options] test_file model_file output_file\n" 179 | "options:\n" 180 | "-b probability_estimates: whether to output probability estimates, 0 or 1 (default 0); currently for logistic regression only\n" 181 | "-q : quiet mode (no outputs)\n" 182 | ); 183 | exit(1); 184 | } 185 | 186 | int main(int argc, char **argv) 187 | { 188 | FILE *input, *output; 189 | int i; 190 | 191 | // parse options 192 | for(i=1;i=argc) 212 | exit_with_help(); 213 | 214 | input = fopen(argv[i],"r"); 215 | if(input == NULL) 216 | { 217 | fprintf(stderr,"can't open input file %s\n",argv[i]); 218 | exit(1); 219 | } 220 | 221 | output = fopen(argv[i+2],"w"); 222 | if(output == NULL) 223 | { 224 | fprintf(stderr,"can't open output file %s\n",argv[i+2]); 225 | exit(1); 226 | } 227 | 228 | if((model_=load_model(argv[i+1]))==0) 229 | { 230 | fprintf(stderr,"can't open model file %s\n",argv[i+1]); 231 | exit(1); 232 | } 233 | 234 | x = (struct feature_node *) malloc(max_nr_attr*sizeof(struct feature_node)); 235 | do_predict(input, output); 236 | free_and_destroy_model(&model_); 237 | free(line); 238 | free(x); 239 | fclose(input); 240 | fclose(output); 241 | return 0; 242 | } 243 | 244 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/python/Makefile: -------------------------------------------------------------------------------- 1 | all = lib 2 | 3 | lib: 4 | make -C .. lib 5 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/tron.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "tron.h" 6 | 7 | #ifndef min 8 | template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } 13 | #endif 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | extern double dnrm2_(int *, double *, int *); 20 | extern double ddot_(int *, double *, int *, double *, int *); 21 | extern int daxpy_(int *, double *, double *, int *, double *, int *); 22 | extern int dscal_(int *, double *, double *, int *); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | static void default_print(const char *buf) 29 | { 30 | fputs(buf,stdout); 31 | fflush(stdout); 32 | } 33 | 34 | void TRON::info(const char *fmt,...) 35 | { 36 | char buf[BUFSIZ]; 37 | va_list ap; 38 | va_start(ap,fmt); 39 | vsprintf(buf,fmt,ap); 40 | va_end(ap); 41 | (*tron_print_string)(buf); 42 | } 43 | 44 | TRON::TRON(const function *fun_obj, double eps, int max_iter) 45 | { 46 | this->fun_obj=const_cast(fun_obj); 47 | this->eps=eps; 48 | this->max_iter=max_iter; 49 | tron_print_string = default_print; 50 | } 51 | 52 | TRON::~TRON() 53 | { 54 | } 55 | 56 | void TRON::tron(double *w) 57 | { 58 | // Parameters for updating the iterates. 59 | double eta0 = 1e-4, eta1 = 0.25, eta2 = 0.75; 60 | 61 | // Parameters for updating the trust region size delta. 62 | double sigma1 = 0.25, sigma2 = 0.5, sigma3 = 4; 63 | 64 | int n = fun_obj->get_nr_variable(); 65 | int i, cg_iter; 66 | double delta, snorm, one=1.0; 67 | double alpha, f, fnew, prered, actred, gs; 68 | int search = 1, iter = 1, inc = 1; 69 | double *s = new double[n]; 70 | double *r = new double[n]; 71 | double *w_new = new double[n]; 72 | double *g = new double[n]; 73 | 74 | for (i=0; ifun(w); 78 | fun_obj->grad(w, g); 79 | delta = dnrm2_(&n, g, &inc); 80 | double gnorm1 = delta; 81 | double gnorm = gnorm1; 82 | 83 | if (gnorm <= eps*gnorm1) 84 | search = 0; 85 | 86 | iter = 1; 87 | 88 | while (iter <= max_iter && search) 89 | { 90 | cg_iter = trcg(delta, g, s, r); 91 | 92 | memcpy(w_new, w, sizeof(double)*n); 93 | daxpy_(&n, &one, s, &inc, w_new, &inc); 94 | 95 | gs = ddot_(&n, g, &inc, s, &inc); 96 | prered = -0.5*(gs-ddot_(&n, s, &inc, r, &inc)); 97 | fnew = fun_obj->fun(w_new); 98 | 99 | // Compute the actual reduction. 100 | actred = f - fnew; 101 | 102 | // On the first iteration, adjust the initial step bound. 103 | snorm = dnrm2_(&n, s, &inc); 104 | if (iter == 1) 105 | delta = min(delta, snorm); 106 | 107 | // Compute prediction alpha*snorm of the step. 108 | if (fnew - f - gs <= 0) 109 | alpha = sigma3; 110 | else 111 | alpha = max(sigma1, -0.5*(gs/(fnew - f - gs))); 112 | 113 | // Update the trust region bound according to the ratio of actual to predicted reduction. 114 | if (actred < eta0*prered) 115 | delta = min(max(alpha, sigma1)*snorm, sigma2*delta); 116 | else if (actred < eta1*prered) 117 | delta = max(sigma1*delta, min(alpha*snorm, sigma2*delta)); 118 | else if (actred < eta2*prered) 119 | delta = max(sigma1*delta, min(alpha*snorm, sigma3*delta)); 120 | else 121 | delta = max(delta, min(alpha*snorm, sigma3*delta)); 122 | 123 | info("iter %2d act %5.3e pre %5.3e delta %5.3e f %5.3e |g| %5.3e CG %3d\n", iter, actred, prered, delta, f, gnorm, cg_iter); 124 | 125 | if (actred > eta0*prered) 126 | { 127 | iter++; 128 | memcpy(w, w_new, sizeof(double)*n); 129 | f = fnew; 130 | fun_obj->grad(w, g); 131 | 132 | gnorm = dnrm2_(&n, g, &inc); 133 | if (gnorm <= eps*gnorm1) 134 | break; 135 | } 136 | if (f < -1.0e+32) 137 | { 138 | info("WARNING: f < -1.0e+32\n"); 139 | break; 140 | } 141 | if (fabs(actred) <= 0 && prered <= 0) 142 | { 143 | info("WARNING: actred and prered <= 0\n"); 144 | break; 145 | } 146 | if (fabs(actred) <= 1.0e-12*fabs(f) && 147 | fabs(prered) <= 1.0e-12*fabs(f)) 148 | { 149 | info("WARNING: actred and prered too small\n"); 150 | break; 151 | } 152 | } 153 | 154 | delete[] g; 155 | delete[] r; 156 | delete[] w_new; 157 | delete[] s; 158 | } 159 | 160 | int TRON::trcg(double delta, double *g, double *s, double *r) 161 | { 162 | int i, inc = 1; 163 | int n = fun_obj->get_nr_variable(); 164 | double one = 1; 165 | double *d = new double[n]; 166 | double *Hd = new double[n]; 167 | double rTr, rnewTrnew, alpha, beta, cgtol; 168 | 169 | for (i=0; iHv(d, Hd); 185 | 186 | alpha = rTr/ddot_(&n, d, &inc, Hd, &inc); 187 | daxpy_(&n, &alpha, d, &inc, s, &inc); 188 | if (dnrm2_(&n, s, &inc) > delta) 189 | { 190 | info("cg reaches trust region boundary\n"); 191 | alpha = -alpha; 192 | daxpy_(&n, &alpha, d, &inc, s, &inc); 193 | 194 | double std = ddot_(&n, s, &inc, d, &inc); 195 | double sts = ddot_(&n, s, &inc, s, &inc); 196 | double dtd = ddot_(&n, d, &inc, d, &inc); 197 | double dsq = delta*delta; 198 | double rad = sqrt(std*std + dtd*(dsq-sts)); 199 | if (std >= 0) 200 | alpha = (dsq - sts)/(std + rad); 201 | else 202 | alpha = (rad - std)/dtd; 203 | daxpy_(&n, &alpha, d, &inc, s, &inc); 204 | alpha = -alpha; 205 | daxpy_(&n, &alpha, Hd, &inc, r, &inc); 206 | break; 207 | } 208 | alpha = -alpha; 209 | daxpy_(&n, &alpha, Hd, &inc, r, &inc); 210 | rnewTrnew = ddot_(&n, r, &inc, r, &inc); 211 | beta = rnewTrnew/rTr; 212 | dscal_(&n, &beta, d, &inc); 213 | daxpy_(&n, &one, r, &inc, d, &inc); 214 | rTr = rnewTrnew; 215 | } 216 | 217 | delete[] d; 218 | delete[] Hd; 219 | 220 | return(cg_iter); 221 | } 222 | 223 | double TRON::norm_inf(int n, double *x) 224 | { 225 | double dmax = fabs(x[0]); 226 | for (int i=1; i= dmax) 228 | dmax = fabs(x[i]); 229 | return(dmax); 230 | } 231 | 232 | void TRON::set_print_string(void (*print_string) (const char *buf)) 233 | { 234 | tron_print_string = print_string; 235 | } 236 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/tron.h: -------------------------------------------------------------------------------- 1 | #ifndef _TRON_H 2 | #define _TRON_H 3 | 4 | class function 5 | { 6 | public: 7 | virtual double fun(double *w) = 0 ; 8 | virtual void grad(double *w, double *g) = 0 ; 9 | virtual void Hv(double *s, double *Hs) = 0 ; 10 | 11 | virtual int get_nr_variable(void) = 0 ; 12 | virtual ~function(void){} 13 | }; 14 | 15 | class TRON 16 | { 17 | public: 18 | TRON(const function *fun_obj, double eps = 0.1, int max_iter = 1000); 19 | ~TRON(); 20 | 21 | void tron(double *w); 22 | void set_print_string(void (*i_print) (const char *buf)); 23 | 24 | private: 25 | int trcg(double delta, double *g, double *s, double *r); 26 | double norm_inf(int n, double *x); 27 | 28 | double eps; 29 | int max_iter; 30 | function *fun_obj; 31 | void info(const char *fmt,...); 32 | void (*tron_print_string)(const char *buf); 33 | }; 34 | #endif 35 | -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/liblinear.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/liblinear.dll -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/libsvmread.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/libsvmread.mexw64 -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/libsvmwrite.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/libsvmwrite.mexw64 -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/predict.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/predict.exe -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/predict.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/predict.mexw64 -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/train.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/train.exe -------------------------------------------------------------------------------- /dependencies/liblinear-1.96/windows/train.mexw64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/suhangpro/mvcnn/99ba97b7cc1044f3473d6b7b3e420fe44765e55a/dependencies/liblinear-1.96/windows/train.mexw64 -------------------------------------------------------------------------------- /evalkit/Evaluator.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var Metrics = require('./Metrics.js'); 3 | 4 | function Evaluator(params) { 5 | this.truth = {}; // id -> {synset, subsynset} 6 | this.bySynset = {}; // synset -> [id] 7 | this.bySubSynset = {}; // subsynset -> [id] 8 | this.outDir = params.outDir || 'out/'; 9 | var scope = this; 10 | fs.readFileSync(params.truthFile).toString().split('\n').forEach(function(line) { 11 | var tokens = line.split(','); 12 | var id = tokens[0]; 13 | if (id === 'id') { return; } // this is the header line so skip it 14 | var synset = tokens[1]; 15 | var subsynset = tokens[2]; 16 | scope.truth[id] = {synset: synset, subsynset: subsynset}; 17 | if (!scope.bySynset[synset]) scope.bySynset[synset] = []; 18 | scope.bySynset[synset].push(id); 19 | if (!scope.bySubSynset[subsynset]) scope.bySubSynset[subsynset] = []; 20 | scope.bySubSynset[subsynset].push(id); 21 | }); 22 | } 23 | Evaluator.prototype.constructor = Evaluator; 24 | 25 | Evaluator.prototype.resultsToScores = function(queryId, results) { 26 | var query = this.truth[queryId]; 27 | var scores = []; 28 | for (var i = 0; i < results.length; i++) { 29 | var result = this.truth[results[i]]; 30 | if (query.synset !== result.synset) { // parent synset mismatch 31 | scores[i] = 0; 32 | } else { // parent synset match, check subsynset 33 | if (query.subsynset === result.subsynset) { // subsynset match 34 | scores[i] = 3; 35 | } else if (query.synset === result.subsynset) { // predicted subsynset is query synset 36 | scores[i] = 2; 37 | } else { // must be a sibling subsynset since match 38 | scores[i] = 1; 39 | } 40 | } 41 | } 42 | //console.log(queryId + ":" + scores); 43 | return scores; 44 | }; 45 | 46 | // Reads all query results from given dir 47 | var readQueryResults = function(dir) { 48 | var files = fs.readdirSync(dir); 49 | var allQueryResults = {}; 50 | 51 | // helper converts line in results file and pushes to results 52 | var lineToQueryResult = function(results, line) { 53 | if (line) { 54 | var tokens = line.split(' '); 55 | var id = tokens[0]; 56 | //var dist = tokens[1]; // ignore dists for now 57 | results.push(id); 58 | } 59 | }; 60 | 61 | // iterate over files and convert each to results list 62 | for (var iF = 0; iF < files.length; iF++) { 63 | var file = files[iF]; 64 | var results = []; 65 | var line2result = lineToQueryResult.bind(undefined, results); 66 | fs.readFileSync(dir + file).toString().split('\n').forEach(line2result); 67 | allQueryResults[file] = results; 68 | } 69 | 70 | return allQueryResults; 71 | }; 72 | 73 | // save average Precision-Recall values for each id in avgs to filename 74 | var savePRs = function(avgs, filename) { 75 | var f = fs.createWriteStream(filename); 76 | f.on('error', function(err) { console.error(err); }); 77 | f.write("class,P,R\n"); 78 | for (var id in avgs) { 79 | if (avgs.hasOwnProperty(id)) { 80 | var a = avgs[id].getAverages(); 81 | var P = a.P; var R = a.R; 82 | for (var k = 0; k < P.length; k++) { 83 | var l = id + "," + a.P[k] + "," + a.R[k] + "\n"; 84 | f.write(l); 85 | } 86 | } 87 | } 88 | f.end(); 89 | }; 90 | 91 | // save average metrics for each id in avgs 92 | var saveAverages = function(avgs, filename) { 93 | var f = fs.createWriteStream(filename); 94 | f.on('error', function(err) { console.error(err); }); 95 | var header = "class,P@N,R@N,F1@N,mAP,NDCG"; 96 | console.log(header); f.write(header + '\n'); 97 | for (var id in avgs) { 98 | if (avgs.hasOwnProperty(id)) { 99 | var a = avgs[id].getAverages(); 100 | var l = [id, a['P@N'], a['R@N'], a['F1@N'], a.mAP, a.NDCG].join(','); 101 | console.log(l); f.write(l + '\n'); 102 | } 103 | } 104 | f.end(); 105 | }; 106 | 107 | Evaluator.prototype.evaluate = function(dir) { 108 | var queries = readQueryResults(dir); 109 | var cutoff = 1000; // only consider recall up to this retrieval list length 110 | var metrics = {'microALL': new Metrics.SummedMetrics()}; 111 | for (var queryId in queries) { 112 | if (!queries.hasOwnProperty(queryId)) { continue; } 113 | var queryTruth = this.truth[queryId]; 114 | var querySynset = queryTruth.synset; 115 | var results = queries[queryId]; 116 | if (results.length > cutoff) { // only accept up to cutoff results 117 | results = results.slice(0, cutoff); 118 | } 119 | var maxPossibleTrue = Math.min(this.bySynset[querySynset].length, cutoff); 120 | var scores = this.resultsToScores(queryId, results); 121 | metrics.microALL.addResult(scores, maxPossibleTrue); 122 | if (!metrics[querySynset]) { metrics[querySynset] = new Metrics.SummedMetrics(); } 123 | metrics[querySynset].addResult(scores, maxPossibleTrue); 124 | } 125 | 126 | // Computer macro average = average of per-synset micro averages 127 | metrics.macroALL = new Metrics.SummedMetrics(); 128 | for (var synId in metrics) { 129 | if (metrics.hasOwnProperty(synId) && synId !== 'microALL') { 130 | metrics.macroALL.addSummedMetrics(metrics[synId]); 131 | } 132 | } 133 | 134 | saveAverages(metrics, 'summary.csv'); 135 | savePRs(metrics, 'PR.csv'); 136 | }; 137 | 138 | // Writes oracle query results to given dir, cutting off lists at maxN 139 | Evaluator.prototype.writeOracleResults = function(dir, maxN) { 140 | // for each id in truth table 141 | for (var modelId in this.truth) { 142 | if (!this.truth.hasOwnProperty(modelId) || modelId === '') { continue; } 143 | var model = this.truth[modelId]; 144 | var synsetId = model.synset; 145 | // get all ids with same synset 146 | var sameSynsetModelIds = this.bySynset[synsetId]; 147 | if (sameSynsetModelIds.length > maxN) { 148 | sameSynsetModelIds = sameSynsetModelIds.slice(0, maxN); 149 | } 150 | var file = dir + '/' + modelId; 151 | fs.writeFileSync(file, sameSynsetModelIds.join('\n')); 152 | } 153 | }; 154 | 155 | module.exports = Evaluator; -------------------------------------------------------------------------------- /evalkit/Metrics.js: -------------------------------------------------------------------------------- 1 | // Discounted Cumulative Gain 2 | var DCG = function(x_, k_) { 3 | var k = k_ || x_.length; 4 | var x = k_ ? x_.slice(0, k) : x_; 5 | var sum = x[0]; 6 | for (var i = 1; i < x.length; i++) { 7 | var w = Math.log(2) / Math.log(i + 1); 8 | sum = sum + (w * x[i]); 9 | } 10 | return sum; 11 | }; 12 | 13 | // Ideal Discounted Cumulative Gain 14 | var IDCG = function(x_, k_) { 15 | var k = k_ || x_.length; 16 | var x = x_.slice(0, k); //NOTE: copy for sort mutation 17 | x = x.sort().reverse(); 18 | return DCG(x); 19 | }; 20 | 21 | // Normalized Discounted Cumulative Gain at 22 | var NDCG = function(x, k) { 23 | return DCG(x, k) / IDCG(x, k); 24 | }; 25 | 26 | // Precisions at k=i for i=1 to x.length 27 | var PrecisionVec = function(x) { 28 | var sum = 0; 29 | var P = []; 30 | for (var i = 0; i < x.length; i++) { 31 | if (x[i]) { sum++; } 32 | P[i] = sum / (i + 1); 33 | } 34 | return P; 35 | }; 36 | 37 | // Precision at k 38 | var Precision = function(x, k_) { 39 | var k = k_ || x.length - 1; 40 | return PrecisionVec(x)[k]; 41 | }; 42 | 43 | // Recalls at k=i for i=1 to x.length 44 | var RecallVec = function(x, cutoff) { 45 | var sum = 0; 46 | var R = []; 47 | for (var i = 0; i < x.length; i++) { 48 | if (x[i]) { sum++; } 49 | R[i] = sum / cutoff; 50 | } 51 | return R; 52 | }; 53 | 54 | // Recall at k_ 55 | var Recall = function(x, cutoff, k_) { 56 | var k = k_ || x.length - 1; 57 | return RecallVec(x, cutoff)[k]; 58 | }; 59 | 60 | // F1 score given precisions Pv and recalls Rv 61 | var F1Vec = function(Pv, Rv) { 62 | var f1 = []; 63 | for (var i = 0; i < Pv.length; i++) { 64 | f1[i] = 2 * Pv[i] * Rv[i] / (Pv[i] + Rv[i]); 65 | } 66 | return f1; 67 | }; 68 | 69 | // F1 score at k_ 70 | var F1 = function(x, cutoff, k_) { 71 | var k = k_ || x.length - 1; 72 | var Pv = PrecisionVec(x); 73 | var Rv = RecallVec(x, cutoff); 74 | return F1Vec(Pv, Rv)[k]; 75 | }; 76 | 77 | // Average precision over x 78 | var AveragePrecision = function(x) { 79 | var sum = 0; 80 | var precisions = []; 81 | for (var i = 0; i < x.length; i++) { 82 | if (x[i]) { 83 | sum++; 84 | precisions.push(sum / (i + 1)); 85 | } 86 | } 87 | var sumPrecisions = 0; 88 | for (var j = 0; j < precisions.length; j++) { 89 | sumPrecisions += precisions[j]; 90 | } 91 | if (precisions.length > 0) { 92 | return sumPrecisions /precisions.length; 93 | } else { 94 | return 0; 95 | } 96 | }; 97 | 98 | var MetricsSet = function(x, cutoff) { 99 | this.p = PrecisionVec(x); 100 | this.r = RecallVec(x, cutoff); 101 | this.f1 = F1Vec(this.p, this.r); 102 | this.ap = AveragePrecision(x); 103 | this.ndcg = NDCG(x); 104 | this.num = 1; 105 | }; 106 | 107 | // Collection of retrieval performance summary stats 108 | var SummedMetrics = function() { 109 | this.pSum = []; 110 | this.rSum = []; 111 | this.f1Sum = []; 112 | this.pNSum = 0; 113 | this.rNSum = 0; 114 | this.f1NSum = 0; 115 | this.apSum = 0; 116 | this.ndcgSum = 0; 117 | this.numSum = 0; 118 | }; 119 | 120 | var addVec = function(x, y) { 121 | var o = []; 122 | for (var i = 0; i < Math.max(x.length, y.length); i++) { 123 | o[i] = (x[i] ? x[i] : 0) + (y[i] ? y[i] : 0); 124 | } 125 | return o; 126 | }; 127 | 128 | SummedMetrics.prototype.addResult = function(x, cutoff) { 129 | var m = new MetricsSet(x, cutoff); 130 | this.addMetricsSet(m); 131 | }; 132 | 133 | SummedMetrics.prototype.addMetricsSet = function(m) { 134 | this.pSum = addVec(this.pSum, m.p); 135 | this.rSum = addVec(this.rSum, m.r); 136 | this.f1Sum = addVec(this.f1Sum, m.f1); 137 | this.pNSum += m.p[m.p.length-1]; 138 | this.rNSum += m.r[m.r.length-1]; 139 | this.f1NSum += m.f1[m.f1.length-1]; 140 | this.apSum += m.ap; 141 | this.ndcgSum += m.ndcg; 142 | this.numSum += m.num; 143 | }; 144 | 145 | var mulVec = function(x, m) { 146 | var o = []; 147 | for (var i = 0; i < x.length; i++) { 148 | o[i] = x[i] * m; 149 | } 150 | return o; 151 | }; 152 | 153 | SummedMetrics.prototype.addSummedMetrics = function(s) { 154 | var avg = s.getAverages(); 155 | this.pSum = addVec(this.pSum, avg.P); 156 | this.rSum = addVec(this.rSum, avg.R); 157 | this.f1Sum = addVec(this.f1Sum, avg.F1); 158 | this.pNSum += avg['P@N']; 159 | this.rNSum += avg['R@N']; 160 | this.f1NSum += avg['F1@N']; 161 | this.apSum += avg.mAP; 162 | this.ndcgSum += avg.NDCG; 163 | this.numSum++; // add one for macro averaging since s is already summed 164 | }; 165 | 166 | SummedMetrics.prototype.getAverages = function() { 167 | var norm = 1 / this.numSum; 168 | var p = mulVec(this.pSum, norm); 169 | var r = mulVec(this.rSum, norm); 170 | var f1 = mulVec(this.f1Sum, norm); 171 | var pn = this.pNSum * norm; 172 | var rn = this.rNSum * norm; 173 | var f1n = this.f1NSum * norm; 174 | var ap = this.apSum * norm; 175 | var ndcg = this.ndcgSum * norm; 176 | return { 177 | 'P': p, 178 | 'R': r, 179 | 'F1': f1, 180 | 'P@N': pn, 181 | 'R@N': rn, 182 | 'F1@N': f1n, 183 | 'mAP': ap, 184 | 'NDCG': ndcg, 185 | 'num': this.numSum 186 | }; 187 | }; 188 | 189 | module.exports = { 190 | DCG: DCG, 191 | IDCG: IDCG, 192 | NDCG: NDCG, 193 | Precision: Precision, 194 | Recall: Recall, 195 | PrecisionVec: PrecisionVec, 196 | RecallVec: RecallVec, 197 | F1Vec: F1Vec, 198 | F1: F1, 199 | AveragePrecision: AveragePrecision, 200 | SummedMetrics: SummedMetrics 201 | }; 202 | -------------------------------------------------------------------------------- /evalkit/README.txt: -------------------------------------------------------------------------------- 1 | In order to run this evaluation code you must have node.js installed on your system (https://nodejs.org/en/). 2 | 3 | Then create an evaluate.js file with the following contents: 4 | var Evaluator = require('./Evaluator.js'); 5 | var evaluator = new Evaluator({truthFile: 'path/to/val.csv'}); 6 | evaluator.evaluate('path/to/dir/with/results/'); 7 | 8 | The 'path/to/val.csv' refers to the category ground truth file (can also be train.csv), while 'path/to/dir/with/results/' refers to the directory containing all ranked list result files. The code will print out a csv format set of evaluation metric statistics for each category, as well as a micro-average and macro-average across all categories. The same information is saved in a 'summary.csv' file in the working directory. In addition, a 'PR.csv' file is saved with precision-recall values that can be used to generate a Precision-Recall plot. 9 | 10 | Please contact the organizers at shrec2016shapenet@gmail.com if you have any questions or issues. 11 | 12 | UPDATES: 13 | 2016-03-01 - Fixed aggregation bug for P@N, R@N, and F1@N when N (retrieval list length) varies. Also added writeOracleResults function to Evaluator -------------------------------------------------------------------------------- /evalkit/evaluate.js: -------------------------------------------------------------------------------- 1 | var Evaluator = require('./Evaluator.js'); 2 | var evaluator = new Evaluator({truthFile: 'val.csv'}); 3 | evaluator.evaluate('path/to/dir/with/results/'); 4 | -------------------------------------------------------------------------------- /exp_scripts/confmat.m: -------------------------------------------------------------------------------- 1 | addpath(genpath('/scratch1/Hang/Dropbox/tools/piotr_toolbox/toolbox')); 2 | load('~/Desktop/pred.mat'); 3 | imdb = get_imdb('modelnet40toon'); 4 | inds = find(imdb.images.set==3);inds = inds(1:12:end);gt=imdb.images.class(inds)'; 5 | CM = confMatrix(gt,predTest,40); 6 | confMatrixShow(CM,strrep(imdb.meta.classes,'_',' '),{'FontSize',10}); 7 | -------------------------------------------------------------------------------- /exp_scripts/display_retrieval_results.m: -------------------------------------------------------------------------------- 1 | function display_retrieval_results() 2 | 3 | % r.rankings 4 | % r.dists 5 | % r.dists0 6 | % info 7 | % imdb 8 | if ~exist('r','var'), load('data/eval.mat'); end; 9 | 10 | % sort imdb.images wrt id 11 | [imdb.images.id, I] = sort(imdb.images.id); 12 | imdb.images.name = imdb.images.name(I); 13 | imdb.images.class = imdb.images.class(I); 14 | imdb.images.set = imdb.images.set(I); 15 | if isfield(imdb.images,'sid'), imdb.images.sid = imdb.images.sid(I); end 16 | 17 | % sort imdb.images wrt sid 18 | if isfield(imdb.images,'sid'), 19 | [imdb.images.sid, I] = sort(imdb.images.sid); 20 | imdb.images.name = imdb.images.name(I); 21 | imdb.images.class = imdb.images.class(I); 22 | imdb.images.set = imdb.images.set(I); 23 | imdb.images.id = imdb.images.id(I); 24 | end 25 | 26 | nViews = 6; 27 | nRefViews = 12; 28 | sketchDir = 'data/sketch160r6'; 29 | zedgeDir = 'data/modelnet40zedge'; 30 | toonDir = 'data/modelnet40toon'; 31 | viewDir = 'data/modelnet40view'; 32 | saveDir = 'data/retrieval-results';vl_xmkdir(saveDir); 33 | queryView = 2; 34 | dispViews = [1];% [1 5 9]; 35 | nTops = 10; 36 | border = 0.06; 37 | 38 | [nQuery, nRef] = size(r.dists); 39 | [~,order] = sort(r.dists,2,'ascend'); 40 | dists0 = reshape(r.dists0,[nViews nQuery*nRefViews*nRef]); 41 | [~,dispViewD] = min(reshape(0.5*(mean(dists0)+min(dists0)),[nQuery nRefViews nRef]),[],2); 42 | dispViewD = squeeze(dispViewD); 43 | 44 | N_W = 1 + nTops; 45 | N_H = 10; %5; %2; % numel(dispViews); 46 | 47 | q_sid = imdb.images.sid(imdb.images.set==4); 48 | q_sid = q_sid(1:nViews:end); 49 | q_class = imdb.images.class(imdb.images.set==4); 50 | q_class = q_class(1:nViews:end); 51 | 52 | figure; 53 | 54 | [~,I] = sort(info.ap,'descend'); 55 | % I = randperm(length(info.ap)); 56 | 57 | I = [18 28 47 71 95 152 170 176 246 115]'; 58 | classes = [1 2 3 5 7 8 9 11 12 13]; 59 | 60 | % plotted = []; 61 | k=1; 62 | for i=I(:)', 63 | % k = numel(plotted)+1; 64 | gt = q_class(i); 65 | % if ismember(gt,plotted), continue; end; 66 | % plotted(end+1) = gt; 67 | idx = find(imdb.images.sid==q_sid(i)); 68 | q_im = imread(fullfile(sketchDir,imdb.images.name{idx(queryView)})); 69 | q_im = 255 - imdilate(255-q_im,strel('disk',3)); 70 | vl_tightsubplot(N_H,N_W,N_W*(k-1)+1); 71 | imshow(q_im); 72 | %{ 73 | vl_tightsubplot(N_H,N_W,N_W+1); 74 | plot(info.recall(i,:),info.precision(i,:)); 75 | axis square on; 76 | grid on; 77 | %} 78 | % [~,v] = max(histc(dispViewD(i,order(i,1:nTops)),1:12)); 79 | skipped = -1; 80 | for j=1:nTops, 81 | c = -1; 82 | while ~ismember(c,classes), 83 | skipped = skipped + 1; 84 | v = dispViewD(i,order(i,j+skipped)); 85 | idx = find(imdb.images.sid==r.rankings(i,j+skipped)); 86 | c = imdb.images.class(idx(v)); 87 | end 88 | % for v=1:numel(dispViews), 89 | % r_im = imread(fullfile(zedgeDir,imdb.images.name{idx(v)})); r_im = 255 - imdilate(255-r_im,strel('disk',3)); 90 | r_im = imread(fullfile(viewDir,strrep(imdb.images.name{idx(v)},'zedge','view'))); % r_im = imfilter(r_im,fspecial('gaussian',[5 5],2)); 91 | if gt==c, 92 | % r_im = highlight_im(r_im,[0 1 0],border); 93 | else 94 | r_im = highlight_im(r_im,[1 0 0],border); 95 | end 96 | vl_tightsubplot(N_H,N_W,N_W*(k-1)+1+j); 97 | imshow(r_im); 98 | % end 99 | end 100 | % print(fullfile(saveDir,[num2str(i) '.jpg']),'-djpeg'); 101 | k = k+1;% if numel(plotted)==N_H, break; end 102 | % waitforbuttonpress; 103 | end 104 | 105 | end 106 | 107 | function im = highlight_im(im0,c,w) 108 | if isinteger(im0), im0 = double(im0)/255; end 109 | if isinteger(c), c = double(c)/255; end 110 | if size(im0,3)==1, im0 = repmat(im0,[1 1 3]); end 111 | sz = size(im0); 112 | if w<1, w = floor(mean(sz(1:2))*w); end 113 | im = bsxfun(@times,ones(size(im0)),reshape(c,[1 1 3])); 114 | im(w+1:end-w,w+1:end-w,:) = im0(w+1:end-w,w+1:end-w,:); 115 | end 116 | -------------------------------------------------------------------------------- /exp_scripts/display_right_wrong.m: -------------------------------------------------------------------------------- 1 | function display_right_wrong(decTest,imdb) 2 | rng(1); 3 | [imdb.images.id,I] = sort(imdb.images.id); 4 | imdb.images.class = imdb.images.class(I); 5 | imdb.images.name = imdb.images.name(I); 6 | imdb.images.set = imdb.images.set(I); 7 | % Cs = imdb.meta.classes; 8 | Cs = { 9 | 'pen' 10 | 'crab' 11 | 'snail' 12 | 'squirrel' 13 | 'guitar' 14 | 'butterfly' 15 | 'church' 16 | 'flying bird' 17 | 'laptop' 18 | 'strawberry' 19 | 'knife' 20 | 'bicycle' 21 | 'grapes', 22 | 'hammer' 23 | }; 24 | Cs = Cs(randperm(length(Cs))); 25 | [~,CIs] = ismember(Cs,imdb.meta.classes); 26 | nTops = [6 6]; 27 | 28 | [~,pred] = max(decTest,[],2); 29 | gt = imdb.images.class(imdb.images.set==3)'; 30 | names = imdb.images.name(imdb.images.set==3); 31 | trueMask = gt==pred; 32 | 33 | saveDir = '~/Desktop/sketch_wrong'; 34 | vl_xmkdir(saveDir); 35 | 36 | for c=1:numel(Cs), 37 | fprintf('%10s ',Cs{c}); 38 | inds1 = (pred==CIs(c)) & trueMask; 39 | inds0 = (pred==CIs(c)) & ~trueMask; 40 | [~,I]=sort(decTest(inds1,CIs(c)),'ascend'); 41 | idxs = find(inds1); 42 | idxs = idxs(I); 43 | % idxs = idxs(randperm(length(idxs))); 44 | for i=1:nTops(1), 45 | vl_tightsubplot(length(Cs),sum(nTops),i+(c-1)*sum(nTops)); 46 | im0 = imread(fullfile(imdb.imageDir,names{idxs(i)})); 47 | im0 = imresize(im0,[400,400]); 48 | im0 = 1-im2double(im0); 49 | im0 = imfilter(im0,fspecial('disk',2)*15); 50 | % im = highlight_im(im,[0 1 0],0.05); 51 | im = ones(size(im0,1),size(im0,2),3); 52 | im(:,:,1) = im(:,:,1)-im0; 53 | im(:,:,3) = im(:,:,3)-im0; 54 | imshow(im); 55 | if i==1, text(25,50,imdb.meta.classes(gt(idxs(i))),'fontsize',18,'color','black'); end; 56 | end 57 | [~,I]=sort(decTest(inds0,CIs(c)),'descend'); 58 | idxs = find(inds0); 59 | idxs = idxs(I); 60 | for i=1:min(nTops(2),length(idxs)), 61 | vl_tightsubplot(length(Cs),sum(nTops),nTops(1)+i+(c-1)*sum(nTops)); 62 | im0 = imread(fullfile(imdb.imageDir,names{idxs(i)})); 63 | im0 = imresize(im0,[400 400]); 64 | im0 = 1-im2double(im0); 65 | im0 = imfilter(im0,fspecial('disk',2)*15); 66 | % im = highlight_im(im,[1 0 0],0.05); 67 | im = ones(size(im0,1),size(im0,2),3); 68 | im(:,:,2) = im(:,:,2)-im0; 69 | im(:,:,3) = im(:,:,3)-im0; 70 | imshow(im); 71 | text(25,50,imdb.meta.classes(gt(idxs(i))),'fontsize',18,'color','black'); 72 | end 73 | for i=min(nTops(2),length(idxs))+1:nTops(2), 74 | vl_tightsubplot(length(Cs),sum(nTops),nTops(1)+i+(c-1)*sum(nTops)); 75 | im = ones(10,10,3); 76 | imshow(im); 77 | end 78 | %{ 79 | keydown = waitforbuttonpress; 80 | if keydown==1, 81 | fprintf('GOOD\n'); 82 | else 83 | fprintf('\n'); 84 | end 85 | %} 86 | %{ 87 | fprintf('\n'); 88 | print(gcf,'-depsc',fullfile(saveDir,[strrep(Cs{c},' ','_') '.eps'])); 89 | print(gcf,'-dpng',fullfile(saveDir,'png',[strrep(Cs{c},' ','_') '.png'])); 90 | %} 91 | end 92 | 93 | end 94 | 95 | function im = highlight_im(im0,c,w) 96 | if isinteger(im0), im0 = double(im0)/255; end 97 | if isinteger(c), c = double(c)/255; end 98 | if size(im0,3)==1, im0 = repmat(im0,[1 1 3]); end 99 | sz = size(im0); 100 | if w<1, w = floor(mean(sz(1:2))*w); end 101 | im = bsxfun(@times,ones(size(im0)),reshape(c,[1 1 3])); 102 | im(w+1:end-w,w+1:end-w,:) = im0(w+1:end-w,w+1:end-w,:); 103 | end 104 | -------------------------------------------------------------------------------- /exp_scripts/learn_metric.m: -------------------------------------------------------------------------------- 1 | setup; 2 | 3 | if ~exist('feat','var'), 4 | feat = load('data/features/modelnet40phong-imagenet-vgg-m-finetuned-modelnet40phong-BS60_AUGnone-finetuned-modelnet40phong-BS60_AUGnone_MVfc7-none/NORM0/relu7.mat'); 5 | % feat = load('data/features/modelnet40phong-imagenet-vgg-m-finetuned-modelnet40phong-BS60_AUGnone-none/NORM0/relu7.mat'); 6 | end 7 | 8 | imdb = feat.imdb; 9 | if isfield(feat,'sid'), 10 | [imdb.images.sid,I] = sort(imdb.images.sid); 11 | imdb.images.id = imdb.images.id(I); 12 | else 13 | [imdb.images.id,I] = sort(imdb.images.id); 14 | end 15 | imdb.images.set = imdb.images.set(I); 16 | imdb.images.class = imdb.images.class(I); 17 | imdb.images.name = imdb.images.name(I); 18 | 19 | if isfield(feat,'sid'), 20 | shapeStartIdx = find(imdb.images.sid-[Inf imdb.images.sid(1:end-1)]); 21 | shapeSid = imdb.images.sid(shapeStartIdx); 22 | [feat.sid, I] = sort(feat.sid); 23 | feat.x = feat.x(I,:); 24 | assert(isequal(feat.sid,shapeSid')); 25 | else 26 | shapeStartIdx = 1:numel(imdb.images.id); 27 | [feat.id, I] = sort(feat.id); 28 | feat.x = feat.x(I,:); 29 | assert(isequal(feat.id,imdb.images.id')); 30 | end 31 | shapeSet = imdb.images.set(shapeStartIdx); 32 | shapeClass = imdb.images.class(shapeStartIdx); 33 | trainInd = shapeSet==1 | shapeSet==2; 34 | trainClass = shapeClass(trainInd); 35 | 36 | trainX = feat.x(trainInd,:); 37 | classIds = unique(trainClass); 38 | 39 | params.targetDim = 128; 40 | if isfield(feat,'id'), params.numPairs = 1e7; 41 | else params.numPairs = 1e6; end 42 | params.W = []; 43 | params.b = []; 44 | 45 | 46 | % TODO - hyperparameter grid-search by cross-validation 47 | params.gammaBias = 0.001; 48 | params.gamma = 0.0001; 49 | params.lambda = 0; 50 | 51 | % The projection matrix is modelDimRedFV.W 52 | tic 53 | modelDimRedFV = trainProj( trainX', trainClass, classIds, params ); 54 | toc 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /exp_scripts/prfigure.m: -------------------------------------------------------------------------------- 1 | 2 | recall_i = 0:0.1:1.0; 3 | fh = figure(1);clf;colormap(lines); 4 | pr_wzr = [1.0000 0.5767 0.4589 0.3738 0.3135 0.2703 0.2331 0.2031 0.1722 0.1383 0.0250 5 | 1.0000 0.6796 0.5637 0.4768 0.4179 0.3639 0.3143 0.2654 0.2194 0.1662 0.0250 6 | 1.0000 0.7184 0.6358 0.5793 0.5322 0.4842 0.4371 0.3928 0.3345 0.2533 0.0250]; 7 | plot(recall_i,pr_wzr(1,:),recall_i,pr_wzr(2,:),recall_i,pr_wzr(3,:)); hold on; 8 | 9 | eval_mvcnn = load('data/features/l11/evalRet.mat'); 10 | eval_mvcnn_metric = load('data/features/l12/evalRet.mat'); 11 | eval_fv = load('data/features/l5/evalRet.mat'); 12 | 13 | info = eval_fv.info; 14 | pr_fv = zeros(size(info.recall_i,1),length(recall_i)); 15 | for i=1:size(info.recall_i,1), 16 | [recall_all,I] = unique(info.recall_i(i,:)); 17 | precision_all = info.precision_i(i,I); 18 | pr_fv(i,:) = interp1(recall_all,precision_all,recall_i); 19 | end 20 | 21 | info = eval_mvcnn.info; 22 | pr_mvcnn = zeros(size(info.recall_i,1),length(recall_i)); 23 | for i=1:size(info.recall_i,1), 24 | [recall_all,I] = unique(info.recall_i(i,:)); 25 | precision_all = info.precision_i(i,I); 26 | pr_mvcnn(i,:) = interp1(recall_all,precision_all,recall_i); 27 | end 28 | 29 | info = eval_mvcnn_metric.info; 30 | pr_mvcnn_metric = zeros(size(info.recall_i,1),length(recall_i)); 31 | for i=1:size(info.recall_i,1), 32 | [recall_all,I] = unique(info.recall_i(i,:)); 33 | precision_all = info.precision_i(i,I); 34 | pr_mvcnn_metric(i,:) = interp1(recall_all,precision_all,recall_i); 35 | end 36 | plot(recall_i,mean(pr_fv),recall_i,mean(pr_mvcnn),recall_i,mean(pr_mvcnn_metric)); 37 | grid on; axis square; 38 | legend('Spherical Harmonic','Light Field','3D ShapeNets','Fisher vector', 'Ours (MVCNN)','Ours (MVCNN+metric)'); 39 | ylabel('Precision'); 40 | xlabel('Recall'); 41 | 42 | print(fh,'pr.eps','-depsc'); 43 | -------------------------------------------------------------------------------- /exp_scripts/visualize_saliency.m: -------------------------------------------------------------------------------- 1 | function visualize_saliency( imdb, net, c ) 2 | 3 | [imdb.images.id, I] = sort(imdb.images.id); 4 | imdb.images.name = imdb.images.name(I); 5 | imdb.images.class = imdb.images.class(I); 6 | imdb.images.set = imdb.images.set(I); 7 | imdb.images.sid = imdb.images.sid(I); 8 | 9 | [imdb.images.sid, I] = sort(imdb.images.sid); 10 | imdb.images.name = imdb.images.name(I); 11 | imdb.images.class = imdb.images.class(I); 12 | imdb.images.set = imdb.images.set(I); 13 | imdb.images.id = imdb.images.id(I); 14 | 15 | nImages = numel(imdb.images.name); 16 | nShapes = length(unique(imdb.images.sid)); 17 | nViews = nImages / nShapes; 18 | sids = imdb.images.sid(1:nViews:end); 19 | gts = imdb.images.class(1:nViews:end); 20 | 21 | 22 | toonDir = 'data/modelnet40view'; 23 | 24 | % figure; 25 | mag = zeros(1,nViews); 26 | saliency = cell(1,nViews); 27 | for i=randperm(length(sids)), 28 | if ~strcmp(imdb.meta.classes{imdb.images.class((i-1)*nViews+1)},c); 29 | continue; 30 | end 31 | names = cellfun(@(s) fullfile(imdb.imageDir,s), ... 32 | imdb.images.name(imdb.images.sid==sids(i)),'UniformOutput',false); 33 | ims = get_image_batch(names,net.normalization); 34 | dzdy = zeros(1,1,length(imdb.meta.classes)); 35 | dzdy(gts(i)) = 1; 36 | res = vl_simplenn(net,ims,dzdy); 37 | [~,I] = max(res(end).x); 38 | if imdb.images.class((i-1)*nViews+1)~=I, 39 | fprintf('%s: %s (passed)\n',imdb.meta.classes{imdb.images.class((i-1)*nViews+1)}, ... 40 | imdb.images.name{(i-1)*nViews+1}); 41 | continue; 42 | end 43 | max_v = 0; 44 | min_v = Inf; 45 | for v=1:nViews, 46 | vl_tightsubplot(2,nViews,v); 47 | imshow(imread(fullfile(imdb.imageDir,imdb.images.name{(i-1)*nViews+v}))); 48 | % imshow(imread(fullfile(toonDir,strrep(imdb.images.name{(i-1)*nViews+v},'toon','view')))); 49 | dzdx = res(1).dzdx(:,:,:,v); 50 | mag(v) = sqrt(sum(dzdx(:).^2)); 51 | saliency{v} = max(abs(dzdx),[],3); 52 | if max(saliency{v}(:))>max_v, max_v = max(saliency{v}(:)); end 53 | if min(saliency{v}(:))1, saveDist = true; end 17 | 18 | for s = 1:nSets, 19 | setId = find(cellfun(@(v) strcmp(sets{s},v),imdb.meta.sets)); 20 | sid = imdb.images.sid(imdb.images.set==setId); 21 | sid = sid(1:nViews:end); 22 | for i = 1:numel(r1{1,s}), 23 | [Y,I]=ismember(r1{1,s}{i}, r2{1,s}{i}); 24 | assert(all(Y)); 25 | [~,I] = sort(I); 26 | r1{1,s}{i} = r1{1,s}{i}(I); 27 | if saveDist, r1{2,s}{i} = r1{2,s}{i}(I); end 28 | end 29 | % write to file 30 | if ~isempty(savePath), 31 | fprintf('Saving re-ranked results to %s ...', fullfile(savePath,sets{s})); 32 | vl_xmkdir(fullfile(savePath,sets{s})); 33 | for i=1:numel(sid) 34 | fid = fopen(fullfile(savePath,sets{s},sprintf('%06d',sid(i))),'w+'); 35 | r = r1{1,s}{i}; 36 | if saveDist, 37 | r = [r ; r1{2,s}{i}]; 38 | fprintf(fid, '%06d %f\n',r); 39 | else 40 | fprintf(fid, '%06d\n', r); 41 | end 42 | fclose(fid); 43 | end 44 | fprintf(' done!\n'); 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /run_experiments.m: -------------------------------------------------------------------------------- 1 | % function run_experiments() 2 | 3 | setup; 4 | 5 | expDir = 'data'; 6 | 7 | %% modelnet experiments (w/ upright assumption) 8 | get_imdb('modelnet40v1', ... 9 | 'func', @(s) setup_imdb_modelnet(s,'useUprightAssumption',true), ... 10 | 'rebuild', true); 11 | % phase 1 -- regular cnn 12 | cnn_shape('modelnet40v1', ... 13 | 'expDir', fullfile(expDir,'modelnet40v1'), ... 14 | 'numFetchThreads', 12, ... 15 | 'pad', 32, ... 16 | 'border', 32, ... 17 | 'multiview', false, ... 18 | 'batchSize', 60, ... 19 | 'gpus', [1], ... 20 | 'maxIterPerEpoch', Inf, ... 21 | 'numEpochs', [15 10 15], ... 22 | 'learningRate', [0.05*ones(1,10) 0.01*ones(1,20) 0.001*ones(1,5) 0.0001*ones(1,5)] ... 23 | ); 24 | % phase 2 -- mvcnn 25 | cnn_shape('modelnet40v1', ... 26 | 'expDir', fullfile(expDir,'modelnet40v1_mv'), ... 27 | 'numFetchThreads', 12, ... 28 | 'pad', 32, ... 29 | 'border', 32, ... 30 | 'baseModel', fullfile(expDir,'modelnet40v1','net-deployed.mat'), ... 31 | 'multiview', true, ... 32 | 'viewpoolPos', 'conv5', ... 33 | 'batchSize', 5, ... 34 | 'gpus', [1], ... 35 | 'maxIterPerEpoch', Inf, ... 36 | 'numEpochs', [0 15 15], ... 37 | 'learningRate', [0.005*ones(1,5) 0.001*ones(1,5) 0.0001*ones(1,10) 0.00001*ones(1,10)] ... 38 | ); 39 | 40 | %% shapenet experiments (w/ upright assumption) 41 | get_imdb('shapenet55v1', ... 42 | 'func', @(s) setup_imdb_shapenet(s,'nViews',12,'useSubclass',false), ... 43 | 'rebuild', true); 44 | % phase 1 -- regular cnn 45 | cnn_shape('shapenet55v1', ... 46 | 'expDir', fullfile(expDir,'shapenet55v1'), ... 47 | 'numFetchThreads', 12, ... 48 | 'multiview', false, ... 49 | 'batchSize', 84, ... 50 | 'gpus', [1], ... 51 | 'maxIterPerEpoch', [1000 200], ... % limiting 1000 iters/epoch at training and 200 iters/epoch at testing 52 | 'balancingFunction', @(v) round(mean(v)*(v/mean(v)).^0.5), ... 53 | 'numEpochs', [15 10 15], ... 54 | 'learningRate', [0.05*ones(1,10) 0.01*ones(1,10) 0.001*ones(1,10) 0.0005*ones(1,10)] ... 55 | ); 56 | % phase 2 -- mvcnn 57 | cnn_shape('shapenet55v1', ... 58 | 'expDir', fullfile(expDir,'shapenet55v1_mv'), ... 59 | 'numFetchThreads', 12, ... 60 | 'baseModel', fullfile(expDir,'shapenet55v1','net-deployed.mat'), ... 61 | 'multiview', true, ... 62 | 'viewpoolPos', 'relu7', ... 63 | 'batchSize', 7, ... 64 | 'gpus', [1], ... 65 | 'maxIterPerEpoch', [1000 200], ... % limiting 1000 iters/epoch at training and 200 iters/epoch at testing 66 | 'balancingFunction', @(v) round(mean(v)*(v/mean(v)).^0.5), ... 67 | 'numEpochs', [0 20 20], ... 68 | 'learningRate', [0.005*ones(1,10) 0.001*ones(1,15) 0.0001*ones(1,10) 0.00005*ones(1,5)] ... 69 | ); 70 | -------------------------------------------------------------------------------- /run_retrieval.m: -------------------------------------------------------------------------------- 1 | function [results] = run_retrieval(feat, imdb, varargin) 2 | % e.g.: r = run_retrieval('prob.mat','shapenet55v2','savePath','prob', ... 3 | % 'distFn',@(x1,x2) par_alldist(x1,[],'numWorkers',36,'maxParts',500)); 4 | 5 | 6 | opts.distFn = @(x1,x2) par_alldist(x1,x2,'numWorkers',12,'maxParts',100); 7 | opts.topK = 1000; 8 | opts.sets = {'train', 'val', 'test'}; 9 | opts.savePath = []; 10 | opts.saveDist = true; 11 | opts.resultType = 'fixedLength'; % 'fixedLength' | 'sameClass' 12 | opts = vl_argparse(opts, varargin); 13 | 14 | if ischar(feat), 15 | feat = load(feat); 16 | feat = feat.feat; 17 | end 18 | 19 | if ischar(imdb), 20 | imdb = get_imdb(imdb); 21 | end 22 | 23 | nViews = numel(imdb.images.name) / size(feat,1); 24 | 25 | results = cell(2,numel(opts.sets)); 26 | for i = 1:numel(opts.sets), 27 | setId = find(cellfun(@(s) strcmp(opts.sets{i},s),imdb.meta.sets)); 28 | f = feat(imdb.images.set(1:nViews:end)==setId,:); 29 | sid = imdb.images.sid(imdb.images.set==setId); 30 | sid = sid(1:nViews:end); 31 | nShapes = size(f,1); 32 | 33 | D = opts.distFn(f',f'); 34 | if strcmpi(opts.resultType,'sameClass'), 35 | [~,I] = max(f,[],2); 36 | sameLabelMask = arrayfun(@(l) (I'==l), I,'UniformOutput', false); 37 | results{1,i} = cellfun(@(c) sid(c), sameLabelMask, 'UniformOutput', false); 38 | results{2,i} = cell(nShapes,1); 39 | for j=1:nShapes, 40 | [results{2,i}{j},I] = sort(D(j,sameLabelMask{j}),'ascend'); 41 | topK = min(opts.topK, numel(I)); 42 | results{2,i}{j} = results{2,i}{j}(1:topK); 43 | results{1,i}{j} = results{1,i}{j}(I(1:topK)); 44 | end 45 | elseif strcmpi(opts.resultType,'fixedLength') 46 | [Y,I] = sort(D,2,'ascend'); 47 | topK = min(opts.topK, numel(sid)); 48 | I = I(:,1:topK); 49 | dist_mat = Y(:,1:topK); 50 | result_mat = sid(I); 51 | results{1,i} = cell(nShapes, 1); 52 | results{2,i} = cell(nShapes, 1); 53 | for j=1:nShapes, 54 | results{1,i}{j} = result_mat(j,:); 55 | results{2,i}{j} = dist_mat(j,:); 56 | end 57 | else 58 | error('Unknown option: %s', opts.resultType); 59 | end 60 | 61 | % write to file 62 | if ~isempty(opts.savePath), 63 | fprintf('Saving retrieval results to %s ...', fullfile(opts.savePath,opts.sets{i})); 64 | vl_xmkdir(fullfile(opts.savePath,opts.sets{i})); 65 | for k=1:numel(sid), 66 | fid = fopen(fullfile(opts.savePath,opts.sets{i},sprintf('%06d',sid(k))),'w+'); 67 | r = results{1,i}{k}; 68 | if opts.saveDist, 69 | r = [r ; results{2,i}{k}]; 70 | fprintf(fid,'%06d %f\n',r); 71 | else 72 | fprintf(fid,'%06d\n',r); 73 | end 74 | fclose(fid); 75 | end 76 | fprintf(' done!\n'); 77 | end 78 | 79 | end 80 | -------------------------------------------------------------------------------- /setup.m: -------------------------------------------------------------------------------- 1 | function setup(doCompile, matconvnetOpts) 2 | % SETUP Setup paths, dependencies, etc. 3 | % 4 | % doCompile:: false 5 | % Set to true to compile the libraries 6 | % matconvnetOpts:: struct('enableGpu',false) 7 | % Options for vl_compilenn 8 | 9 | if nargin==0, 10 | doCompile = false; 11 | elseif nargin<2, 12 | matconvnetOpts = struct('enableGpu', false); 13 | end 14 | 15 | if doCompile && gpuDeviceCount()==0 ... 16 | && isfield(matconvnetOpts,'enableGpu') && matconvnetOpts.enableGpu, 17 | fprintf('No supported gpu detected! '); 18 | return; 19 | end 20 | 21 | addpath(genpath('dataset')); 22 | addpath(genpath('utils')); 23 | 24 | % ------------------------------------------------------------------------- 25 | % liblinear 26 | % ------------------------------------------------------------------------- 27 | if doCompile, 28 | if ispc 29 | cd('dependencies/liblinear-1.96/'); 30 | % change to vcvars32 for 32-bit platforms 31 | !vcvars64 & nmake /f Makefile.win clean & nmake /f Makefile.win lib 32 | cd('../../'); 33 | else 34 | !make -C dependencies/liblinear-1.96/ clean 35 | !make -C dependencies/liblinear-1.96/ 36 | end 37 | run dependencies/liblinear-1.96/matlab/make.m 38 | end 39 | addpath('dependencies/liblinear-1.96/matlab'); 40 | 41 | % ------------------------------------------------------------------------- 42 | % vlfeat 43 | % ------------------------------------------------------------------------- 44 | if doCompile, 45 | if ispc 46 | cd('dependencies/vlfeat/'); 47 | % change to vcvars32 for 32-bit platforms 48 | % if there is a crash here, you need to explicitly change 49 | % the paths and parameters in vlfeat's Makefile.mak 50 | % also remove "-f $(MEXOPT)" from Makefile.mak for latest VS 51 | % versions. 52 | !vcvars64 & nmake /f Makefile.mak clean & nmake /f Makefile.mak 53 | cd('../../'); 54 | else 55 | !make -C dependencies/vlfeat/ clean 56 | !make -C dependencies/vlfeat/ 57 | end 58 | end 59 | run dependencies/vlfeat/toolbox/vl_setup.m 60 | 61 | % ------------------------------------------------------------------------- 62 | % matconvnet 63 | % ------------------------------------------------------------------------- 64 | if doCompile, 65 | run dependencies/matconvnet/matlab/vl_setupnn.m 66 | cd dependencies/matconvnet 67 | vl_compilenn(matconvnetOpts); 68 | cd ../.. 69 | end 70 | run dependencies/matconvnet/matlab/vl_setupnn.m 71 | addpath('dependencies/matconvnet/examples/imagenet'); 72 | -------------------------------------------------------------------------------- /shape_compute_descriptor.m: -------------------------------------------------------------------------------- 1 | function descr = shape_compute_descriptor( path_to_shape, varargin ) 2 | % SHAPE_COMPUTE_DESCRIPTOR Compute and save the CNN descriptor for a single 3 | % input obj/off shape, or for each shape in an input folder 4 | % Quick examples of use: 5 | % shape_compute_descriptor('bunny.off'); 6 | % shape_compute_descriptor('my_bunnies_folder/'); 7 | % 8 | % path_to_shape:: (default) 'data/' 9 | % can be either a filename for a mesh in OBJ/OFF format 10 | % or a name of folder containing multiple OBJ/OFF meshes 11 | % `cnnModel`:: (default) '' 12 | % this is a matlab file with the saved CNN parameters 13 | % if the default file is not found, it will be downloaded from our 14 | % server. 15 | % Note: The default mat file assumes that shapes that are 16 | % upright oriented according to +Z axis! 17 | % if you want to use the CNN trained *without upright assumption*, use 18 | % 'cnn-vggm-relu7-v2' 19 | % `applyMetric`:: (default) false 20 | % set to true to disable transforming descriptor based on specified 21 | % distance metric 22 | % `metricModel`:: (default:) '' 23 | % this is a matlab file with the saved metric parameters 24 | % if the default file is not found, it will attempt to download from our 25 | % server 26 | % `gpus`:: (default) [] 27 | % set to use GPU 28 | 29 | setup; 30 | 31 | if nargin<1 || isempty(path_to_shape), 32 | path_to_shape = 'data/'; 33 | end 34 | 35 | % default options 36 | opts.feature = 'viewpool'; 37 | opts.useUprightAssumption = true; 38 | opts.applyMetric = false; 39 | opts.gpus = []; 40 | [opts, varargin] = vl_argparse(opts,varargin); 41 | 42 | opts.metricModel = ''; 43 | opts.cnnModel = ''; 44 | opts = vl_argparse(opts,varargin); 45 | 46 | % locate network file 47 | default_viewpool_loc = 'relu7'; 48 | vl_xmkdir(fullfile('data','models')) ; 49 | if isempty(opts.cnnModel), 50 | if opts.useUprightAssumption, 51 | opts.cnnModel = sprintf('cnn-vggm-%s-v1',default_viewpool_loc); 52 | nViews = 12; 53 | else 54 | opts.cnnModel = sprintf('cnn-vggm-%s-v2',default_viewpool_loc); 55 | nViews = 80; 56 | end 57 | baseModel = 'imagenet-matconvnet-vgg-m'; 58 | cnn = cnn_shape_init({},'base',baseModel,'viewpoolPos',default_viewpool_loc,'nViews',nViews); 59 | netFilePath = fullfile('data','models',[opts.cnnModel '.mat']); 60 | save(netFilePath,'-struct','cnn'); 61 | end 62 | 63 | if ~ischar(opts.cnnModel), 64 | cnn = opts.cnnModel; 65 | else 66 | netFilePath = fullfile('data','models',[opts.cnnModel '.mat']); 67 | if ~exist(netFilePath,'file'), 68 | fprintf('Downloading model (%s) ...', opts.cnnModel) ; 69 | urlwrite(fullfile('http://maxwell.cs.umass.edu/mvcnn/models/', ... 70 | [opts.cnnModel '.mat']), netFilePath) ; 71 | fprintf(' done!\n'); 72 | end 73 | cnn = load(netFilePath); 74 | end 75 | 76 | % locate metric file 77 | if isempty(opts.metricModel) && opts.applyMetric, 78 | opts.applyMetric = false; 79 | warning('No metric file specified. Post-processing turned off'); 80 | end 81 | if opts.applyMetric 82 | metricFilePath = fullfile('data','models',[opts.metricModel '.mat']); 83 | if ~exist(metricFilePath,'file'), 84 | fprintf('Downloading model (%s) ...', opts.metricModel) ; 85 | urlwrite(fullfile('http://maxwell.cs.umass.edu/mvcnn/models/', ... 86 | [opts.metricModel '.mat']), metricFilePath) ; 87 | fprintf(' done!\n'); 88 | end 89 | modelDimRedFV = load(metricFilePath); 90 | end 91 | 92 | % see if it's a multivew net 93 | viewpoolIdx = find(cellfun(@(x)strcmp(x.name, 'viewpool'), cnn.layers)); 94 | if ~isempty(viewpoolIdx), 95 | if numel(viewpoolIdx)>1, 96 | error('More than one viewpool layers found!'); 97 | end 98 | if ~isfield(cnn.layers{viewpoolIdx},'vstride'), 99 | num_views = cnn.layers{viewpoolIdx}.stride; % old format 100 | else 101 | num_views = cnn.layers{viewpoolIdx}.vstride; 102 | end 103 | fprintf('CNN model is based on %d views. Will process %d views per mesh.\n', num_views, num_views); 104 | else 105 | error('Computing a descriptor per shape requires a multi-view CNN.'); 106 | end 107 | 108 | % work on mesh (or meshes) 109 | if strcmpi((path_to_shape(end-2:end)),'off') || strcmpi((path_to_shape(end-2:end)),'obj') 110 | mesh_filenames(1).name = path_to_shape; 111 | else 112 | mesh_filenames = [dir( fullfile(path_to_shape, '*.off' ) ); dir( fullfile(path_to_shape, '*.obj') )]; 113 | for i=1:length(mesh_filenames) 114 | mesh_filenames(i).name = fullfile(path_to_shape,mesh_filenames(i).name); 115 | end 116 | if isempty(mesh_filenames) 117 | error('No obj/off meshes found in the specified folder!'); 118 | end 119 | end 120 | 121 | descr = cell( 1, length(mesh_filenames)); 122 | fig = figure('Visible','off'); 123 | for i=1:length(mesh_filenames) 124 | fprintf('Loading shape %s ...', mesh_filenames(i).name); 125 | mesh = loadMesh( mesh_filenames(i).name ); 126 | if isempty(mesh.F) 127 | error('Could not load mesh from file'); 128 | else 129 | fprintf(' done!\n'); 130 | end 131 | fprintf('Rendering shape %s ...', mesh_filenames(i).name); 132 | if num_views == 12 133 | ims = render_views(mesh, 'figHandle', fig); 134 | else 135 | ims = render_views(mesh, 'use_dodecahedron_views', true, 'figHandle', fig); 136 | end 137 | fprintf(' done!\n'); 138 | outs = cnn_shape_get_features(ims, cnn, {opts.feature}, 'gpus', opts.gpus); 139 | out = outs.(opts.feature)(:); 140 | if opts.applyMetric 141 | out = single(modelDimRedFV.W*out); 142 | end 143 | descr{i} = out; 144 | out = double(out); 145 | save( sprintf('%s_descriptor.txt', mesh_filenames(i).name(1:end-4)), 'out', '-ascii'); 146 | end 147 | close(fig); 148 | -------------------------------------------------------------------------------- /utils/RenderMe/RenderDepth/RenderMex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | This code is to render a Mesh given a 3x4 camera matrix with an image resolution widthxheight. The rendering result is an ID map for facets, edges and vertices. This can usually used for occlusion testing in texture mapping a model from an image, such as the texture mapping in the following two papers. 4 | 5 | --Jianxiong Xiao http://mit.edu/jxiao/ 6 | 7 | Citation: 8 | 9 | [1] J. Xiao, T. Fang, P. Zhao, M. Lhuillier, and L. Quan 10 | Image-based Street-side City Modeling 11 | ACM Transaction on Graphics (TOG), Volume 28, Number 5 12 | Proceedings of ACM SIGGRAPH Asia 2009 13 | 14 | [2] J. Xiao, T. Fang, P. Tan, P. Zhao, E. Ofek, and L. Quan 15 | Image-based Facade Modeling 16 | ACM Transaction on Graphics (TOG), Volume 27, Number 5 17 | Proceedings of ACM SIGGRAPH Asia 2008 18 | 19 | */ 20 | 21 | #include "mex.h" 22 | #include 23 | #include 24 | 25 | void uint2uchar(unsigned int in, unsigned char* out){ 26 | out[0] = (in & 0x00ff0000) >> 16; 27 | out[1] = (in & 0x0000ff00) >> 8; 28 | out[2] = in & 0x000000ff; 29 | } 30 | 31 | unsigned int uchar2uint(unsigned char* in){ 32 | unsigned int out = (((unsigned int)(in[0])) << 16) + (((unsigned int)(in[1])) << 8) + ((unsigned int)(in[2])); 33 | return out; 34 | } 35 | 36 | // Input: 37 | // arg0: 3x4 Projection matrix, 38 | // arg1: image width, 39 | // arg2: image height, 40 | // arg3: 3xn double vertices matrix, 41 | // arg4: 4xn uint32 face matrix, index from zero 42 | // Output: you will need to transpose the result in Matlab manually 43 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 44 | // mexPrintf("RenderMex\n"); 45 | 46 | float m_near = 0.3; 47 | float m_far = 1e8; 48 | int m_level = 0; 49 | 50 | int m_linewidth = 1; 51 | int m_pointsize = 1; 52 | 53 | double* projection = mxGetPr(prhs[0]); // 3x4 matrix 54 | int m_width = (int)mxGetScalar(prhs[1]); 55 | int m_height = (int)mxGetScalar(prhs[2]); 56 | double* vertex = mxGetPr(prhs[3]); // 3xn double vertices matrix 57 | unsigned int num_vertex = mxGetN(prhs[3]); 58 | unsigned int* face = (unsigned int*) mxGetData(prhs[4]); // 4xn uint32 face matrix 59 | unsigned int num_face = mxGetN(prhs[4]); 60 | 61 | // Step 1: setup off-screen mesa's binding 62 | OSMesaContext ctx; 63 | ctx = OSMesaCreateContextExt(OSMESA_RGB, 32, 0, 0, NULL ); 64 | unsigned char * pbuffer = new unsigned char [3 * m_width * m_height]; 65 | // Bind the buffer to the context and make it current 66 | if (!OSMesaMakeCurrent(ctx, (void*)pbuffer, GL_UNSIGNED_BYTE, m_width, m_height)) { 67 | mexErrMsgTxt("OSMesaMakeCurrent failed!: "); 68 | } 69 | OSMesaPixelStore(OSMESA_Y_UP, 0); 70 | 71 | /*// Clear previous settings 72 | glMatrixMode(GL_PROJECTION); 73 | glLoadIdentity(); 74 | glMatrixMode(GL_MODELVIEW); 75 | glLoadIdentity();*/ 76 | 77 | // Step 2: Setup basic OpenGL setting 78 | glEnable(GL_DEPTH_TEST); 79 | glDisable(GL_LIGHTING); 80 | glDisable(GL_CULL_FACE); 81 | //glEnable(GL_CULL_FACE); 82 | //glCullFace(GL_BACK); 83 | glPolygonMode(GL_FRONT, GL_FILL); 84 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 85 | //glClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2], 1.0f); // this line seems useless 86 | glViewport(0, 0, m_width, m_height); 87 | 88 | // Step 3: Set projection matrices 89 | double scale = (0x0001) << m_level; 90 | double final_matrix[16]; 91 | 92 | // new way: faster way by reuse computation and symbolic derive. See sym_derive.m to check the math. 93 | double inv_width_scale = 1.0/(m_width*scale); 94 | double inv_height_scale = 1.0/(m_height*scale); 95 | double inv_width_scale_1 =inv_width_scale - 1.0; 96 | double inv_height_scale_1_s = -(inv_height_scale - 1.0); 97 | double inv_width_scale_2 = inv_width_scale*2.0; 98 | double inv_height_scale_2_s = -inv_height_scale*2.0; 99 | double m_far_a_m_near = m_far + m_near; 100 | double m_far_s_m_near = m_far - m_near; 101 | double m_far_d_m_near = m_far_a_m_near/m_far_s_m_near; 102 | final_matrix[ 0]= projection[2+0*3]*inv_width_scale_1 + projection[0+0*3]*inv_width_scale_2; 103 | final_matrix[ 1]= projection[2+0*3]*inv_height_scale_1_s + projection[1+0*3]*inv_height_scale_2_s; 104 | final_matrix[ 2]= projection[2+0*3]*m_far_d_m_near; 105 | final_matrix[ 3]= projection[2+0*3]; 106 | final_matrix[ 4]= projection[2+1*3]*inv_width_scale_1 + projection[0+1*3]*inv_width_scale_2; 107 | final_matrix[ 5]= projection[2+1*3]*inv_height_scale_1_s + projection[1+1*3]*inv_height_scale_2_s; 108 | final_matrix[ 6]= projection[2+1*3]*m_far_d_m_near; 109 | final_matrix[ 7]= projection[2+1*3]; 110 | final_matrix[ 8]= projection[2+2*3]*inv_width_scale_1 + projection[0+2*3]*inv_width_scale_2; 111 | final_matrix[ 9]= projection[2+2*3]*inv_height_scale_1_s + projection[1+2*3]*inv_height_scale_2_s; 112 | final_matrix[10]= projection[2+2*3]*m_far_d_m_near; 113 | final_matrix[11]= projection[2+2*3]; 114 | final_matrix[12]= projection[2+3*3]*inv_width_scale_1 + projection[0+3*3]*inv_width_scale_2; 115 | final_matrix[13]= projection[2+3*3]*inv_height_scale_1_s + projection[1+3*3]*inv_height_scale_2_s; 116 | final_matrix[14]= projection[2+3*3]*m_far_d_m_near - (2*m_far*m_near)/m_far_s_m_near; 117 | final_matrix[15]= projection[2+3*3]; 118 | 119 | // matrix is ready. use it 120 | glMatrixMode(GL_PROJECTION); 121 | glLoadMatrixd(final_matrix); 122 | glMatrixMode(GL_MODELVIEW); 123 | glLoadIdentity(); 124 | 125 | // Step 3: render the mesh with encoded color from their ID 126 | unsigned char colorBytes[3]; 127 | unsigned int base_offset; 128 | 129 | // render face 130 | // printf("face begin\n"); 131 | base_offset = 1; 132 | for (unsigned int i = 0; i < num_face; ++i) { 133 | uint2uchar(base_offset+i,colorBytes); 134 | glColor3ubv(colorBytes); 135 | glBegin(GL_POLYGON); 136 | glVertex3dv(vertex+3*(*face++)); 137 | glVertex3dv(vertex+3*(*face++)); 138 | glVertex3dv(vertex+3*(*face++)); 139 | glVertex3dv(vertex+3*(*face++)); 140 | glEnd(); 141 | } 142 | // printf("face done\n"); 143 | 144 | // // render vertex 145 | // base_offset = 1+num_face; 146 | // glPointSize(m_pointsize); 147 | // glBegin(GL_POINTS); 148 | // for (unsigned int i = 0; i < num_vertex; ++i) { 149 | // uint2uchar(base_offset+i,colorBytes); 150 | // glColor3ubv(colorBytes); 151 | // glVertex3dv(vertex); 152 | // // printf("%lf %lf %lf\n", vertex[0], vertex[1], vertex[2]); 153 | // vertex+=3; 154 | // } 155 | // glEnd(); 156 | 157 | glFinish(); // done rendering 158 | 159 | // Step 5: convert the result from color to interger array 160 | // plhs[0] = mxCreateNumericMatrix(m_width, m_height, mxUINT32_CLASS, mxREAL); 161 | // unsigned int* result = (unsigned int*) mxGetData(plhs[0]); 162 | // 163 | // unsigned int* resultCur = result; 164 | // unsigned int* resultEnd = result + m_width * m_height; 165 | // unsigned char * pbufferCur = pbuffer; 166 | // while(resultCur != resultEnd){ 167 | // *resultCur = uchar2uint(pbufferCur); 168 | // pbufferCur += 3; 169 | // ++resultCur; 170 | // } 171 | 172 | 173 | unsigned int* pDepthBuffer; 174 | GLint outWidth, outHeight, bitPerDepth; 175 | OSMesaGetDepthBuffer(ctx, &outWidth, &outHeight, &bitPerDepth, (void**)&pDepthBuffer); 176 | // mexPrintf("w = %d, h = %d, bitPerDepth = %d\n", outWidth, outHeight, bitPerDepth); 177 | plhs[0] = mxCreateNumericMatrix((int)outWidth, (int)outHeight, mxUINT32_CLASS, mxREAL); 178 | unsigned int* result = (unsigned int*) mxGetData(plhs[0]); 179 | for(int i=0; i z_far_ratio * maxDepth); 47 | crop = findCropRegion(~cropmask); 48 | depth = depth(crop(1)+(0:crop(3)-1), crop(2)+(0:crop(4)-1)); 49 | depth(cropmask(crop(1)+(0:crop(3)-1), crop(2)+(0:crop(4)-1))) = z_far_ratio * maxDepth; 50 | 51 | end 52 | 53 | function crop = findCropRegion(mask) 54 | 55 | [xlist, ylist] = ind2sub(size(mask), find(mask)); 56 | xmin = min(xlist); xmax = max(xlist); 57 | ymin = min(ylist); ymax = max(ylist); 58 | crop = [xmin, ymin, xmax - xmin + 1, ymax - ymin + 1]; 59 | 60 | margin_ratio = 0.2; 61 | pad = floor(margin_ratio*crop(3:4)); 62 | crop = [crop(1)-pad(1), ... 63 | crop(2)-pad(2), ... 64 | crop(3)+pad(1)*2, ... 65 | crop(4)+pad(2)*2, ... 66 | ]; 67 | crop = max(crop,1); 68 | crop(3:4) = min(crop(3:4),size(mask)-crop(1:2)+1); 69 | 70 | end 71 | 72 | function R = genRotMat(theta) 73 | 74 | R = [cos(theta), 0, -sin(theta) 75 | 0, 1, 0 76 | sin(theta), 0, cos(theta)]; 77 | 78 | end 79 | 80 | function R = genTiltMat(theta) 81 | 82 | R = [1, 0, 0 83 | 0, cos(theta), -sin(theta) 84 | 0, sin(theta), cos(theta)]; 85 | 86 | end 87 | 88 | function coornew = scalePoints(coor, center, size) 89 | 90 | % function coornew = scalePoints(coor, box) 91 | % 92 | % parameters: 93 | % coor: 3*n coordinates of n points 94 | % center: 3*1 the center of new point cloud 95 | % size: 3*1 the size of new point cloud 96 | 97 | minv = min(coor, [], 2); 98 | maxv = max(coor, [], 2); 99 | oldCenter = (minv+maxv)/2; 100 | oldSize = maxv - minv; 101 | scale = min(size ./ oldSize); 102 | coornew = bsxfun(@plus, scale * coor, center-scale*oldCenter); 103 | 104 | end 105 | -------------------------------------------------------------------------------- /utils/RenderMe/RenderDepth/offLoader.m: -------------------------------------------------------------------------------- 1 | function offobj = offLoader(filename) 2 | 3 | % function offobj = offLoader(filename) 4 | 5 | offobj = struct(); 6 | fid = fopen(filename, 'r'); 7 | words = fscanf(fid, 'OFF %d %d %d'); 8 | if isempty(words), 9 | words = fscanf(fid,'COFF %d %d %d'); 10 | c_off = true; 11 | else 12 | c_off = false; 13 | end 14 | nV = words(1); nF = words(2); nE = words(3); 15 | if c_off, 16 | offobj.vmat = reshape(fscanf(fid, '%f', nV*7), 7, nV); 17 | offobj.vmat = offobj.vmat(1:3,:); 18 | else 19 | offobj.vmat = reshape(fscanf(fid, '%f', nV*3), 3, nV); 20 | end 21 | fstr = textscan(fid, '%s', nF, 'delimiter', '\n', 'MultipleDelimsAsOne', 1); 22 | tfmat = zeros(nF*2,4); 23 | nf3 = 0; 24 | for i=1:nF 25 | words = sscanf(fstr{1}{i}, '%d'); 26 | if words(1) == 3 27 | nf3 = nf3 + 1; 28 | tfmat(nf3, :) = words([4,2,3,4]); 29 | elseif words(1) == 4 30 | nf3 = nf3 + 1; 31 | tfmat(nf3, :) = words([4,2,3,4]); 32 | nf3 = nf3 + 1; 33 | tfmat(nf3, :) = words([5,2,4,5]); 34 | else 35 | error('size of face is not 3 or 4'); 36 | end 37 | end 38 | offobj.fmat = tfmat(1:nf3, :)'; 39 | fclose(fid); 40 | 41 | end 42 | 43 | -------------------------------------------------------------------------------- /utils/RenderMe/RenderDepth/render_edge.m: -------------------------------------------------------------------------------- 1 | function render_edge( offfiles, readDir, saveDir, nViews, logPath) 2 | 3 | if ~exist('nViews','var') || isempty(nViews), 4 | nViews = 12; 5 | end 6 | if ~exist('readDir', 'var') || isempty(readDir), 7 | readDir = '.'; 8 | end 9 | if ~exist('saveDir', 'var') || isempty(saveDir), 10 | saveDir = '.'; 11 | end 12 | if ~exist('logPath', 'var') || isempty(logPath), 13 | logPath = fullfile('log','render.log'); 14 | end 15 | Rs = linspace(0,2*pi,nViews+1); 16 | Rs = Rs(1:end-1); 17 | max_sz = 628; 18 | im_sz = 800; 19 | suffix = 'zedge'; 20 | nShapes = numel(offfiles); 21 | 22 | poolobj = gcp('nocreate'); 23 | if isempty(poolobj), 24 | poolSize = 0; 25 | else 26 | poolSize = poolobj.NumWorkers; 27 | end 28 | parfor (offi = 1:nShapes, poolSize) 29 | % for offi = 1:nShapes, 30 | fprintf(' %s\n',offfiles{offi}); 31 | for i = 1:length(Rs), 32 | savePath = fullfile(saveDir,offfiles{offi}); 33 | savePath = [savePath(1:end-4) '_' suffix '_' num2str(i) '.png']; 34 | if exist(savePath,'file'), continue; end; 35 | 36 | % get depth buffer 37 | depth = off2im(fullfile(readDir,offfiles{offi}),3.5,Rs(i),0,2.5,0); 38 | if isempty(depth), 39 | fid = fopen(logPath,'a+'); 40 | fprintf(fid,'Skipped because of no object: %s (view #%d)\n',offfiles{offi},i); 41 | fclose(fid); 42 | continue; 43 | end 44 | depth = (depth-min(depth(:)))/(max(depth(:))-min(depth(:))); 45 | 46 | % edge map 47 | emap0 = edge(depth,'canny'); 48 | region = find_object_region(emap0); 49 | if isempty(region), 50 | fid = fopen(logPath,'a+'); 51 | fprintf(fid,'Skipped because of no object: %s (view #%d)\n',offfiles{offi},i); 52 | fclose(fid); 53 | continue; 54 | end 55 | t_sz = floor(max(region(3:4)) / max_sz * im_sz); 56 | [yi, xi] = ind2sub(size(emap0),find(emap0)); 57 | yi = floor(yi + (t_sz-region(4))/2 - region(2) + 1); 58 | xi = floor(xi + (t_sz-region(3))/2 - region(1) + 1); 59 | emap = zeros(t_sz,t_sz); 60 | emap(sub2ind(size(emap),yi,xi)) = 1; 61 | emap = imdilate(emap,strel('disk',1)); 62 | emap = imfilter(emap,fspecial('gaussian',[3 3],0.5)); 63 | emap = max(min(imresize(emap,[im_sz im_sz]),1),0); 64 | emap = 1-emap; 65 | 66 | % save 67 | vl_xmkdir(fileparts(savePath)); 68 | imwrite(emap, savePath); 69 | end 70 | end 71 | 72 | end 73 | 74 | 75 | 76 | function crop = find_object_region(mask) 77 | 78 | [ylist, xlist] = ind2sub(size(mask), find(mask)); 79 | xmin = min(xlist); xmax = max(xlist); 80 | ymin = min(ylist); ymax = max(ylist); 81 | crop = [xmin, ymin, xmax - xmin + 1, ymax - ymin + 1]; 82 | 83 | end 84 | -------------------------------------------------------------------------------- /utils/add_rotated_copies.m: -------------------------------------------------------------------------------- 1 | function add_rotated_copies(imdb,saveDir,angles,flips) 2 | 3 | if ~exist('saveDir','var') || isempty(saveDir), 4 | saveDir = '.'; 5 | end 6 | if ~exist('angles','var') || isempty(angles), 7 | angles = linspace(0,360,13); 8 | angles = angles(1:end-1); 9 | end 10 | if ~exist('flips','var') || isempty(flips), 11 | flips = zeros(size(angles)); 12 | end 13 | 14 | nInstances = numel(imdb.images.name); 15 | nViews = length(angles); 16 | 17 | imdb2 = imdb; 18 | imdb2.imageDir = saveDir; 19 | imdb2.meta.nShapes = nInstances; 20 | 21 | imdb2.images.name = cell(1,nInstances*nViews); 22 | imdb2.images.class = reshape(repmat(imdb.images.class,[nViews 1]),[1 nInstances*nViews]); 23 | imdb2.images.set = reshape(repmat(imdb.images.set,[nViews 1]),[1 nInstances*nViews]); 24 | imdb2.images.id = 1:nInstances*nViews; 25 | imdb2.images.sid = reshape(repmat(imdb.images.id,[nViews 1]),[1 nInstances*nViews]); 26 | 27 | poolobj = gcp('nocreate'); 28 | if isempty(poolobj), 29 | poolSize = 0; 30 | else 31 | poolSize = poolobj.NumWorkers; 32 | end 33 | parfor (i=1:nInstances, poolSize) 34 | imPath = strrep(fullfile(imdb.imageDir,imdb.images.name{i}),'\',filesep); 35 | im = imread(imPath); 36 | fprintf(' %s\n', imdb.images.name{i}); 37 | for ri = 1:nViews, 38 | [pathstr, name, ext] = fileparts(imdb.images.name{i}); 39 | vl_xmkdir(fullfile(saveDir,pathstr)); 40 | savePath = fullfile(saveDir,fullfile(pathstr,[name '_' num2str(ri) ext])); 41 | if exist(savePath,'file'), continue; end 42 | im_r = 255 - imrotate(255-im,angles(ri),'crop'); 43 | if flips(ri), im_r = fliplr(im_r); end 44 | imwrite(im_r,savePath); 45 | end 46 | end 47 | for i=1:nInstances, 48 | for ri = 1:nViews, 49 | [pathstr, name, ext] = fileparts(imdb.images.name{i}); 50 | imdb2.images.name{(i-1)*nViews+ri} = fullfile(pathstr,[name '_' num2str(ri) ext]); 51 | end 52 | if mod(i,10)==0, fprintf('.'); end 53 | if mod(i,500)==0, fprintf(' [%d/%d]\n',i,nInstances); end 54 | end 55 | save(fullfile(saveDir,'imdb.mat'),'-struct','imdb2'); 56 | 57 | end 58 | -------------------------------------------------------------------------------- /utils/convert_net_format.m: -------------------------------------------------------------------------------- 1 | function net = convert_net_format(net, to_type) 2 | 3 | if strcmpi(to_type,'old'), 4 | for l = 1:numel(net.layers), 5 | if isfield(net.layers{l},'weights'), 6 | net.layers{l}.filters = net.layers{l}.weights{1}; 7 | net.layers{l}.biases = net.layers{l}.weights{2}; 8 | net.layers{l} = rmfield(net.layers{l},'weights'); 9 | end 10 | end 11 | elseif strcmpi(to_type,'new'), 12 | for l=1:numel(net.layers), 13 | if isfield(net.layers{l},'filters'), 14 | net.layers{l}.weights = {net.layers{l}.filters, ... 15 | net.layers{l}.biases}; 16 | net.layers{l} = rmfield(net.layers{l}, ... 17 | {'filters','biases'}); 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /utils/diagnose_confusionmat.m: -------------------------------------------------------------------------------- 1 | function diagnose_confusionmat(gt, pred, labels) 2 | %DIAGNOSE_CONFUSIONMAT diagnose from classification results 3 | % gt:: 4 | % Nx1 ground truth labels 5 | % pred:: 6 | % Nx1 predicted labels 7 | % labels:: 8 | % Nx1 cell, label names 9 | 10 | labels = cellfun(@(s) strrep(s,'_',' '), labels, 'UniformOutput', false); 11 | 12 | C = confusionmat(gt,pred); 13 | S = sum(C,2); 14 | C0 = C.*(1-eye(size(C))); 15 | S0 = sum(C0,2); 16 | 17 | fprintf('\n DIAGNOSIS \n===============\n\n'); 18 | 19 | fprintf('There are %d testing instances coming from %d categories. \n', ... 20 | sum(S), length(S)); 21 | 22 | fprintf('There are %d wrongly classified instances, the overall \naccuracy is %.2f%%. \n\n',... 23 | sum(S0), (1-sum(S0)/sum(S))*100); 24 | 25 | fprintf(' class ::#test #error most-confused-to \n'); 26 | fprintf('--------------------------------------------------\n'); 27 | for c = 1: length(S), 28 | [y,i] = sort(C0(c,:),'descend'); 29 | fprintf('%10s :: %3d %2d(%4.1f%%)', ... 30 | labels{c}(1:min(10,length(labels{c}))), S(c), S0(c), 100*S0(c)/S(c)); 31 | if y(1)>0, 32 | fprintf(' %10s:%3d(%5.1f%%)', ... 33 | labels{i(1)}(1:min(10,length(labels{i(1)}))), y(1), 100*y(1)/S0(c)); 34 | end 35 | 36 | fprintf('\n'); 37 | end 38 | fprintf('\n'); 39 | 40 | C2 = triu(C0+C0'); 41 | [Y,I] = sort(C2(:),'descend'); 42 | [I1, I2] = ind2sub(size(C),I); 43 | nPairs = min(20,max(find(Y,1,'last'))); 44 | 45 | fprintf('Most confused pairs: \n\n'); 46 | fprintf(' pair #error(%%) accum \n'); 47 | fprintf('--------------------------------------------------\n'); 48 | for p = 1:nPairs, 49 | 50 | fprintf('%12s <=>%12s %3d(%4.1f%%) %5.1f%%\n', ... 51 | labels{I1(p)}(1:min(12,length(labels{I1(p)}))), ... 52 | labels{I2(p)}(1:min(12,length(labels{I2(p)}))), ... 53 | Y(p), 100*Y(p)/sum(S0), 100*sum(Y(1:p))/sum(S0)); 54 | end 55 | fprintf('\n'); -------------------------------------------------------------------------------- /utils/dirfun.m: -------------------------------------------------------------------------------- 1 | function dirfun( dir_path, processFn, save_path, imreadFn, file_pattern, save_pattern, cnt_limit ) 2 | % dirfun Apply a function to each file in the directory 3 | % 4 | % dir_path: directory containing images, will be searched recursively 5 | % processFn: function that will be applied to each image found 6 | % save_path: (default:: dir_path) path to save resized images 7 | % imreadFn: (default:: @imread_safe) function used to load images 8 | % file_pattern: (default:: '*') images that will be processed 9 | % save_pattern: (default:: '') e.g. '%02d.png' will save images as 01.png, 02.png, ... 10 | % cnt_limit: (default:: inf) the maximum number of images used in each folder itself 11 | % 12 | % Hang Su 13 | 14 | % if save_path is not specified, overwrite original image 15 | if ~exist('save_path','var') || isempty(save_path), 16 | save_path = dir_path; 17 | end 18 | 19 | % default imreadFn 20 | if ~exist('imreadFn','var') || isempty(imreadFn), 21 | imreadFn = @imread_safe; 22 | end 23 | 24 | % default file pattern 25 | if ~exist('file_pattern','var') || isempty(file_pattern), 26 | file_pattern = '*'; 27 | end 28 | 29 | % default save pattern 30 | if ~exist('save_pattern','var') || isempty(save_pattern), 31 | save_pattern = ''; 32 | end 33 | 34 | % default cnt_limit 35 | if ~exist('cnt_limit','var') || isempty(cnt_limit), 36 | cnt_limit = inf; 37 | end 38 | 39 | if ischar(file_pattern), 40 | file_pattern = {file_pattern}; 41 | end 42 | 43 | % run recursively 44 | update_dir(dir_path, save_path, processFn, imreadFn, file_pattern, save_pattern, cnt_limit); 45 | 46 | end 47 | 48 | function update_dir(cur_dir, cur_save_dir, processFn, imreadFn, file_pattern, save_pattern, cnt_limit) 49 | 50 | file_names = {}; 51 | for i = 1:numel(file_pattern), 52 | files = dir(fullfile(cur_dir, file_pattern{i})); 53 | file_names_cur = {files.name}; 54 | file_names_cur = file_names_cur(~cell2mat({files.isdir})); 55 | file_names = [file_names file_names_cur]; 56 | end 57 | dirs = dir(cur_dir); 58 | dir_names = {dirs.name}; 59 | dir_names = setdiff(dir_names(cell2mat({dirs.isdir})),{'.','..'}); 60 | 61 | if~exist(cur_save_dir,'dir'), mkdir(cur_save_dir); end; 62 | 63 | % do work 64 | im_cnt = 0; 65 | for i=1:numel(file_names), 66 | im = imreadFn(fullfile(cur_dir, file_names{i})); 67 | if isempty(im), 68 | if strcmp(cur_dir, cur_save_dir), 69 | delete(fullfile(cur_dir, file_names{i})); 70 | end 71 | continue; 72 | end; 73 | im = processFn(im); 74 | im_cnt = im_cnt + 1; 75 | 76 | % save (possibly overwriting original image) 77 | if isempty(save_pattern), 78 | imwrite(im,fullfile(cur_save_dir, file_names{i})); 79 | else 80 | if ~isempty(strfind(save_pattern,'%s')), 81 | [~,cur_name] = fileparts(file_names{i}); 82 | cur_name = strrep(save_pattern,'%s',cur_name); 83 | else 84 | cur_name = save_pattern; 85 | end 86 | if ~isempty(strfind(cur_name,'%d')), 87 | cur_name = sprintf(cur_name,im_cnt); 88 | end 89 | imwrite(im,fullfile(cur_save_dir, cur_name)); 90 | end 91 | 92 | if im_cnt >= cnt_limit, break; end 93 | 94 | end 95 | 96 | for d = 1:numel(dir_names), 97 | update_dir(fullfile(cur_dir,dir_names{d}), ... 98 | fullfile(cur_save_dir,dir_names{d}), processFn, imreadFn, file_pattern, save_pattern, cnt_limit); 99 | end 100 | 101 | end 102 | 103 | function im = imread_safe(path) 104 | try 105 | im = imread(path); 106 | catch 107 | warning('Unable to load image: %s', path); 108 | im = []; 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /utils/get_augmentation_matrix.m: -------------------------------------------------------------------------------- 1 | function subWins = get_augmentation_matrix( augmentationType, varargin ) 2 | %GET_AUGMENTATION Build augmentation matrix 3 | % 4 | % augmentationType:: 'nr3' 5 | % 1st field(f|n) indicates whether include flipped copy or not 6 | % 2nd field(s|r) indicates type of region - Square or Rectangle 7 | % 3rd field(1..4) indicates number of levels 8 | % note: 'none', 'ns1', 'nr1' are equivalent 9 | % `width`:: [1, 0.75, 0.5, 1/3] 10 | % width of windows in each layer 11 | 12 | opts.width = [1, 0.75, 0.5, 1/3]; 13 | opts = vl_argparse(opts,varargin); 14 | width = opts.width; 15 | 16 | if strcmp(augmentationType,'none'), 17 | augmentationType = 'ns1'; 18 | end 19 | nLevels = str2double(augmentationType(3)); 20 | if nLevels>length(width), 21 | error('Too many levels.'); 22 | end 23 | if augmentationType(2)=='s', 24 | subWins = [0;1;0;1]; 25 | for l=2:nLevels, 26 | sv = linspace(0,1-width(l),l); 27 | [XX,YY] = meshgrid(sv,sv); 28 | sxy = reshape(permute(cat(3,XX,YY),[3,1,2]),[2,l^2]); 29 | subWins = [subWins [sxy(1,:) ; width(l)*ones(1,l^2) ;sxy(2,:); ... 30 | width(l)*ones(1,l^2)]]; 31 | end 32 | elseif augmentationType(2)=='r', 33 | sv = []; 34 | w = []; 35 | for l=1:nLevels, 36 | sv = [sv linspace(0,1-width(l),l)]; 37 | w = [w width(l)*ones(1,l)]; 38 | end 39 | [XX_sv,YY_sv] = meshgrid(sv,sv); 40 | [XX_w,YY_w] = meshgrid(w,w); 41 | sxy = reshape(permute(cat(3,XX_sv,YY_sv),[3,1,2]),[2,numel(XX_sv)]); 42 | wxy = reshape(permute(cat(3,XX_w,YY_w),[3,1,2]),[2,numel(XX_w)]); 43 | subWins = [sxy(1,:) ; wxy(1,:) ; sxy(2,:) ; wxy(2,:)]; 44 | else 45 | error('Unknow augmentation type: %s', augmentationType); 46 | end 47 | subWins(end+1,:) = 0; 48 | if augmentationType(1)=='f', 49 | subWins = [subWins subWins]; 50 | subWins(end,size(subWins,2)/2+1:end) = 1; 51 | end 52 | 53 | end 54 | 55 | -------------------------------------------------------------------------------- /utils/imread_255.m: -------------------------------------------------------------------------------- 1 | function im = imread_255( path, nChannels ) 2 | %IMREAD_255 A wrapper of imread that always returns image of nChannels 3 | %channels and with single precision values in range [0.0,255.0] 4 | % 5 | 6 | if nargin==1, nChannels = 3; end 7 | 8 | if ~(nChannels==1 || nChannels==3), 9 | error('Unsupported type of image format: %d channels', nChannels); 10 | end 11 | 12 | im = imread(strrep(path,'\',filesep)); 13 | 14 | if size(im,3)~=nChannels, 15 | if size(im,3)==3, 16 | im = rgb2gray(im); 17 | elseif size(im,3)==1, 18 | im = repmat(im,[1,1,3]); 19 | else 20 | error('Unsupported image format: %d channels', size(im,3)); 21 | end 22 | end 23 | 24 | if ~isa(im,'uint8'), 25 | im = im2single(im) * 255; 26 | else 27 | im = single(im); 28 | end 29 | 30 | end 31 | 32 | -------------------------------------------------------------------------------- /utils/loadMesh.m: -------------------------------------------------------------------------------- 1 | function [mesh labels labelsmap] = loadMesh(filestr) 2 | % loads a mesh from filestr, supports obj or off 3 | 4 | file = fopen( strtrim( filestr ), 'rt'); 5 | if file == -1 6 | warning(['Could not open mesh file: ' filestr]); 7 | mesh = []; 8 | return; 9 | end 10 | mesh.filename = strtrim( filestr ); 11 | 12 | if strcmp( filestr(end-3:end), '.off') 13 | line = strtrim(fgetl(file)); 14 | skipline = 0; 15 | if strcmp(line, 'OFF') 16 | line = strtrim(fgetl(file)); 17 | skipline = 2; 18 | else 19 | line = line(4:end); 20 | skipline = 1; 21 | end 22 | [token,line] = strtok(line); 23 | numverts = eval(token); 24 | [token,line] = strtok(line); 25 | numfaces = eval(token); 26 | mesh.V = zeros( 3, numverts, 'single' ); 27 | mesh.F = zeros( 3, numfaces, 'single' ); 28 | 29 | DATA = dlmread(filestr, ' ', skipline, 0); 30 | DATA = DATA(1:numverts+numfaces, :); 31 | mesh.V(1:3, 1:numverts) = DATA(1:numverts, 1:3)'; 32 | mesh.F(1:3, 1:numfaces) = DATA(numverts+1:numverts+numfaces, 2:4)' + 1; 33 | elseif strcmp( filestr(end-3:end), '.obj') 34 | mesh.V = zeros(3, 10^6, 'single'); 35 | mesh.Nv = zeros(3, 10^6, 'single'); 36 | mesh.F = zeros(3, 5*10^6, 'uint32'); 37 | v = 0; 38 | f = 0; 39 | vn = 0; 40 | 41 | while(~feof(file)) 42 | line_type = fscanf(file,'%c',2); 43 | switch line_type(1) 44 | case {'#', 'g'} 45 | fgets(file); 46 | case 'v' 47 | if line_type(2) == 'n' 48 | vn = vn + 1; 49 | normal = fscanf(file, '%f%f%f'); 50 | mesh.Nv(:, vn) = normal; 51 | elseif isspace( line_type(2) ) 52 | v = v + 1; 53 | point = fscanf(file, '%f%f%f'); 54 | mesh.V(:, v) = point; 55 | else 56 | fgets(file); 57 | end 58 | case 'f' 59 | f = f + 1; 60 | face = fscanf(file, '%u%u%u'); 61 | mesh.F(:, f) = face; 62 | otherwise 63 | if feof(file) 64 | break; 65 | end 66 | if isspace(line_type(1)) 67 | fseek(file, -1, 'cof'); 68 | continue; 69 | end 70 | fprintf('last string read: %c%c %s', line_type(1), line_type(2), fgets(file)); 71 | fclose(file); 72 | error('only triangular obj meshes are supported with vertices, normals and faces.'); 73 | end 74 | end 75 | mesh.V = mesh.V(:, 1:v); 76 | mesh.F = mesh.F(:, 1:f); 77 | mesh.Nv = mesh.Nv(:, 1:v); 78 | end 79 | 80 | fclose(file); 81 | 82 | end -------------------------------------------------------------------------------- /utils/modify_net.m: -------------------------------------------------------------------------------- 1 | function net = modify_net(net, layer, varargin) 2 | 3 | opts.mode = []; 4 | opts.loc = []; 5 | opts = vl_argparse(opts,varargin); 6 | 7 | switch opts.mode, 8 | case 'add_layer', 9 | if nargin<2 || isempty(layer), 10 | error('Please provide the layer to be inserted!'); 11 | end 12 | if isempty(opts.loc), 13 | I = 0; 14 | else 15 | I = cellfun(@(x)(strcmp(x.name, opts.loc)), net.layers); 16 | I = find(I); 17 | if numel(I)~=1, 18 | error('Ambiguous location: more than one %s layer!', opts.loc); 19 | end; 20 | end 21 | net.layers = horzcat(net.layers(1:I), ... 22 | layer, ... 23 | net.layers(I+1:end)); 24 | case 'rm_layer', 25 | if isempty(opts.loc), 26 | error('Please indicate a layer to be removed!'); 27 | end 28 | I = cellfun(@(x)(strcmp(x.name, opts.loc)), net.layers); 29 | I = find(I); 30 | if isempty(I), 31 | error('No %s layer found!', opts.loc); 32 | elseif numel(I)~=1, 33 | error('Ambiguous location: more than one %s layer!', opts.loc); 34 | end; 35 | net.layers(I) = []; 36 | otherwise, 37 | fprintf('Empty/unknown action specified: %s\n', opts.mode); 38 | end 39 | -------------------------------------------------------------------------------- /utils/next_samples.m: -------------------------------------------------------------------------------- 1 | function [sample, sampleQueue] = next_samples(sample, sampleQueue, label, nSample, balancingFn) 2 | %NEXT_SAMPLES Shuffling + sampling + balancing + clipping + caching 3 | % 4 | % Hang Su 5 | 6 | if ~exist('sampleQueue', 'var') || isempty(sampleQueue), 7 | sampleQueue = []; 8 | end 9 | 10 | if ~exist('label', 'var') || isempty(label), 11 | label = ones(1,numel(sample)); 12 | end 13 | 14 | if ~exist('nSample', 'var') || isempty(nSample), 15 | nSample = Inf; 16 | end 17 | 18 | if ~exist('balancingFn', 'var') || isempty(balancingFn), 19 | balancingFn = @(v) v; 20 | end 21 | 22 | if isnumeric(balancingFn), 23 | balancingFn = get_default_balancingfn(balancingFn); 24 | end 25 | 26 | if numel(sampleQueue)opts.maxParts, 57 | npar = max(1,npar - 1); 58 | w = ceil(max([n1 n2]./npar)); 59 | npar = ceil([n1 n2]/w); 60 | end 61 | sz1 = [ones(1,npar(1)-1)*w n1-(npar(1)-1)*w]; 62 | sz2 = [ones(1,npar(2)-1)*w n2-(npar(2)-1)*w]; 63 | 64 | % partition data 65 | t = npar(1)*npar(2); 66 | x1cell = cell(1,t); 67 | x2cell = cell(1,t); 68 | distcell = cell(1,t); 69 | if opts.verbose, 70 | ts = tic; 71 | fprintf('[1/3] Partitioning into %dx ((%d,%d),(%d,%d)) parts \n', ... 72 | t, D,w,D,w); 73 | end 74 | for i = 1:t, 75 | i1 = mod(i-1,npar(1))+1; 76 | i2 = floor((i-1)/npar(1))+1; 77 | if withSelf && i2>i1, % skip top-right triangle for self-dist 78 | x1cell{i} = []; 79 | x2cell{i} = []; 80 | else 81 | x1cell{i} = x1(:,(i1-1)*w+(1:sz1(i1))); 82 | x2cell{i} = x2(:,(i2-1)*w+(1:sz2(i2))); 83 | end 84 | if opts.verbose, 85 | if mod(i,10)==0, fprintf('.');end; 86 | if mod(i,200)==0, fprintf(' [%d/%d]\n',i,t); end; 87 | end 88 | end 89 | if opts.verbose, fprintf(' done! (%s)\n', timestr(toc(ts))); end; 90 | 91 | % estimate speed 92 | tmp = rand(1000,1000); 93 | tt=tic;vl_alldist2(tmp,tmp,opts.measure);tc=toc(tt); 94 | if withSelf, 95 | estTime = (D*(n1*n2/2-n1/2)/1e9)*tc; 96 | else 97 | estTime = (D*n1*n2/1e9)*tc; 98 | end 99 | 100 | % real work 101 | %{- 102 | % comment this block for ealier MATLAB versions 103 | pool = gcp('nocreate'); 104 | if isempty(pool) || pool.NumWorkersi1, 133 | dist_block = distcell{(i1-1)*npar(1)+i2}'; 134 | else 135 | dist_block = distcell{i}; 136 | end 137 | Dist((i1-1)*w+(1:sz1(i1)),(i2-1)*w+(1:sz2(i2))) = dist_block; 138 | if opts.verbose, 139 | if mod(i,10)==0, fprintf('.'); end 140 | if mod(i,200)==0, fprintf(' [%d/%d]\n',i,t); end; 141 | end 142 | end 143 | if opts.verbose, fprintf(' done! (%s)\n', timestr(toc(ts))); end; 144 | 145 | end 146 | 147 | function str=timestr(nsecs) 148 | 149 | muls = [60 60]; 150 | % label = {'H','M','S'}; 151 | label = {'','',''}; 152 | tvec = [nsecs]; 153 | 154 | for i=1:numel(muls) 155 | tvec = [floor(tvec(1)/muls(end-i+1)) tvec]; 156 | tvec(2) = mod(tvec(2),muls(end-i+1)); 157 | end 158 | 159 | if tvec(end)~=floor(tvec(end)) 160 | str = sprintf('%02.2f%s',tvec(end),label{end}); 161 | else 162 | str = sprintf('%02d%s',tvec(end),label{end}); 163 | end 164 | for i=2:numel(tvec), 165 | str = sprintf('%02d%s:%s',tvec(end-i+1),label{end-i+1},str); 166 | end 167 | 168 | end 169 | -------------------------------------------------------------------------------- /utils/parfor_progress.m: -------------------------------------------------------------------------------- 1 | function percent = parfor_progress(N) 2 | %PARFOR_PROGRESS Progress monitor (progress bar) that works with parfor. 3 | % PARFOR_PROGRESS works by creating a file called parfor_progress.txt in 4 | % your working directory, and then keeping track of the parfor loop's 5 | % progress within that file. This workaround is necessary because parfor 6 | % workers cannot communicate with one another so there is no simple way 7 | % to know which iterations have finished and which haven't. 8 | % 9 | % PARFOR_PROGRESS(N) initializes the progress monitor for a set of N 10 | % upcoming calculations. 11 | % 12 | % PARFOR_PROGRESS updates the progress inside your parfor loop and 13 | % displays an updated progress bar. 14 | % 15 | % PARFOR_PROGRESS(0) deletes parfor_progress.txt and finalizes progress 16 | % bar. 17 | % 18 | % To suppress output from any of these functions, just ask for a return 19 | % variable from the function calls, like PERCENT = PARFOR_PROGRESS which 20 | % returns the percentage of completion. 21 | % 22 | % Example: 23 | % 24 | % N = 100; 25 | % parfor_progress(N); 26 | % parfor i=1:N 27 | % pause(rand); % Replace with real code 28 | % parfor_progress; 29 | % end 30 | % parfor_progress(0); 31 | % 32 | % See also PARFOR. 33 | % 34 | %Copyright (c) 2011, Jeremy Scheff 35 | %All rights reserved. 36 | % 37 | %Redistribution and use in source and binary forms, with or without 38 | %modification, are permitted provided that the following conditions are 39 | %met: 40 | % 41 | % * Redistributions of source code must retain the above copyright 42 | % notice, this list of conditions and the following disclaimer. 43 | % * Redistributions in binary form must reproduce the above copyright 44 | % notice, this list of conditions and the following disclaimer in 45 | % the documentation and/or other materials provided with the distribution 46 | % 47 | %THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 | %AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 | %IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 | %ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 | %LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 | %CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 | %SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 | %INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 | %CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 | %ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 57 | %POSSIBILITY OF SUCH DAMAGE. 58 | 59 | % By Jeremy Scheff - jdscheff@gmail.com - http://www.jeremyscheff.com/ 60 | 61 | error(nargchk(0, 1, nargin, 'struct')); 62 | 63 | if nargin < 1 64 | N = -1; 65 | end 66 | 67 | percent = 0; 68 | w = 68; % Width of progress bar 69 | 70 | if N > 0 71 | f = fopen('parfor_progress.txt', 'w'); 72 | if f<0 73 | error('Do you have write permissions for %s?', pwd); 74 | end 75 | fprintf(f, '%d\n', N); % Save N at the top of progress.txt 76 | fclose(f); 77 | 78 | if nargout == 0 79 | disp([' 0%[>', repmat(' ', 1, w), ']']); 80 | % fprintf([' 0%%[>', repmat(' ', 1, w), ']']); 81 | end 82 | elseif N == 0 83 | delete('parfor_progress.txt'); 84 | percent = 100; 85 | 86 | if nargout == 0 87 | disp([repmat(char(8), 1, (w+9)), char(10), '100%[', repmat('=', 1, w+1), ']']); 88 | % fprintf([repmat('\b', 1, (w+7)), '100%%[', repmat('=', 1, w+1), ']\n']); 89 | end 90 | else 91 | if ~exist('parfor_progress.txt', 'file') 92 | error('parfor_progress.txt not found. Run PARFOR_PROGRESS(N) before PARFOR_PROGRESS to initialize parfor_progress.txt.'); 93 | end 94 | 95 | f = fopen('parfor_progress.txt', 'a'); 96 | fprintf(f, '1\n'); 97 | fclose(f); 98 | 99 | f = fopen('parfor_progress.txt', 'r'); 100 | progress = fscanf(f, '%d'); 101 | fclose(f); 102 | percent = (length(progress)-1)/progress(1)*100; 103 | 104 | if nargout == 0 105 | perc = sprintf('%3.0f%%', percent); % 4 characters wide, percentage 106 | disp([repmat(char(8), 1, (w+9)), char(10), perc, '[', repmat('=', 1, round(percent*w/100)), '>', repmat(' ', 1, w - round(percent*w/100)), ']']); 107 | % perc = sprintf('%3.0f%%%%', percent); % 4 characters wide, percentage 108 | % fprintf([repmat('\b', 1, (w+7)), perc, '[', repmat('=', 1, round(percent*w/100)), '>', repmat(' ', 1, w - round(percent*w/100)), ']']); 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /utils/parsave.m: -------------------------------------------------------------------------------- 1 | function parsave(filename, structX) 2 | vl_xmkdir(fileparts(filename)); 3 | save(filename,'-struct','structX'); 4 | end 5 | -------------------------------------------------------------------------------- /utils/plotMesh.m: -------------------------------------------------------------------------------- 1 | function h = plotMesh(mesh, style, az, el) 2 | 3 | if nargin < 2 4 | style = 'solid'; 5 | end 6 | 7 | if strcmpi(style, 'mesh') 8 | h = trimesh(mesh.F', mesh.V(1,:)', mesh.V(2,:)', mesh.V(3,:)', 'FaceColor', 'none', 'EdgeColor', 'w', ... 9 | 'AmbientStrength', 0.4, 'EdgeLighting', 'flat'); 10 | set(gcf, 'Color', 'k', 'Renderer', 'OpenGL'); 11 | set(gca, 'Projection', 'perspective'); 12 | axis equal; 13 | axis off; 14 | view(az,el); 15 | elseif strcmpi(style, 'solid') 16 | h = trimesh(mesh.F', mesh.V(1,:)', mesh.V(2,:)' ,mesh.V(3,:)', 'FaceColor', 'w', 'EdgeColor', 'none', ... 17 | 'AmbientStrength', 0.3, 'DiffuseStrength', 0.6, 'SpecularStrength', 0.0, 'FaceLighting', 'flat'); 18 | set(gcf, 'Color', 'w', 'Renderer', 'OpenGL'); 19 | set(gca, 'Projection', 'perspective'); 20 | axis equal; 21 | axis off; 22 | view(az,el); 23 | camlight('HEADLIGHT'); 24 | elseif strcmpi(style, 'solidphong') 25 | mesh = normals(mesh); 26 | h = trimesh(mesh.F', mesh.V(1,:)', mesh.V(2,:)' ,mesh.V(3,:)', 'FaceColor', 'w', 'EdgeColor', 'none', ... 27 | 'AmbientStrength', 0.3, 'DiffuseStrength', 0.6, 'SpecularStrength', 0.0, 'FaceLighting', 'gouraud', ... 28 | 'VertexNormals', -mesh.Nv(1:3,:)', 'BackFaceLighting', 'reverselit'); 29 | set(gcf, 'Color', 'w', 'Renderer', 'OpenGL'); 30 | set(gca, 'Projection', 'perspective'); 31 | axis equal; 32 | axis off; 33 | view(az,el); 34 | camlight('HEADLIGHT'); 35 | elseif strcmpi(style, 'soliddoublesided') 36 | mesh = normals(mesh); 37 | lx = cos(az) * cos(el); 38 | ly = cos(az) * sin(el); 39 | lz = sin(az); 40 | lightdir = [lx ly lz]'; 41 | mesh.C = zeros( size(mesh.V, 2), 3 ); 42 | for i=1:size(mesh.V, 2) 43 | mesh.C(i, :) = .3 + .6 * max( sum( lightdir .* mesh.Nv(:, i) ), sum( -lightdir .* mesh.Nv(:, i) ) ); 44 | end 45 | h = trimesh(mesh.F', mesh.V(1,:)', mesh.V(2,:)' ,mesh.V(3,:)', 'EdgeColor', 'none', ... 46 | 'FaceVertexCData', mesh.C, 'FaceColor', 'interp' ); 47 | set(gcf, 'Color', 'w', 'Renderer', 'OpenGL'); 48 | set(gca, 'Projection', 'perspective'); 49 | axis equal; 50 | axis off; 51 | view(az,el); 52 | camlight; 53 | end 54 | 55 | -------------------------------------------------------------------------------- /utils/plot_confusionmat.m: -------------------------------------------------------------------------------- 1 | function C = plot_confusionmat(gt, pred, labels, savePath, fTitle) 2 | %PLOT_CONFUSIONMAT plot confusion matrix from classification results 3 | % gt:: 4 | % Nx1 ground truth labels 5 | % pred:: 6 | % Nx1 predicted labels 7 | % labels:: 8 | % Nx1 cell, label names 9 | % savePath:: [] 10 | % path to save the figure as pdf file 11 | % if specified, figure will be closed automatically 12 | % fTitle:: 'confusion matrix' 13 | % title of figure 14 | 15 | if ~exist('fTitle','var') || isempty(fTitle), 16 | fTitle = 'confusion matrix'; 17 | end 18 | if ~exist('savePath','var') || isempty(savePath), 19 | savePath = []; 20 | end 21 | 22 | C = confusionmat(gt,pred); 23 | S = sum(C,2); 24 | S(S==0)=1; 25 | C = bsxfun(@rdivide, C, S); 26 | 27 | figure(1); clf; 28 | imagesc(C); colormap(gray); 29 | title(fTitle); 30 | axis square; colorbar; 31 | 32 | labels = cellfun(@(s) strrep(s,'_',' '), labels, 'UniformOutput', false); 33 | 34 | % set(gca, 'XTick', 1:length(labels), 'XTickLabel', labels); 35 | set(gca, 'XTick', 1:length(labels), 'XTickLabel', []); 36 | set(gca, 'YTick', 1:length(labels), 'YTickLabel', labels); 37 | 38 | drawnow; 39 | if ~isempty(savePath), 40 | print(1, savePath, '-dpdf'); 41 | close(1); 42 | end 43 | -------------------------------------------------------------------------------- /utils/python/get_top_classes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import os.path 5 | import argparse 6 | 7 | parser = argparse.ArgumentParser() 8 | parser.add_argument('dataDir') 9 | parser.add_argument('-o','--output') 10 | parser.add_argument('-n','--nlargest',type=int,default=100) 11 | args = parser.parse_args() 12 | 13 | allClasses = filter(lambda s: os.path.isdir(os.path.join(args.dataDir,s)),os.listdir(args.dataDir)) 14 | allClassesCnt = {x: len(os.listdir(os.path.join(args.dataDir,x))) for x in allClasses} 15 | resultClasses = sorted(allClassesCnt,key=allClassesCnt.__getitem__,reverse=True)[:args.nlargest] 16 | 17 | if args.output==None: 18 | for x in resultClasses: 19 | print x 20 | else: 21 | f = open(args.output,'w') 22 | f.write("\n".join(resultClasses)+"\n") 23 | f.close() 24 | -------------------------------------------------------------------------------- /utils/python/keep_first_instances.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import os.path 4 | import sys 5 | 6 | top_n = {'test':20,'train':80} 7 | rootdir = sys.argv[1] 8 | classes = [s for s in os.listdir(rootdir) if os.path.isdir(os.path.join(rootdir,s))] 9 | startidx = {} 10 | startidx['test'] = {'cone': 168, 'cup': 80, 'tent': 164, 'plant': 241, 'bookshelf': 573, 11 | 'toilet': 345, 'dresser': 201, 'bottle': 336, 'laptop': 150, 'stairs': 125, 12 | 'night_stand': 201, 'sofa': 681, 'stool': 91, 'car': 198, 'chair': 890, 13 | 'piano': 232, 'flower_pot': 150, 'bathtub': 107, 'bench': 174, 'table': 393, 14 | 'airplane': 627, 'monitor': 466, 'vase': 476, 'door': 110, 'guitar': 156, 15 | 'lamp': 125, 'glass_box': 172, 'tv_stand': 268, 'person': 89, 'keyboard': 146, 16 | 'curtain': 139, 'mantel': 285, 'bed': 516, 'desk': 201, 'wardrobe': 88, 17 | 'sink': 129, 'radio': 105, 'bowl': 65, 'range_hood': 116, 'xbox': 104} 18 | startidx['train'] = {'cone': 1, 'cup': 1, 'tent': 1, 'plant': 1, 'bookshelf': 1, 19 | 'toilet': 1, 'dresser': 1, 'bottle': 1, 'laptop': 1, 'stairs': 1, 20 | 'night_stand': 1, 'sofa': 1, 'stool': 1, 'car': 1, 'chair': 1, 21 | 'piano': 1, 'flower_pot': 1, 'bathtub': 1, 'bench': 1, 'table': 1, 22 | 'airplane': 1, 'monitor': 1, 'vase': 1, 'door': 1, 'guitar': 1, 23 | 'lamp': 1, 'glass_box': 1, 'tv_stand': 1, 'person': 1, 'keyboard': 1, 24 | 'curtain': 1, 'mantel': 1, 'bed': 1, 'desk': 1, 'wardrobe': 1, 25 | 'sink': 1, 'radio': 1, 'bowl': 1, 'range_hood': 1, 'xbox': 1} 26 | 27 | for (i,c) in enumerate(classes): 28 | for s in top_n.keys(): 29 | curr_path = os.path.join(rootdir,c,s) 30 | files = os.listdir(curr_path) 31 | files.sort() 32 | files_to_rm = files[top_n[s]:] 33 | files_to_keep = files[:top_n[s]] 34 | for f in files_to_rm: 35 | os.remove(os.path.join(rootdir,c,s,f)) 36 | for (idx,f) in enumerate(files_to_keep): 37 | f_new = '{0}_{1:0>4}.off'.format(c,idx+startidx[s][c]) 38 | os.rename(os.path.join(rootdir,c,s,f),os.path.join(rootdir,c,s,f_new)) 39 | print('[{0}/{1}]{2}/{3}: {4} files removed.'.format(i+1,len(classes),c,s,len(files_to_rm))) 40 | -------------------------------------------------------------------------------- /utils/render_views.m: -------------------------------------------------------------------------------- 1 | function ims = render_views( mesh, varargin ) 2 | %RENDER_VIEWS render a 3d shape from multiple views 3 | % mesh:: 4 | % a mesh object containing fileds 5 | % .F 3 x #faces (1-based indexing) 6 | % .V 3 x #vertices 7 | % OR a path to .off file 8 | % `az`:: (default) [0:30:330] 9 | % horizontal viewing angles, use this setting for shapes that are 10 | % upright oriented according to +Z axis! 11 | % `el`:: (default) 30 12 | % vertical elevation, , use this setting for shapes that are 13 | % upright oriented according to +Z axis! 14 | % `use_dodecahedron_views`:: (default) false 15 | % ignores az, el - places cameras on the vertices of a unit 16 | % dodecahedron, rotates them, and produces 80 views. 17 | % use this setting for shapes that are not upright oriented. 18 | % `colorMode`:: (default) 'rgb' 19 | % color mode of output images ('rgb' or 'gray') 20 | % `outputSize`:: (default) 224 21 | % output image size (both dimensions) 22 | % `minMargin`:: (default) 0.1 23 | % minimun margin ratio in output images 24 | % `maxArea`:: (default) 0.3 25 | % maximun area ratio in output images 26 | % `figHandle`:: (default) [] 27 | % handle to existing figure 28 | 29 | opts.az = [0:30:330]; 30 | opts.el = 30; 31 | opts.use_dodecahedron_views = false; 32 | opts.colorMode = 'rgb'; 33 | opts.outputSize = 224; 34 | opts.minMargin = 0.1; 35 | opts.maxArea = 0.3; 36 | opts.figHandle = []; 37 | opts = vl_argparse(opts,varargin); 38 | 39 | if isempty(opts.figHandle) 40 | opts.figHandle = figure; 41 | end 42 | 43 | if opts.use_dodecahedron_views 44 | phi = (1+sqrt(5))/2; 45 | 46 | vertices = [ 47 | 1, 1, 1; 48 | 1, 1, -1; 49 | 1, -1, 1; 50 | 1, -1, -1; 51 | -1, 1, 1; 52 | -1, 1, -1; 53 | -1, -1, 1; 54 | -1, -1, -1; 55 | 56 | 0, 1/phi, phi; 57 | 0, 1/phi, -phi; 58 | 0, -1/phi, phi; 59 | 0, -1/phi, -phi; 60 | 61 | phi, 0, 1/phi; 62 | phi, 0, -1/phi; 63 | -phi, 0, 1/phi; 64 | -phi, 0, -1/phi; 65 | 66 | 1/phi, phi, 0; 67 | -1/phi, phi, 0; 68 | 1/phi, -phi, 0; 69 | -1/phi, -phi, 0; 70 | ]; 71 | 72 | num_vertices = size(vertices,1); 73 | 74 | opts.az = zeros(num_vertices, 1); 75 | opts.el = zeros(num_vertices, 1); 76 | 77 | for i=1:num_vertices 78 | opts.az(i) = (vertices(i,2) / vertices(i,1)); 79 | opts.az(i) = atan(opts.az(i)) / pi * 180 + 180 * (sign(vertices(i,1))==-1); 80 | 81 | opts.el(i) = (vertices(i,3) / sqrt(vertices(i,1) * vertices(i,1) + vertices(i,2) * vertices(i,2))); 82 | opts.el(i) = atan(opts.el(i)) / pi * 180; 83 | end 84 | end 85 | 86 | if ischar(mesh), 87 | if strcmpi(mesh(end-2:end),'off') || strcmpi(mesh(end-2:end),'obj') 88 | mesh = loadMesh(mesh); 89 | else 90 | error('file type (.%s) not supported.',mesh(end-2:end)); 91 | end 92 | end 93 | 94 | if opts.use_dodecahedron_views 95 | ims = cell(1, length(opts.az) * 4); 96 | im_counter = 0; 97 | for i=1:length(opts.az) 98 | plotMesh(mesh,'solid',opts.az(i),opts.el(i)); 99 | for cv=1:4 100 | im_counter = im_counter + 1; 101 | ims{im_counter} = print('-RGBImage', '-r100'); %in case of an error,you have an old matlab version: comment this line and uncomment the following 2 ones 102 | %saveas(opts.figHandle, '__temp__.png'); 103 | %ims{im_counter} = imread('__temp__.png'); 104 | if strcmpi(opts.colorMode,'gray'), ims{im_counter} = rgb2gray(ims{im_counter}); end 105 | ims{im_counter} = resize_im(ims{im_counter}, opts.outputSize, opts.minMargin, opts.maxArea); 106 | camroll(90); 107 | end 108 | end 109 | else 110 | ims = cell(1,length(opts.az)); 111 | for i=1:length(opts.az), 112 | plotMesh(mesh,'solid',opts.az(i),opts.el); 113 | ims{i} = print('-RGBImage', '-r100'); %in case of an error,you have an old matlab version: comment this line and uncomment the following 2 ones 114 | %saveas(opts.figHandle, '__temp__.png'); 115 | %ims{i} = imread('__temp__.png'); 116 | if strcmpi(opts.colorMode,'gray'), ims{i} = rgb2gray(ims{i}); end 117 | ims{i} = resize_im(ims{i}, opts.outputSize, opts.minMargin, opts.maxArea); 118 | end 119 | end 120 | 121 | %delete('__temp__.png'); 122 | 123 | end 124 | 125 | 126 | 127 | 128 | function im = resize_im(im,outputSize,minMargin,maxArea) 129 | 130 | max_len = outputSize * (1-minMargin); 131 | max_area = outputSize^2 * maxArea; 132 | 133 | nCh = size(im,3); 134 | mask = ~im2bw(im,1-1e-10); 135 | mask = imfill(mask,'holes'); 136 | % blank image (all white) is outputed if not object is observed 137 | if isempty(find(mask, 1)), 138 | im = uint8(255*ones(outputSize,outputSize,nCh)); 139 | return; 140 | end 141 | [ys,xs] = ind2sub(size(mask),find(mask)); 142 | y_min = min(ys); y_max = max(ys); h = y_max - y_min + 1; 143 | x_min = min(xs); x_max = max(xs); w = x_max - x_min + 1; 144 | scale = min(max_len/max(h,w), sqrt(max_area/sum(mask(:)))); 145 | patch = imresize(im(y_min:y_max,x_min:x_max,:),scale); 146 | [h,w,~] = size(patch); 147 | im = uint8(255*ones(outputSize,outputSize,nCh)); 148 | loc_start = floor((outputSize-[h w])/2); 149 | im(loc_start(1)+(0:h-1),loc_start(2)+(0:w-1),:) = patch; 150 | 151 | end 152 | -------------------------------------------------------------------------------- /utils/render_views_of_all_meshes_in_a_folder.m: -------------------------------------------------------------------------------- 1 | function render_views_of_all_meshes_in_a_folder(folder, varargin) 2 | % calls 'render_views' for every shape found in the given folder 3 | 4 | opts.ext = '.jpg'; % extension of target files 5 | opts.range = []; % if empty, all found shapes will be rendered, while a range [X:Y] will render shapes in the given range 6 | opts.useUprightAssumption = true; % if true, 12 views will be used to render meshes, otherwise 80 views based on a dodecahedron 7 | opts = vl_argparse(opts, varargin); 8 | 9 | mesh_filenames = [rdir( sprintf('%s\\**\\*.obj', folder) ); rdir( sprintf('%s\\**\\**.off', folder) )]; 10 | 11 | if isempty( opts.range ) 12 | range = 1:length( mesh_filenames ); 13 | else 14 | range = opts.range; 15 | end 16 | 17 | fig = figure('Visible','off'); 18 | for fi=range 19 | fprintf('Loading and rendering input shape %s...', mesh_filenames(fi).name ); 20 | mesh = loadMesh( mesh_filenames(fi).name ); 21 | if isempty(mesh.F) 22 | error('Could not load mesh from file'); 23 | else 24 | fprintf('Done.\n'); 25 | end 26 | if opts.useUprightAssumption 27 | ims = render_views(mesh, 'figHandle', fig); 28 | else 29 | ims = render_views(mesh, 'use_dodecahedron_views', true, 'figHandle', fig); 30 | end 31 | 32 | for ij=1:length(ims) 33 | imwrite( ims{ij}, sprintf('%s_%03d%s', mesh_filenames(fi).name(1:end-4), ij, opts.ext) ); 34 | end 35 | end 36 | close(fig); 37 | -------------------------------------------------------------------------------- /utils/vgg_metric/evalBestThresh.m: -------------------------------------------------------------------------------- 1 | % Copyright (c) 2014, Karen Simonyan 2 | % All rights reserved. 3 | % This code is made available under the terms of the BSD license (see COPYING file). 4 | 5 | % Modified by Aruni Roy Chowdhury 6 | 7 | function [ res, extra ] = evalBestThresh( scores, gt ) 8 | %EVALBESTTHRESH 9 | % finds an optimal threshold - the threshold which maximises the accuracy 10 | 11 | % threshold scores and get the sign 12 | 13 | res = -Inf; 14 | extra.bestThresh = []; 15 | 16 | % thresh loop 17 | for i=1:numel(scores) 18 | 19 | curThresh = scores(i); 20 | class = 2 * (scores >= curThresh) - 1; 21 | 22 | % class-n accuracy 23 | acc = mean(class == gt); 24 | 25 | if acc > res 26 | 27 | res = acc; 28 | extra.bestThresh = curThresh; 29 | end 30 | end 31 | 32 | res = res * 100; 33 | 34 | end 35 | 36 | -------------------------------------------------------------------------------- /utils/vgg_metric/getNegPairs.m: -------------------------------------------------------------------------------- 1 | function neg_pairs = getNegPairs(personID, personList, numPairs) 2 | %GETNEGPAIRS Returns randomly sampled negative face pair indices 3 | % personID(i) has the class label (person) for the i-th sample in the dataset 4 | % 1xN where N=total number of samples/images in dataset 5 | % personList is the list of all unique persons present in the dataset 6 | % 1xP where P=number of persons in dataset 7 | % numPairs is the number of negative pairs to be returned 8 | 9 | rng(65535); 10 | neg_pairs = zeros(2, numPairs); 11 | 12 | for i = 1:numPairs 13 | 14 | % select two different persons 15 | t = randperm(length(personList), 2); 16 | personListIdx1 = personList(t(1)); 17 | personListIdx2 = personList(t(2)); 18 | 19 | % All data indices of that person 20 | idxData1 = find(personID == personListIdx1); 21 | idxData2 = find(personID == personListIdx2); 22 | 23 | % generate random pair of image-indices of that person 24 | neg_pairs(1, i) = idxData1(randi(length(idxData1))); 25 | neg_pairs(2, i) = idxData2(randi(length(idxData2))); 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /utils/vgg_metric/getPCAWhite.m: -------------------------------------------------------------------------------- 1 | function W = getPCAWhite( fv_data, targetDim ) 2 | %GETPCAWHITE Compute PCA-Whitening matrix 3 | % Dependency on pca() function of MATLAB Statistics Toolbox 4 | 5 | % pca() expects data to be row-wise, hence transposed fv_data 6 | [pca_proj, ~, pca_eigvals] = ... 7 | pca(fv_data', 'NumComponents', targetDim, ... 8 | 'Centered',false); 9 | 10 | whiteMat = diag(1./sqrt(pca_eigvals(1:targetDim) + 1e-5)); 11 | W = single(whiteMat * pca_proj'); 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /utils/vgg_metric/getPosPairs.m: -------------------------------------------------------------------------------- 1 | function pos_pairs = getPosPairs(personID, personList, numPairs) 2 | %GETPOSPAIRS Returns randomly sampled positive face pair indices 3 | % personID(i) has the class label (person) for the i-th sample in the dataset 4 | % 1xN where N=total number of samples/images in dataset 5 | % personList is the list of all unique persons present in the dataset 6 | % 1xP where P=number of persons in dataset 7 | % numPairs is the number of positive pairs to be returned 8 | 9 | rng(65535); 10 | pos_pairs = zeros(2, numPairs); 11 | numSamples = length(personList); 12 | 13 | % number of data-samples per person 14 | personNumImg = hist(personID, [1:numSamples]); %#ok 15 | 16 | % IDs of persons that have >= 2 data-samples 17 | personSet = personList(personNumImg >= 2); 18 | 19 | 20 | for i = 1:numPairs 21 | 22 | % select person 23 | pid = personSet(randi(length(personSet))); 24 | 25 | % All image indices of that person 26 | idxData = find(personID == pid); %linear indexing -> position 27 | 28 | % generate random pair of image-indices of that person 29 | t = randperm(length(idxData), 2); 30 | pairIdx1 = t(1); 31 | pairIdx2 = t(2); 32 | pos_pairs(1, i) = idxData(pairIdx1); 33 | pos_pairs(2, i) = idxData(pairIdx2); 34 | 35 | end 36 | end -------------------------------------------------------------------------------- /utils/vgg_metric/initValidBias.m: -------------------------------------------------------------------------------- 1 | function b = initValidBias( faceFeat, W, idxBiasTrainPos, idxBiasTrainNeg ) 2 | %INITVALIDBIAS Initialize bias b on the validation set 3 | % 4 | numTrainBias = size(idxBiasTrainPos,2) + size(idxBiasTrainNeg, 2); 5 | featDiff = W * (faceFeat(:, idxBiasTrainPos(1, :)) - faceFeat(:, idxBiasTrainPos(2, :))); 6 | biasTrainDistPos = sum(featDiff .^ 2, 1); 7 | 8 | featDiff = W * (faceFeat(:, idxBiasTrainNeg(1, :)) - faceFeat(:, idxBiasTrainNeg(2, :))); 9 | biasTrainDistNeg = sum(featDiff .^ 2, 1); 10 | 11 | biasTrainAnno = [ones(1, numTrainBias/2, 'single'), -ones(1, numTrainBias/2, 'single')]; 12 | 13 | [~, extra] = evalBestThresh(-[biasTrainDistPos, biasTrainDistNeg], biasTrainAnno); 14 | b = -extra.bestThresh; 15 | end 16 | 17 | -------------------------------------------------------------------------------- /utils/vgg_metric/trainProj.m: -------------------------------------------------------------------------------- 1 | function [ model ] = trainProj( faceFeat, personID, personList, params ) 2 | %TRAINPROJ Discriminative dimensionality reduction of Fisher Vectors 3 | % params.targetDim Reduced dimension 4 | % params.numPairs total number of +ve and -ve pairs 5 | % params.numIter total number of iterations 6 | % params.W [] or pre-computed 7 | % params.b [] or pre-computed 8 | % params.gamma [] or pre-computed 9 | % params.gammaBias [] or pre-computed 10 | % params.lambda [] or pre-computed 11 | % 12 | % personID(i) has the class label (person) for the i-th sample in the dataset 13 | % 1xN where N=total number of samples/images in dataset 14 | % personList is the list of all unique persons present in the dataset 15 | % 1xP where P=number of persons in dataset 16 | 17 | % TODO - give Validation pos and neg pairs for gamma & gammaBias grid-search 18 | 19 | 20 | % ========================================================================= 21 | % INITIALIZATION 22 | % ========================================================================= 23 | 24 | % Intializations 25 | numPairs = params.numPairs; 26 | t0 = 1; 27 | idxPos = 0; 28 | idxNeg = 0; 29 | numTrainBias = 1e3; % edit to 1e3 30 | 31 | if ~isfield(params,'numIter') || isempty(params.numIter), 32 | numIter = 1e6; 33 | else 34 | numIter = params.numIter; 35 | end 36 | 37 | if ~isfield(params,'gamma') || isempty(params.gamma), 38 | gamma = 0.0001; 39 | else 40 | gamma = params.gamma; 41 | end 42 | 43 | if ~isfield(params,'gammaBias') || isempty(params.gammaBias), 44 | gammaBias = 0.0001; 45 | else 46 | gammaBias = params.gammaBias; 47 | end 48 | 49 | if ~isfield(params,'lambda') || isempty(params.lambda), 50 | lambda = 0.01; 51 | else 52 | lambda = params.lambda; 53 | end 54 | 55 | % Form positive and negative pairs 56 | disp('Forming positive and negative pairs.'); 57 | posPair = getPosPairs(personID, personList, numPairs/2); %2xnumPairs 58 | negPair = getNegPairs(personID, personList, numPairs/2); %2xnumPairs 59 | 60 | 61 | nPos = size(posPair, 2); 62 | nNeg = size(negPair, 2); 63 | 64 | 65 | % Initialize W to PCA-whitening matrix 66 | disp('PCA-whitening.'); 67 | if isempty(params.W) 68 | W = getPCAWhite( faceFeat, params.targetDim ); 69 | else 70 | W = params.W; 71 | end 72 | model.W = W; 73 | 74 | 75 | % Dataset for initializing bias - subset from pos and neg pairs 76 | idxBiasTrainPos = posPair(:, randi(nPos, numTrainBias, 1)); 77 | idxBiasTrainNeg = negPair(:, randi(nNeg, numTrainBias, 1)); 78 | 79 | 80 | % Initialize bias b on validation-set FV differences 81 | % TODO - testing and debugging initValidBias() on real inputs 82 | disp('Initializing bias b.'); 83 | if isempty(params.b) 84 | b = initValidBias(faceFeat, W, idxBiasTrainPos, idxBiasTrainNeg); 85 | else 86 | b = params.b; 87 | end 88 | model.b = b; 89 | 90 | 91 | 92 | % ========================================================================= 93 | % STOCHASTIC GRADIENT DESCENT ON W & BIAS b - VGG code 94 | % ========================================================================= 95 | 96 | % SGD iterations 97 | disp('Beginning stochastic gradient descent.'); 98 | for t = t0:numIter 99 | 100 | if mod(t, 2) == 1 101 | 102 | % positive sample 103 | idxPos = idxPos + 1; 104 | 105 | if idxPos > nPos 106 | idxPos = 1; 107 | end 108 | 109 | % feature vector 110 | featDiff = faceFeat(:, posPair(1, idxPos)) - faceFeat(:, posPair(2, idxPos)); 111 | featDiffProj = W * featDiff; 112 | 113 | % update 114 | if norm(featDiffProj) ^ 2 > b - 1 115 | W = W - (gamma * featDiffProj) * featDiff'; 116 | b = b + gammaBias; 117 | end 118 | 119 | else 120 | 121 | % negative sample 122 | idxNeg = idxNeg + 1; 123 | 124 | if idxNeg > nNeg 125 | idxNeg = 1; 126 | end 127 | 128 | % feature vector 129 | featDiff = faceFeat(:, negPair(1, idxNeg)) - faceFeat(:, negPair(2, idxNeg)); 130 | featDiffProj = W * featDiff; 131 | 132 | % update W 133 | if norm(featDiffProj) ^ 2 < b + 1 134 | W = W + (gamma * featDiffProj) * featDiff'; 135 | b = b - gammaBias; 136 | end 137 | 138 | end 139 | 140 | % regularization 141 | W = (1-gamma*lambda) * W; 142 | 143 | if mod(t,1e4)==0, fprintf('.'); end; 144 | if mod(t,1e5)==0, fprintf(' [%d/%d]\n', t,numIter); end; 145 | 146 | end 147 | disp('Completed.'); 148 | 149 | % ========================================================================= 150 | % SETTING LEARNED MODEL VALUES 151 | % ========================================================================= 152 | 153 | model.W = W; 154 | model.b = b; 155 | model.discrDimRed = true; 156 | 157 | end 158 | 159 | 160 | 161 | % Currently un-used! 162 | function eer = get_val_eer(W) 163 | % validation EER 164 | 165 | valFeatsProj = W * valData.pairFeat; 166 | valDist = sum(valFeatsProj .^ 2, 1); 167 | 168 | [~,~,info] = vl_roc(valData.anno, -valDist); 169 | eer = 1 - info.eer; 170 | 171 | end 172 | 173 | --------------------------------------------------------------------------------