├── Demo.m ├── DemoCofw.m ├── DemoLfw.m ├── DemoOfd.m ├── README.md ├── UCI_OFD ├── DetectionResults │ ├── HPM_HELEN68.txt │ ├── HPM_multi_res_worot_HELEN68.txt │ ├── HPM_multi_res_wrot_HELEN68.txt │ └── HPM_no_occ_HELEN68.txt ├── Evaluate.m ├── Main.m ├── OFD │ ├── 1.jpg │ ├── 10.jpg │ ├── 11.jpg │ ├── 12.jpg │ ├── 14.jpg │ ├── 15.jpg │ ├── 16.jpg │ ├── 17.jpg │ ├── 18.jpg │ ├── 19.jpg │ ├── 20.jpg │ ├── 21.jpg │ ├── 22.jpg │ ├── 23.jpg │ ├── 24.jpg │ ├── 25.jpg │ ├── 26.jpg │ ├── 27.jpg │ ├── 28.jpg │ ├── 3.jpg │ ├── 30.jpg │ ├── 31.jpg │ ├── 32.jpg │ ├── 33.jpg │ ├── 34.jpg │ ├── 35.jpg │ ├── 36.jpg │ ├── 37.jpg │ ├── 38.jpg │ ├── 39.jpg │ ├── 4.jpg │ ├── 40.jpg │ ├── 41.jpg │ ├── 42.jpg │ ├── 50.jpg │ ├── 52.jpg │ ├── 53.jpg │ ├── 54.jpg │ ├── 55.jpg │ ├── 57.jpg │ ├── 58.jpg │ ├── 59.jpg │ ├── 6.jpg │ ├── 60.jpg │ ├── 61.jpg │ ├── 62.jpg │ ├── 63.jpg │ ├── 66.jpg │ ├── 68.jpg │ ├── 72.jpg │ ├── 73.jpg │ ├── 74.jpg │ ├── 75.jpg │ ├── 76.jpg │ ├── 77.jpg │ ├── 78.jpg │ ├── 79.jpg │ ├── 8.jpg │ ├── 80.jpg │ ├── 82.jpg │ ├── 9.jpg │ └── annotations.mat ├── PlotCurves.m ├── ReadDetectionResults.m ├── ReadMe.md ├── ShowAnnotations.m ├── VaryColor.m └── plots │ ├── all faces.pdf │ ├── occluded faces.pdf │ ├── significant occluded faces.pdf │ └── visible faces.pdf ├── cache ├── HPM_OFD_boxes_interval5_incOB0_rot20_10.mat ├── HPM_afw_boxes_interval5_incOB0_ov8_rot15_5.mat ├── HPM_cofw_test_boxes_interval5_incOB0_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOB10_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOB20_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOB30_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn10_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn20_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn30_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn40_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn50_ov7_rot30_6.mat ├── HPM_cofw_test_boxes_interval5_incOBn60_ov7_rot30_6.mat ├── HPM_cofw_train_boxes_interval5_incOB0_ov7_rot0_1.mat ├── HPM_final.mat ├── HPM_final_bbox.mat ├── HPM_lfw_A_B_boxes_interval5_incOB0_ov2_rot30_6.mat ├── HPM_lfw_val_M_boxes_interval5_incOB0_ov2_rot10_5.mat └── HPM_multi_res_final.mat ├── compile.m ├── data ├── AfwTestData.m ├── CofwData.m └── LfwData.m ├── detection ├── BoxesFilename.m ├── BoxesInfo.m ├── DetectDetect.m ├── DetectGivendet.m ├── DetectionWRotation.m ├── FlipBoxesOfFlippedComp.m ├── LocalizationsWORotation.m ├── LocalizationsWRotation.m ├── NmsFaceWRotation.m ├── TestLocalization.m └── featpyramid.m ├── evaluation ├── LandmarkLocalizationEval.m ├── PrintDetectionResToFile.m └── multipie_eval_landmark.m ├── init.m ├── matlabPool ├── closematlabpool.m └── startmatlabpool.m ├── mex_unix ├── bounded_dt.mexa64 ├── bounded_shiftdt.mexa64 ├── bounded_shiftdt_.mexa64 ├── fast_bounded_dt.mexa64 ├── fast_bounded_shifdt.cc ├── fast_bounded_shifdt.mexa64 ├── fast_bounded_shifdt_nc.mexa64 ├── fast_bounded_shifdt_ncw.mexa64 ├── fast_update_score.cc ├── fast_update_score.mexa64 ├── fconv.cc ├── fconv.mexa64 ├── fconvMT.cc ├── fconvblas.cc ├── fconvblas_gcc445.cc ├── features.cc ├── features.mexa64 ├── passmessage.mexa64 ├── reduce.cc ├── reduce.mexa64 ├── resize.cc ├── resize.mexa64 ├── shiftdt.cc ├── shiftdt.mexa64 ├── update_score.cc ├── updatescore.mexa64 └── updatescore_ref.mexa64 ├── photos ├── .DS_Store ├── img1.jpg ├── img2.jpg ├── img3.jpg ├── img4.jpg └── img5.jpg ├── utility ├── ApplyRegression.m ├── ChangeAlpha.m ├── ChangeBiasOfOccKeypoints.m ├── CofwKeypointsToHpmParts.m ├── ComputeReqLevels.m ├── LearnRegression.m ├── LfwKeypointsToHpmParts.m ├── Mapping68kToAfw.m ├── Mapping68kToCofw29.m ├── Mapping68kToLfw.m ├── RotateAround.m ├── RotateFace.m └── RotatePoints.m └── visualization ├── PlotGraphs.m ├── ShowPoints.m ├── VisualizeData.m ├── VisualizeDetectionRes.m ├── VisualizeLocalizationRes.m ├── VisualizeModel.m ├── distinguishable_colors.m └── ellipse.m /Demo.m: -------------------------------------------------------------------------------- 1 | init; 2 | 3 | % Loads pre-trained HPM multi resolution model. 4 | load('cache/HPM_multi_res_final.mat'); 5 | 6 | % Visualizes the model 7 | %disp('Model visualization ...'); 8 | %VisualizeModel(model); 9 | 10 | options = []; 11 | % Sets the parameter alpha to change the bias of the model toward occlusion 12 | % prediction. 13 | % larger value for this parameter --> higher recall of occlusion 14 | options.occ_bias_inc = 0; 15 | model = ChangeBiasOfOccKeypoints(model, options.occ_bias_inc); 16 | 17 | options.cachedir = 'cache/'; 18 | options.figdir = 'cache/figdir/'; 19 | options.experiment_name = 'demo_det'; 20 | options.limited_level = true; 21 | 22 | % Runs the model on 3 rotations (-14, 0, 14) of the test image to 23 | % detect rotated faces. 24 | % More rotations better results, but slower model. 25 | options.rotation_range = 14; 26 | options.rotation_step = 14; 27 | 28 | % Number of HoG cells to be padded to the test image. It is helpful for finding the 29 | % cropped faces. But, larger value will increase the running time. 30 | model.feature_map_padding = 0; 31 | 32 | % nms threshold 33 | options.nms_threshold = 0.4; 34 | 35 | % The scale between the images of two consecutive level of the feature pyramid 36 | % is 2^(1/model.interval). larger values --> more level in feature pyramid --> 37 | % searches over more scales of the image and the results look better 38 | % --> but increasing running time. 39 | options.interval = 5; 40 | model.interval = options.interval;; 41 | 42 | % Based on the size of the faces that we want our model to detect, sets the 43 | % min_scale and max_scale parameters: 44 | 45 | % Sets the high resolution components should be run on which scales of the image 46 | % (or which levels of the the feature pyramid). 47 | % High resolution components are about 100 pixels tall. 48 | for i = 1 : 3 49 | model.min_scale(i) = -1; 50 | % --> no restriction on the maximum face height 51 | model.max_scale(i) = 1; 52 | % --> minimum face height of about 100 pixels. 53 | end 54 | % Sets the low resolution components should be run on which scales of the image. 55 | % (or which levels of the feature pyramid). 56 | % Low resolution components are about 60 pixels tall. 57 | for i = 4 : length(model.components) 58 | model.min_scale(i) = 60/100; 59 | % --> image scale >= 0.6 --> model scale <= 100/60 --> maximum face height = 100/60 * 60 = 100 pixels. 60 | model.max_scale(i) = 1; 61 | % --> image scale <= 1 --> model scale >= 1 --> minimum face height = 60 62 | % With this setting low resolution components detect faces between 60 63 | % to 100 pixels high (eyebrow to chin) (They can detect a little smaller or 64 | % larger faces because of the deformation). 65 | end 66 | % To detect smaller faces we can increase the max_scale parameter up to 2. But, 67 | % it will increase the running time of the model. 68 | 69 | % lower thresh --> higher recall of face detection, also increasing running time! 70 | model.thresh = -0.25; 71 | 72 | % Runs the model in the images in photos directory. 73 | ims = dir('photos/*.jpg'); 74 | ims = [ims; dir('photos/*.png')]; 75 | 76 | startmatlabpool(); 77 | for i = 1 : length(ims) 78 | fprintf('testing on (%d/%d) : %s\n', i, length(ims), ims(i).name); 79 | I = imread(['photos/' ims(i).name]); 80 | siz = size(I); 81 | fprintf('image size is %d %d %d\n', siz(1), siz(2), siz(3)); 82 | clf; imagesc(I); axis image; axis off; drawnow; hold on; 83 | if(size(I, 3) == 1) 84 | colormap(gray); 85 | end 86 | 87 | boxes = DetectionWRotation(model, I, options.nms_threshold, 1, ... 88 | options.rotation_range, ... 89 | options.rotation_step); 90 | for b = boxes, 91 | plot(b.det(b.occ == 1, 1), b.det(b.occ == 1, 2), '.r', ... 92 | 'MarkerSize', 10); 93 | plot(b.det(b.occ == 0, 1), b.det(b.occ == 0, 2), '.g', ... 94 | 'MarkerSize', 10); 95 | end 96 | fprintf('Press any key to continue\n'); 97 | pause; 98 | end 99 | closematlabpool(); 100 | -------------------------------------------------------------------------------- /DemoCofw.m: -------------------------------------------------------------------------------- 1 | %%%%% Note: for more info about some of the parameters, check the comments 2 | %%%%% of Demo.m file. 3 | 4 | function DemoCofw() 5 | init; 6 | 7 | % Loads pre-trained HPM model for localization. 8 | load('cache/HPM_final.mat'); 9 | 10 | % Visualizes the model 11 | disp('Model visualization ...'); 12 | VisualizeModel(model); 13 | 14 | options = []; 15 | options.occ_bias_inc = 0; 16 | 17 | options.interval = 5; 18 | model.interval = options.interval;; 19 | 20 | model.feature_map_padding = 0; 21 | 22 | options.cachedir = 'cache/'; 23 | options.figdir = 'cache/figdir/'; 24 | options.experiment_name = 'HPM'; 25 | 26 | % index of eyes keypoints in the ground-truth for computing 27 | % the distance between centers of eyes. 28 | options.left_eye_inds = 17; 29 | options.right_eye_inds = 18; 30 | 31 | % Highest score configuration with at least options.overlap_threshold 32 | % with the gound-truth bounding box will be returned as a solution. 33 | options.overlap_threshold = 0.7; 34 | % The overlap of bounding box of options.keypoint_subset keypoints with the ground truth bbox 35 | % will be computed and the best solution with at least overlap_threshold will be returned. 36 | % since COFW bounding boxes are tight bounding boxes around its 29 keypoints, 37 | % the bounding box of non-jaw keypoints should be used. 38 | options.keypoint_subset = 1 : 52; 39 | 40 | % Minimum level of feature pyramid that the model is run. Since, running the 41 | % model on the low level of feature pyramid (corresponding to upsampled image) 42 | % is computationaly expensive, we can restrict the minimum level. 43 | options.min_level = 1; 44 | 45 | 46 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 47 | fprintf('Landmark localization on the validation data (COFW train data) ...\n'); 48 | % Runs the model on the validation data and use the 68 landmark localization 49 | % resutls to learn a mapping from 68 keypoints to 29 (COFW) keypoints. 50 | % Also in landmark localization problem we are given the boundig box of 51 | % keypoints, so we know the rough scale of the face. Therefore, we don't need 52 | % to run the model on all the levels of the feature pyramid. 53 | % We can run the model on validation data for all or most of the levels and 54 | % learns the range of boundig box sizes on the fired levels. (bounding boxes 55 | % of validation data and test data should be similar) 56 | 57 | % We rotate the validation data to be vertical, so we don't need to 58 | % run the model for different rotations of them. 59 | options.rotation_range = 0; 60 | options.rotation_step = 1; 61 | 62 | % Images will be cropped bye option.pad_ratio padding around the bounding boxes, 63 | % and algorithm will be run on the cropped images. 64 | options.pad_ratio = 0.1; 65 | 66 | % Runs the model on all the levels for the validation data. 67 | options.limited_level = false; 68 | 69 | options.cofw_train_data_file = '../databases/COFW_train.mat'; 70 | options.cofw_train_images_dir = '../databases/cofw_train/'; 71 | 72 | validation_data_name = 'cofw_train'; 73 | options.test_name = validation_data_name; 74 | 75 | try 76 | load([options.cachedir validation_data_name]); 77 | catch 78 | validation = CofwData(options.cofw_train_data_file, ... 79 | options.cofw_train_images_dir, 1); 80 | save([options.cachedir validation_data_name], 'validation'); 81 | end 82 | %VisualizeData(validation); 83 | 84 | % Runs model on the validation data 85 | validation_boxes = LocalizationsWORotation(validation, model, options); 86 | %VisualizeLocalizationRes(validation_boxes, 'det68', 'occ68', validation, 'cofw_train', options.figdir, 1, 1, 1); 87 | 88 | % Sets the minimum and maximum size of the ground-truth boxes on the 89 | % levels that model is fired, based on the validation data. 90 | [model.min_bbox, model.max_bbox, model.min_bbox68, model.max_bbox68] = ... 91 | ComputeReqLevels(validation, validation_boxes, model.interval); 92 | save('cache/HPM_final_bbox.mat', 'model'); 93 | 94 | % Computes 29 keypoints locations using nearest keypoint(s). 95 | for i = 1 : length(validation_boxes) 96 | [validation_boxes{i}.det_sim_29, validation_boxes{i}.occ29] = ... 97 | Mapping68kToCofw29(validation_boxes{i}.det68, ... 98 | validation_boxes{i}.occ68); 99 | end 100 | 101 | [errors, precision, recall] = LandmarkLocalizationEval( ... 102 | validation_boxes, 'det_sim_29', 'occ29', validation, ... 103 | options.left_eye_inds, options.right_eye_inds); 104 | %VisualizeLocalizationRes(validation_boxes, 'det_sim_29', 'occ29', validation, 'cofw_train', options.figdir, 1, 1, 1); 105 | 106 | % Learns mapping from 68 keypoints to 29 keypoints. 107 | mapping_2_hpm_parts = CofwKeypointsToHpmParts(); 108 | save_c68_to_29_add = 'cache/c68to29'; 109 | try 110 | load(save_c68_to_29_add, 'c68to29'); 111 | catch 112 | c68to29 = LearnRegression(validation, validation_boxes, errors, ... 113 | model, mapping_2_hpm_parts); 114 | save(save_c68_to_29_add, 'c68to29'); 115 | end 116 | model.c68to29 = c68to29; 117 | 118 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 119 | % Benchmarks the model on the COFW test data. 120 | fprintf('Landmark localization on the COFW test data ...\n'); 121 | options.test_name = 'cofw_test'; 122 | options.limited_level = true; 123 | options.rotation_step = 6; 124 | options.rotation_range = 30; 125 | options.pad_ratio = 0.2; 126 | model.feature_map_padding = 0; 127 | 128 | options.cofw_test_data_file = '../databases/COFW_test.mat'; 129 | options.cofw_test_images_dir = '../databases/cofw_test/'; 130 | test_data_name = 'cofw_test'; 131 | try 132 | load([options.cachedir test_data_name]); 133 | catch 134 | test = CofwData(options.cofw_test_data_file, ... 135 | options.cofw_test_images_dir, 0); 136 | save([options.cachedir test_data_name], 'test'); 137 | end 138 | %VisualizeData(test); 139 | 140 | options.occ_bias_inc = -0.1; 141 | modelo = model; 142 | model = ChangeBiasOfOccKeypoints(model, options.occ_bias_inc); 143 | 144 | %profile on; 145 | boxes = LocalizationsWRotation(test, model, options); 146 | %p = profile('info'); 147 | %profsave(p, [options.cachedir 'profile_test_cofw']); 148 | %VisualizeLocalizationRes(boxes, 'det68', 'occ68', test, 'cofw_test', options.figdir, 1, 1, 0); 149 | 150 | % computes 29 keypoints locations using nearest keypoint(s). 151 | for i = 1 : length(boxes) 152 | if ~isempty(boxes{i}) 153 | [boxes{i}.det_sim_29, boxes{i}.occ29] = ... 154 | Mapping68kToCofw29(boxes{i}.det68, boxes{i}.occ68); 155 | boxes{i}.det29 = ApplyRegression(boxes{i}, model.c68to29); 156 | end 157 | end 158 | 159 | [errors, precision, recall] = LandmarkLocalizationEval( ... 160 | boxes, 'det29', 'occ29', test, options.left_eye_inds, ... 161 | options.right_eye_inds); 162 | % Prints results. 163 | ave_errors = nanmean(errors); 164 | failure_rate = sum(errors > 0.1) / length(errors) * 100; 165 | fprintf('Average Error: %.3f, Failure rate(at the thresh 0.1): %.2f%% ', ave_errors, failure_rate); 166 | fprintf('(normalized by the distance between the centers of eyes)\n'); 167 | fprintf('recall: %.3f precision %.3f\n', recall, precision); 168 | fprintf('number of nan: %d\n', sum(isnan(errors))); 169 | 170 | max_to_show = 5; 171 | crop_images = 0; 172 | show_groundtruth = 0; 173 | show_keypoint_num = 0; 174 | %VisualizeLocalizationRes(boxes, 'det29', 'occ29', test, 'cofw_test', ... 175 | % options.figdir, crop_images, show_groundtruth, ... 176 | % show_keypoint_num, errors, max_to_show); 177 | VisualizeLocalizationRes(boxes, 'det68', 'occ68', test, 'cofw_test', ... 178 | options.figdir, crop_images, show_groundtruth, ... 179 | show_keypoint_num, errors, max_to_show); 180 | 181 | fprintf(['Press any key to change the alpha parameter and compute' ... 182 | ' precision/recall curve for occlusion prediction.\n']); 183 | pause; 184 | 185 | ChangeAlpha(modelo, test, options.test_name, options); 186 | -------------------------------------------------------------------------------- /DemoLfw.m: -------------------------------------------------------------------------------- 1 | %%%%% Note: for more info about some of the parameters, check the comments 2 | %%%%% of Demo.m and DemoCofw.m files. 3 | 4 | function demo_lfw() 5 | init; 6 | 7 | % Loads pre-trained HPM model for localization. 8 | load('cache/HPM_final'); 9 | 10 | % Visualizes the model 11 | disp('Model visualization ...'); 12 | VisualizeModel(model); 13 | 14 | options = []; 15 | options.occ_bias_inc = 0; 16 | model = ChangeBiasOfOccKeypoints(model, options.occ_bias_inc); 17 | 18 | options.interval = 5; 19 | model.interval = options.interval;; 20 | 21 | model.feature_map_padding = 0; 22 | 23 | options.cachedir = 'cache/'; 24 | options.figdir = 'cache/figdir/'; 25 | options.experiment_name = 'HPM'; 26 | 27 | options.limited_level = false; 28 | options.rotation_range = 10; 29 | options.rotation_step = 5; 30 | options.overlap_threshold = 0.2; 31 | 32 | options.keypoint_subset = 1 : 68; 33 | options.pad_ratio = 0; 34 | options.lfw_file_add = '../databases/lfw_ffd_ann.txt'; 35 | options.lfw_dir = '../databases/lfw/'; 36 | 37 | options.left_eye_inds = 1 : 2; 38 | options.right_eye_inds = 7 : 8; 39 | 40 | % --------------------------------------------------------------------------- 41 | % Runs HPM on the validation data (subset of LFW images which starts with M!). 42 | options.test_name = 'lfw_val_M'; 43 | options.min_level = ceil(model.interval / 2); 44 | try 45 | load([options.cachedir options.test_name]); 46 | catch 47 | validation_data = LfwData(options.lfw_file_add, options.lfw_dir, 'M'); 48 | save([options.cachedir options.test_name], 'validation_data'); 49 | end 50 | 51 | validation_boxes = LocalizationsWORotation(validation_data, model, options); 52 | assert(length(validation_data) == length(validation_boxes)); 53 | %VisualizeLocalizationRes(validation_boxes, 'det68', 'occ68', validation_data, 'lfw_M', options.figdir, 1, 1, 0); 54 | 55 | % Based on ground truth bounding boxes finds subset of levels that 56 | % we need to run the model based on the size of bounding box 57 | [model.min_bbox, model.max_bbox] = ComputeReqLevels( ... 58 | validation_data, validation_boxes, model.interval); 59 | 60 | % --------------------------------------------------------------------------- 61 | % Runs HPM on the validation data (subset of LFW images which starts with A or B!). 62 | options.test_name = 'lfw_A_B'; 63 | filename = BoxesFilename(options); 64 | 65 | options.limited_level = true; 66 | options.rotation_step = 6; 67 | options.rotation_range = 30; 68 | options.min_level = 1; 69 | 70 | try 71 | load([options.cachedir options.test_name]); 72 | catch 73 | test = LfwData(options.lfw_file_add, options.lfw_dir, 'AB'); 74 | save([options.cachedir options.test_name], 'test'); 75 | end 76 | 77 | fprintf('Landmark localization on the LFW test data ...\n'); 78 | boxes = LocalizationsWRotation(test, model, options); 79 | 80 | for i = 1 : length(boxes) 81 | if ~isempty(boxes{i}) 82 | [boxes{i}.det_lfw, boxes{i}.occ_lfw] = Mapping68kToLfw(boxes{i}.det68, boxes{i}.occ68); 83 | end 84 | end 85 | 86 | filename = BoxesFilename(options); 87 | save(filename, 'boxes', 'test'); 88 | 89 | [errors, precision, recall] = LandmarkLocalizationEval( ... 90 | boxes, 'det_lfw', 'occ_lfw', test, options.left_eye_inds, ... 91 | options.right_eye_inds); 92 | % Prints results. 93 | ave_errors = nanmean(errors); 94 | failure_rate = sum(errors > 0.1) / length(errors) * 100; 95 | fprintf('Evaluation %s\n', filename(7 : end)); 96 | fprintf('Results on all faces, normalized by the distance between centers of eyes\n'); 97 | fprintf('Average Error: %.3f, Failure rate(at thresh 0.1): %.2f%%\n', ave_errors, failure_rate); 98 | 99 | % Prints view on frontal view faces. 100 | view_points = [test.headpose]; 101 | front_view = [view_points == -1 | view_points == 0 | view_points == 1]; 102 | errors_fv = errors(front_view); 103 | ave_errors = nanmean(errors_fv); 104 | failure_rate = sum(errors_fv > 0.1) / length(errors_fv) * 100; 105 | 106 | fprintf('------------------------------\n'); 107 | fprintf('Results on front view faces, normalized by the distance between centers of eyes\n'); 108 | fprintf('Average Error: %.3f, Failure rate(at thresh 0.1): %.2f%%\n', ave_errors, failure_rate); 109 | 110 | max_to_show = 5; 111 | crop_images = 0; 112 | show_groundtruth = 0; 113 | show_keypoint_num = 0; 114 | 115 | VisualizeLocalizationRes(boxes, 'det_lfw', 'occ_lfw', test, 'cofw_test', ... 116 | options.figdir, crop_images, show_groundtruth, ... 117 | show_keypoint_num, errors, max_to_show); 118 | %VisualizeLocalizationRes(boxes, 'det68', 'occ68', test, 'cofw_test', ... 119 | % options.figdir, crop_images, show_groundtruth, ... 120 | % show_keypoint_num, errors, max_to_show); 121 | 122 | -------------------------------------------------------------------------------- /DemoOfd.m: -------------------------------------------------------------------------------- 1 | %%%%% Note: for more info about some of the parameters, check the comments 2 | %%%%% of Demo.m file. 3 | 4 | init; 5 | 6 | % Loads pre-trained multi-resolution HPM model. 7 | load('cache/HPM_multi_res_final.mat'); 8 | 9 | % Visualizes the model 10 | disp('Model visualization ...'); 11 | VisualizeModel(model); 12 | 13 | options = []; 14 | options.occ_bias_inc = 0; 15 | model = ChangeBiasOfOccKeypoints(model, options.occ_bias_inc); 16 | 17 | options.rotation_range = 20; 18 | options.rotation_step = 10; 19 | 20 | model.feature_map_padding = 0; 21 | 22 | options.nms_threshold = 0.4; 23 | 24 | options.cachedir = 'cache/'; 25 | options.figdir = 'cache/figdir/'; 26 | options.experiment_name = 'HPM'; 27 | options.test_name = 'OFD'; 28 | 29 | options.interval = 5; 30 | model.interval = options.interval;; 31 | 32 | for i = 1 : 3 33 | model.min_scale(i) = -1; 34 | model.max_scale(i) = 1; 35 | end 36 | for i = 4 : length(model.components) 37 | model.min_scale(i) = 60/100; 38 | model.max_scale(i) = 1; 39 | end 40 | 41 | model.thresh = -0.5; 42 | 43 | filename = BoxesFilename(options); 44 | try 45 | load(filename); 46 | catch 47 | save_boxes_add = [options.cachedir 'boxes_' filename(7 : end) '/']; 48 | if(~exist(save_boxes_add, 'dir')) 49 | mkdir(save_boxes_add); 50 | end 51 | 52 | ims = dir('UCI_OFD/OFD/*.jpg'); 53 | startmatlabpool(); 54 | gt = []; 55 | for i = 1 : length(ims) 56 | gt(i).im = ['UCI_OFD/OFD/' ims(i).name]; 57 | gt(i).id = ims(i).name(1:end-4); 58 | end 59 | %profile on; 60 | for i = 1 : length(ims) 61 | fprintf('testing on (%d/%d) : %s\n', i, length(ims), ims(i).name); 62 | I = imread(gt(i).im); 63 | box_filename = [save_boxes_add 'box_' gt(i).id]; 64 | try 65 | load(box_filename, 'bs'); 66 | fprintf('Saved result loaded.\n'); 67 | catch 68 | siz = size(I); 69 | fprintf('image size is %d %d %d\n', siz(1), siz(2), siz(3)); 70 | bs = DetectionWRotation( ... 71 | model, I, options.nms_threshold, 1, ... 72 | options.rotation_range, options.rotation_step); 73 | save(box_filename, 'bs'); 74 | end 75 | boxes{i} = bs; 76 | for j = 1 : length(boxes{i}) 77 | boxes{i}(j).id = ims(i).name(1:end-4); 78 | end 79 | % Visualizes the detections. 80 | clf; imagesc(I); axis image; axis off; drawnow; hold on; 81 | if(size(I, 3) == 1) 82 | colormap(gray); 83 | end 84 | 85 | for b = boxes{i} 86 | plot(b.det(b.occ == 1, 1), b.det(b.occ == 1, 2), ... 87 | '.r', 'MarkerSize', 10); 88 | plot(b.det(b.occ == 0, 1), b.det(b.occ == 0, 2), ... 89 | '.g', 'MarkerSize', 10); 90 | drawnow; 91 | end 92 | end 93 | %p = profile('info'); 94 | %profsave(p, [options.cachedir 'profile_test_ofd']); 95 | 96 | save(filename, 'boxes', 'gt'); 97 | rmdir(save_boxes_add, 's'); 98 | end 99 | 100 | confidence = []; 101 | BB = zeros(4, 0); 102 | ids = cell(0, 1); 103 | for i = 1 : length(boxes) 104 | for j = 1 : length(boxes{i}) 105 | xy = boxes{i}(j).xy; 106 | if(size(xy, 1) == 1) 107 | BB(:, end+1) = xy; 108 | elseif(size(xy, 1) == 7) 109 | bb = [min(xy(:, 1)), min(xy(:, 2)), ... 110 | max(xy(:, 3)), max(xy(:, 4))]; 111 | sc = bb(3) - bb(1) + bb(4) - bb(2); 112 | bb(1) = bb(1) + sc/15; 113 | bb(2) = bb(2) + sc/20; 114 | bb(3) = bb(3) - sc/15; 115 | bb(4) = bb(4) - sc/20; 116 | BB(:, end+1) = bb; 117 | else 118 | BB(:, end+1) = [min(xy(:, 1)), min(xy(:, 2)), ... 119 | max(xy(:, 3)), max(xy(:, 4))]; 120 | end 121 | confidence(end+1) = boxes{i}(j).s; 122 | ids{end + 1} = boxes{i}(j).id; 123 | boxes{i}(j).BB = BB(:, end); 124 | end 125 | end 126 | 127 | save_res_add = 'cache/HPM_multi_res_HELEN68_PASCAL_OFD.txt'; 128 | PrintDetectionResToFile(BB', ids, confidence, save_res_add); 129 | 130 | cur_dir = pwd; 131 | cd 'UCI_OFD' 132 | Main([cur_dir '/' save_res_add], '*Multi-resolution HPM'); 133 | cd(cur_dir); 134 | 135 | if 1 136 | fprintf('Visualizing the detection results ...\n'); 137 | save_vis_res = [options.figdir options.experiment_name '_' ... 138 | options.test_name '/']; 139 | max_to_show = 5; 140 | figure; 141 | VisualizeDetectionRes(gt, boxes, save_vis_res, -0.3, max_to_show); 142 | end 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Occlusion Coherence: Detecting and Localizing Occluded Faces 2 | This is the code for the method described in: 3 | 4 | [Golnaz Ghiasi, Charless Fowlkes, "Occlusion Coherence: Detecting and Localizing Occluded Faces" , Technical Report, June 2015, arXiv:1506.08347](http://arxiv.org/pdf/1506.08347.pdf) 5 | 6 | [Golnaz Ghiasi, Charless Fowlkes, "Occlusion Coherence: Localizing Occluded Faces with a Hierarchical Deformable Part Model" , (CVPR) 2014](http://www.ics.uci.edu/~gghiasi/papers/gf-cvpr14.pdf) 7 | 8 | 9 | This library is written in Matlab, and is based on the following two works: 10 | 11 | * [Face Detection, Pose Estimation and Landmark Localization in the Wild](http://www.ics.uci.edu/~xzhu/face/) 12 | 13 | 14 | * [Articluated Pose Estimation with Flexible Mixtures of Parts](http://www.ics.uci.edu/~yyang8/research/pose/index.html) 15 | 16 | 17 | ---- 18 | 19 | ## Quick start guide 20 | 21 | This code is tested on Linux. Pre-compiled Mex files for linux are inlcuded. 22 | 23 | ### Download and compile 24 | ``` sh 25 | cd PATH 26 | git clone git://github.com/golnazghiasi/hpm-detection-code.git 27 | cd hpm-detection-code/ 28 | matlab 29 | >> compile 30 | ``` 31 | 32 | 33 | ### Runs face detection using pre-trained model on the images in the photo directory. 34 | ``` sh 35 | cd PATH/hpm-detection-code/ 36 | matlab 37 | >> Demo 38 | ``` 39 | 40 | ### Benchmarks landmark localization on the COFW test data. 41 | Download COFW data: 42 | ``` sh 43 | cd PATH 44 | mkdir databases/ 45 | cd databases/ 46 | wget http://www.vision.caltech.edu/xpburgos/ICCV13/Data/COFW.zip 47 | unzip COFW.zip 48 | mv common/xpburgos/behavior/code/pose/COFW_test.mat . 49 | mv common/xpburgos/behavior/code/pose/COFW_train.mat . 50 | ``` 51 | 52 | ``` sh 53 | cd PATH/hpm-detection-code/ 54 | matlab 55 | >> DemoCofw 56 | ``` 57 | This will load the pre-computed results. To run the detection code on the COFW test dataset, 58 | remove or rename the following files: 59 | ``` sh 60 | PATH/hpm-detection-code/cache/HPM_cofw_* 61 | PATH/hpm-detection-code/cache/changealpha/* 62 | ``` 63 | 64 | ### Benchmarks face detection on the UCI-OFD test data. 65 | ``` sh 66 | cd PATH/hpm-detection-code/ 67 | matlab 68 | >> DemoOfd 69 | ``` 70 | This will load the pre-computed results. To run the detection code on the UCI-OFD dataset, 71 | remove or rename the following file: 72 | ``` sh 73 | PATH/hpm-detection-code/cache/HPM_OFD_* 74 | ``` 75 | 76 | ### Benchmarks landmark localization on LFW test data. 77 | Note: our model is trained on the front view training data, while this dataset 78 | has many side view faces. 79 | 80 | Download LFW data: 81 | ``` sh 82 | cd PATH/databases/ 83 | wget http://www.vision.ee.ethz.ch/~mdantone/datasets/lfw_ffd_ann.txt 84 | wget http://vis-www.cs.umass.edu/lfw/lfw.tgz 85 | tar zxvf lfw.tgz 86 | ``` 87 | 88 | ``` sh 89 | cd PATH/hpm-detection-code/ 90 | matlab 91 | >> DemoLFW 92 | ``` 93 | 94 | This will load the pre-computed results. To run the detection code on the LFW test dataset, 95 | remove or rename the following files: 96 | ``` sh 97 | PATH/hpm-detection-code/cache/HPM_lfw_* 98 | ``` 99 | 100 | ---- 101 | 102 | ### Issues, Questions, Congratulations, etc 103 | 104 | Please contact "gghiasi @ ics.uci.edu" 105 | 106 | --- - 107 | **Copyright (C) 2015 Golnaz Ghiasi, Charless Fowlkes** 108 | 109 | Permission is hereby granted, free of charge, to any person obtaining 110 | a copy of this software and associated documentation files (the 111 | "Software"), to deal in the Software without restriction, including 112 | without limitation the rights to use, copy, modify, merge, publish, 113 | distribute, sublicense, and/or sell copies of the Software, and to 114 | permit persons to whom the Software is furnished to do so, subject to 115 | the following conditions: 116 | 117 | The above copyright notice and this permission notice shall be 118 | included in all copies or substantial portions of the Software. 119 | 120 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 121 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 122 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 123 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 124 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 125 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 126 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 127 | 128 | -------------------------------------------------------------------------------- /UCI_OFD/Evaluate.m: -------------------------------------------------------------------------------- 1 | % This method computes Precision-Recall curves of face detection on (a) all the faces, 2 | % (b) ovisible subset, (c) ccluded subset and (d) significant occluded subset. 3 | % This method computes Precsion-Recall curves for all faces, visible 4 | % faces, occluded faces and significant occluded faces subsets of the dataset. 5 | % It is based on the detection evaluation of Pascal VOC toolbox. 6 | % http://pascallin.ecs.soton.ac.uk/challenges/VOC/voc2011/VOCdevkit_25-May-2011.tar 7 | 8 | function [rec, prec, ap, rec_o, prec_o, ap_o, rec_so, prec_so, ap_so, rec_v, prec_v, ap_v, confidence] = Evaluate(gt, gtids, BB, ids, confidence, min_overlap) 9 | [~, si] = sort(-confidence); 10 | ids = ids(si); 11 | BB = BB(:, si); 12 | 13 | nd = length(confidence); 14 | % True positive 15 | tp = zeros(nd, 1); 16 | % False positive 17 | fp = zeros(nd, 1); 18 | % True positive for an occluded face 19 | tp_o = zeros(nd, 1); 20 | % True positive for a visible face 21 | tp_v = zeros(nd, 1); 22 | % True positive for a significantly occluded face 23 | tp_so = zeros(nd, 1); 24 | 25 | for d = 1 : nd 26 | % Finds ground truth image. 27 | i = []; 28 | for j = 1 : length(gtids) 29 | if strcmp(gtids{j}, ids{d}) 30 | i = j; 31 | break; 32 | end 33 | end 34 | if isempty(i) 35 | error('Unrecognized image "%s"', ids{d}); 36 | elseif length(i)>1 37 | error('Multiple image with id: "%s"', ids{d}); 38 | end 39 | 40 | % Assigns detection to ground truth object if any. 41 | bb=BB(:, d); 42 | ovmax = -inf; 43 | for j = 1 : size(gt(i).BB, 2) 44 | bbgt = gt(i).BB(:, j); 45 | bi = [max(bb(1), bbgt(1)) ; max(bb(2), bbgt(2)) ; min(bb(3), bbgt(3)) ; min(bb(4), bbgt(4))]; 46 | iw = bi(3) - bi(1) + 1; 47 | ih = bi(4) - bi(2) + 1; 48 | if iw > 0 && ih > 0 49 | % Computes overlap as area of intersection / area of union. 50 | ua = (bb(3) - bb(1) + 1) * (bb(4) - bb(2) + 1) + ... 51 | (bbgt(3) - bbgt(1) + 1) * (bbgt(4) - bbgt(2) + 1) -... 52 | iw * ih; 53 | ov = iw * ih / ua; 54 | if ov > ovmax 55 | ovmax = ov; 56 | jmax = j; 57 | end 58 | end 59 | end 60 | % Assigns detection as true positive/don't care/false positive. 61 | if ovmax >= min_overlap 62 | if ~gt(i).det(jmax) 63 | % True positive 64 | tp(d) = 1; 65 | gt(i).det(jmax) = true; 66 | if(gt(i).occ(jmax) > 0) 67 | % True positive for an occluded face 68 | tp_o(d) = 1; 69 | end 70 | if(gt(i).occ(jmax) > 1) 71 | % True positive for a significantly occluded face 72 | tp_so(d) = 1; 73 | end 74 | % True positive for a visible face 75 | if(gt(i).occ(jmax) == 0) 76 | tp_v(d) = 1; 77 | end 78 | else 79 | % False positive (multiple detection) 80 | fp(d) = 1; 81 | end 82 | else 83 | % False positive 84 | fp(d) = 1; 85 | end 86 | end 87 | 88 | npos_v = 0; 89 | npos_so = 0; 90 | npos_o = 0; 91 | npos = 0; 92 | for i = 1 : length(gtids) 93 | npos_v = npos_v + sum(gt(i).occ == 0); 94 | npos_o = npos_o + sum(gt(i).occ > 0); 95 | npos_so = npos_so + sum(gt(i).occ > 1); 96 | npos = npos + length(gt(i).occ); 97 | end 98 | 99 | [rec_v, prec_v, ap_v] = ComputePrecRec(fp, tp_v, npos_v); 100 | [rec_so, prec_so, ap_so] = ComputePrecRec(fp, tp_so, npos_so); 101 | [rec_o, prec_o, ap_o] = ComputePrecRec(fp, tp_o, npos_o); 102 | [rec, prec, ap] = ComputePrecRec(fp, tp, npos); 103 | 104 | function [rec, prec, ap] = ComputePrecRec(fp, tp, npos) 105 | % Computes precision/recall. 106 | fp = cumsum(fp); 107 | tp = cumsum(tp); 108 | rec = tp / npos; 109 | prec = tp ./ (fp + tp); 110 | 111 | % Computes average precision. 112 | ap = 0; 113 | for t = 0 : 0.01 : 1 114 | p = max(prec(rec >= t)); 115 | if isempty(p) 116 | p = 0; 117 | end 118 | ap = ap + p / 101; 119 | end 120 | -------------------------------------------------------------------------------- /UCI_OFD/Main.m: -------------------------------------------------------------------------------- 1 | function Main(file_name, method_name) 2 | close all; 3 | 4 | load('OFD/annotations.mat'); 5 | min_overlap = 0.5; 6 | file_names = cell(1, 0); 7 | method_names = cell(1, 0); 8 | 9 | file_names{end+1} = 'DetectionResults/HPM_no_occ_HELEN68.txt'; 10 | method_names{end+1} = 'HPM-occ(HELEN68)'; 11 | 12 | file_names{end+1} = 'DetectionResults/HPM_HELEN68.txt'; 13 | method_names{end+1} = 'HPM(HELEN68)'; 14 | 15 | % Multi-resolution HPM without rotation at test 16 | %file_names{end+1} = 'DetectionResults/HPM_multi_res_worot_HELEN68.txt'; 17 | %method_names{end+1} = 'Multi-resolution HPM(HELEN68)'; 18 | 19 | % Multi-resolution HPM wtih rotation at test 20 | file_names{end+1} = 'DetectionResults/HPM_multi_res_wrot_HELEN68.txt'; 21 | method_names{end+1} = 'Multi-resolution HPM(HELEN68)'; 22 | 23 | % Add other methods here. 24 | % file_names{end+1} = 'address of the detection info file'; 25 | % method_names{end+1} = 'name of the method'; 26 | 27 | % Adds the results of the input. 28 | if(exist('file_name', 'var')) 29 | file_names{end+1} = file_name; 30 | method_names{end+1} = method_name; 31 | end 32 | 33 | for i = 1 : length(file_names) 34 | fprintf('Reading detection results of "%s" from "%s".\n', ... 35 | method_names{i}, file_names{i}); 36 | [BB, ids, confidence] = ReadDetectionResults(file_names{i}); 37 | fprintf('Evaluating ...\n'); 38 | [rec{i}, prec{i}, ap{i}, rec_o{i}, prec_o{i}, ap_o{i}, rec_so{i}, ... 39 | prec_so{i}, ap_so{i}, rec_v{i}, prec_v{i}, ap_v{i}] = ... 40 | Evaluate(ground_truth, gt_ids, BB, ids, confidence, min_overlap); 41 | end 42 | 43 | fprintf('Plotting precision-recall curves ...\n'); 44 | PlotCurves(rec, prec, ap, method_names, 'all faces'); 45 | PlotCurves(rec_v, prec_v, ap_v, method_names, 'visible faces'); 46 | PlotCurves(rec_o, prec_o, ap_o, method_names, 'occluded faces'); 47 | PlotCurves(rec_so, prec_so, ap_so, method_names, 'significant occluded faces'); 48 | -------------------------------------------------------------------------------- /UCI_OFD/OFD/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/1.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/10.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/11.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/12.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/14.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/15.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/16.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/17.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/18.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/19.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/20.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/21.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/22.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/23.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/24.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/25.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/26.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/27.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/28.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/3.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/30.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/31.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/32.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/33.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/34.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/35.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/36.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/37.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/38.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/39.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/4.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/40.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/41.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/42.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/50.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/52.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/53.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/54.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/55.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/55.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/57.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/57.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/58.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/58.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/59.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/59.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/6.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/60.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/60.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/61.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/61.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/62.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/62.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/63.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/63.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/66.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/66.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/68.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/68.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/72.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/72.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/73.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/73.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/74.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/74.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/75.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/75.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/76.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/76.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/77.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/77.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/78.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/78.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/79.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/79.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/8.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/80.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/80.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/82.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/82.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/9.jpg -------------------------------------------------------------------------------- /UCI_OFD/OFD/annotations.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/OFD/annotations.mat -------------------------------------------------------------------------------- /UCI_OFD/PlotCurves.m: -------------------------------------------------------------------------------- 1 | function PlotCurves(precision, recall, ap, method_names, title_name) 2 | 3 | figure; hold on; grid on; 4 | colors = VaryColor(length(method_names)); 5 | for i = 1 : length(method_names) 6 | plot(precision{i}, recall{i}, 'color', colors(i, :), 'linewidth', 2); 7 | method_names{i} = [method_names{i} sprintf(', AP: %.2f%%', ap{i} * 100)]; 8 | end 9 | 10 | xlabel('Recall', 'fontsize', 20); 11 | ylabel('Precision', 'fontsize', 20); 12 | set(gca,'fontsize', 20); 13 | set(gcf,'color', 'w') 14 | legend(method_names, 'Location', 'SouthWest', 'FontSize', 15); 15 | title(title_name); 16 | axis tight 17 | 18 | %if(~exist('plots','dir')) 19 | % mkdir('plots'); 20 | %end 21 | %addpath /extra/titansc0/gghiasi/Matlab_tools/export_fig/ 22 | %export_fig(['plots/' title_name], '-pdf'); 23 | -------------------------------------------------------------------------------- /UCI_OFD/ReadDetectionResults.m: -------------------------------------------------------------------------------- 1 | function [BB, ids, confidence] = ReadDetectionResults(filename) 2 | 3 | fid = fopen(filename, 'r'); 4 | ids = []; 5 | confidence = []; 6 | BB = []; 7 | while ~feof(fid) 8 | nextline = fgets(fid); 9 | imageid = sscanf(nextline, '%s', 1); 10 | nextline = nextline(length(imageid) + 1 : end); 11 | tbox = sscanf(nextline, '%f'); 12 | tBB(1, 1) = tbox(1); 13 | tBB(2, 1) = tbox(2); 14 | tBB(3, 1) = tbox(3); 15 | tBB(4, 1) = tbox(4); 16 | 17 | BB = [BB tBB]; 18 | confidence = [confidence; tbox(5)]; 19 | ids{end + 1, 1} = imageid; 20 | end 21 | fclose(fid); 22 | -------------------------------------------------------------------------------- /UCI_OFD/ReadMe.md: -------------------------------------------------------------------------------- 1 | This directory contains UCI-OFD dataset which is introduced in the following paper: 2 | 3 | [Golnaz Ghiasi, Charless Fowlkes, 4 | "Occlusion Coherence: Detecting and Localizing Occluded Faces", 5 | Technical Report, June 2015] [arXiv:1506.08347](http://arxiv.org/pdf/1506.08347.pdf) 6 | 7 | "OFD" directory contains dataset images and the annotations are stored in 8 | "OFD/annotations.mat". For each image, this file has bounding boxes of the 9 | faces and labels that indicate the level of occlusion of faces (0 -> visible, 10 | 1-> occluded, 2-> at least one of the inner parts of the face is occluded by 11 | something other than glass!). ShowAnnotation.m visualizes the annotations. 12 | 13 | To evaluate your model, generate a file that contains the detections info and 14 | specify its address and name in Main.m. 15 | Each line of the file should contain image id, bounding box coordinates and 16 | detection score for one face. 17 | image_id bbox_x1 bbox_y1 bbox_x2 bbox_y2 score 18 | 19 | 20 | -------------------------------------------------------------------------------- /UCI_OFD/ShowAnnotations.m: -------------------------------------------------------------------------------- 1 | load('OFD/annotations'); 2 | for i = 1: length(ground_truth) 3 | figure(1); clf; 4 | imshow(imread(ground_truth(i).im)); hold on; 5 | 6 | bb = ground_truth(i).BB; 7 | for j = 1 : size(bb, 2) 8 | x = bb([1 1 3 3 1], j); 9 | y = bb([2 4 4 2 2], j); 10 | if(ground_truth(i).occ(j) == 2) 11 | col = 'm'; 12 | elseif(ground_truth(i).occ(j) == 1) 13 | col = 'r'; 14 | else 15 | col = 'g'; 16 | end 17 | plot(x, y, col, 'linewidth', 2); 18 | end 19 | fprintf('Press any key to show next image\n'); 20 | pause; 21 | end 22 | 23 | -------------------------------------------------------------------------------- /UCI_OFD/VaryColor.m: -------------------------------------------------------------------------------- 1 | function ColorSet=varycolor(NumberOfPlots) 2 | % VARYCOLOR Produces colors with maximum variation on plots with multiple 3 | % lines. 4 | % 5 | % VARYCOLOR(X) returns a matrix of dimension X by 3. The matrix may be 6 | % used in conjunction with the plot command option 'color' to vary the 7 | % color of lines. 8 | % 9 | % Yellow and White colors were not used because of their poor 10 | % translation to presentations. 11 | % 12 | % Example Usage: 13 | % NumberOfPlots=50; 14 | % 15 | % ColorSet=varycolor(NumberOfPlots); 16 | % 17 | % figure 18 | % hold on; 19 | % 20 | % for m=1:NumberOfPlots 21 | % plot(ones(20,1)*m,'Color',ColorSet(m,:)) 22 | % end 23 | 24 | %Created by Daniel Helmick 8/12/2008 25 | 26 | error(nargchk(1,1,nargin))%correct number of input arguements?? 27 | error(nargoutchk(0, 1, nargout))%correct number of output arguements?? 28 | 29 | %Take care of the anomolies 30 | if NumberOfPlots<1 31 | ColorSet=[]; 32 | elseif NumberOfPlots==1 33 | ColorSet=[0 1 0]; 34 | elseif NumberOfPlots==2 35 | ColorSet=[0 1 0; 0 1 1]; 36 | elseif NumberOfPlots==3 37 | ColorSet=[0 1 0; 0 1 1; 0 0 1]; 38 | elseif NumberOfPlots==4 39 | ColorSet=[0 1 0; 0 1 1; 0 0 1; 1 0 1]; 40 | elseif NumberOfPlots==5 41 | ColorSet=[0 1 0; 0 1 1; 0 0 1; 1 0 1; 1 0 0]; 42 | elseif NumberOfPlots==6 43 | ColorSet=[0 1 0; 0 1 1; 0 0 1; 1 0 1; 1 0 0; 0 0 0]; 44 | 45 | else %default and where this function has an actual advantage 46 | 47 | %we have 5 segments to distribute the plots 48 | EachSec=floor(NumberOfPlots/5); 49 | 50 | %how many extra lines are there? 51 | ExtraPlots=mod(NumberOfPlots,5); 52 | 53 | %initialize our vector 54 | ColorSet=zeros(NumberOfPlots,3); 55 | 56 | %This is to deal with the extra plots that don't fit nicely into the 57 | %segments 58 | Adjust=zeros(1,5); 59 | for m=1:ExtraPlots 60 | Adjust(m)=1; 61 | end 62 | 63 | SecOne =EachSec+Adjust(1); 64 | SecTwo =EachSec+Adjust(2); 65 | SecThree =EachSec+Adjust(3); 66 | SecFour =EachSec+Adjust(4); 67 | SecFive =EachSec; 68 | 69 | for m=1:SecOne 70 | ColorSet(m,:)=[0 1 (m-1)/(SecOne-1)]; 71 | end 72 | 73 | for m=1:SecTwo 74 | ColorSet(m+SecOne,:)=[0 (SecTwo-m)/(SecTwo) 1]; 75 | end 76 | 77 | for m=1:SecThree 78 | ColorSet(m+SecOne+SecTwo,:)=[(m)/(SecThree) 0 1]; 79 | end 80 | 81 | for m=1:SecFour 82 | ColorSet(m+SecOne+SecTwo+SecThree,:)=[1 0 (SecFour-m)/(SecFour)]; 83 | end 84 | 85 | for m=1:SecFive 86 | ColorSet(m+SecOne+SecTwo+SecThree+SecFour,:)=[(SecFive-m)/(SecFive) 0 0]; 87 | end 88 | 89 | end 90 | -------------------------------------------------------------------------------- /UCI_OFD/plots/all faces.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/plots/all faces.pdf -------------------------------------------------------------------------------- /UCI_OFD/plots/occluded faces.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/plots/occluded faces.pdf -------------------------------------------------------------------------------- /UCI_OFD/plots/significant occluded faces.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/plots/significant occluded faces.pdf -------------------------------------------------------------------------------- /UCI_OFD/plots/visible faces.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/UCI_OFD/plots/visible faces.pdf -------------------------------------------------------------------------------- /cache/HPM_OFD_boxes_interval5_incOB0_rot20_10.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_OFD_boxes_interval5_incOB0_rot20_10.mat -------------------------------------------------------------------------------- /cache/HPM_afw_boxes_interval5_incOB0_ov8_rot15_5.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_afw_boxes_interval5_incOB0_ov8_rot15_5.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOB0_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOB0_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOB10_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOB10_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOB20_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOB20_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOB30_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOB30_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn10_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn10_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn20_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn20_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn30_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn30_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn40_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn40_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn50_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn50_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_test_boxes_interval5_incOBn60_ov7_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_test_boxes_interval5_incOBn60_ov7_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_cofw_train_boxes_interval5_incOB0_ov7_rot0_1.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_cofw_train_boxes_interval5_incOB0_ov7_rot0_1.mat -------------------------------------------------------------------------------- /cache/HPM_final.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_final.mat -------------------------------------------------------------------------------- /cache/HPM_final_bbox.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_final_bbox.mat -------------------------------------------------------------------------------- /cache/HPM_lfw_A_B_boxes_interval5_incOB0_ov2_rot30_6.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_lfw_A_B_boxes_interval5_incOB0_ov2_rot30_6.mat -------------------------------------------------------------------------------- /cache/HPM_lfw_val_M_boxes_interval5_incOB0_ov2_rot10_5.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_lfw_val_M_boxes_interval5_incOB0_ov2_rot10_5.mat -------------------------------------------------------------------------------- /cache/HPM_multi_res_final.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/cache/HPM_multi_res_final.mat -------------------------------------------------------------------------------- /compile.m: -------------------------------------------------------------------------------- 1 | % ============= 2 | % Learning code 3 | %cd learning; 4 | %mex -O -largeArrayDims qp_one_sparse.cc 5 | %mex -O -largeArrayDims score.cc 6 | %mex -O -largeArrayDims lincomb.cc 7 | %cd ..; 8 | 9 | % ============= 10 | % Detection code 11 | if isunix() 12 | cd mex_unix 13 | % use one of the following depending on your setup 14 | % 1 is fastest, 3 is slowest 15 | % 1) multithreaded convolution using blas 16 | mex -O fconvblas_gcc445.cc -lmwblas -output fconv 17 | % 2) mulththreaded convolution without blas 18 | % mex -O fconvMT.cc -o fconv 19 | % 3) basic convolution, very compatible 20 | % mex -O fconv.cc -o fconv 21 | elseif ispc() 22 | cd mex_pc; 23 | mex -O fconv.cc 24 | end 25 | 26 | mex -O resize.cc 27 | mex -O reduce.cc 28 | mex -O shiftdt.cc 29 | mex -O fast_bounded_shifdt.cc 30 | mex -O features.cc 31 | mex -O fast_update_score.cc 32 | 33 | cd ..; 34 | -------------------------------------------------------------------------------- /data/AfwTestData.m: -------------------------------------------------------------------------------- 1 | function test = AfwTestData(afw_dir) 2 | disp('Reading AFW test data ...'); 3 | 4 | load([afw_dir 'anno.mat']); 5 | test = []; 6 | for i = 1 : length(anno) 7 | for j = 1 : length(anno{i, 2}) 8 | if(isempty(test)) 9 | test(1).im = [afw_dir anno{i, 1}]; 10 | else 11 | test(end+1).im = [afw_dir anno{i, 1}]; 12 | end 13 | test(end).pts = anno{i, 4}{j}; 14 | test(end).headpose = anno{i, 3}{j}(1); 15 | test(end).bbox = anno{i, 2}{j}; 16 | test(end).bbox = [test(end).bbox(1, :) test(end).bbox(2, :)]; 17 | test(end).id = num2str(length(test)); 18 | test(end).image_num = i; 19 | test(end).face_num = j; 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /data/CofwData.m: -------------------------------------------------------------------------------- 1 | function test = CofwData(data_file_add, data_dir, read_train_data) 2 | % data_file_add: address to the COFW train data. 3 | % data_dir: directory to save cofw images. 4 | % read_train_data: read train or test COFW test data, 1->train, 0->test 5 | 6 | if read_train_data 7 | fprintf('Reading COFW train data ...\n'); 8 | else 9 | fprintf('Reading COFW test data ...\n'); 10 | end 11 | 12 | if read_train_data 13 | load(data_file_add, 'phisTr','IsTr','bboxesTr'); 14 | else 15 | % Reads cofw test data. 16 | load(data_file_add, 'phisT','IsT','bboxesT'); 17 | phisTr = phisT; 18 | IsTr = IsT; 19 | bboxesTr = bboxesT; 20 | end 21 | 22 | if(~exist(data_dir, 'dir')) 23 | mkdir(data_dir); 24 | end 25 | for i = 1 : length(IsTr) 26 | im_add = [data_dir '/im_' num2str(i) '.png']; 27 | try 28 | imread(im_add); 29 | catch 30 | I = IsTr{i}; 31 | imwrite(I, im_add); 32 | end 33 | try 34 | load(im_add(1 : end - 4), 'pts', 'occ'); 35 | catch 36 | pts = [phisTr(i, 1 : 29)' phisTr(i, 30 : 58)']; 37 | occ = phisTr(i, 59 : end); 38 | save(im_add(1 : end - 4), 'pts', 'occ'); 39 | end 40 | test(i).id = num2str(i); 41 | test(i).im = im_add; 42 | test(i).pts = pts; 43 | test(i).occ = occ; 44 | if read_train_data 45 | % Sets the COFW training boundig boxes to be same as the COFW testing 46 | % bounding boxes! (Original training bouding boxes have more variation) 47 | test(i).bbox = round([min(pts(:, 1)) - 10, min(pts(:, 2)) - 10, ... 48 | max(pts(:,1)) + 10, max(pts(:, 2)) + 10]); 49 | else 50 | % For the experiments in the papers, these bounding boxes are set to be 51 | % the tight bounding boxes around the keypoints and the detection with 52 | % highest score and minimum 80% overlap is returned as the solution. 53 | % However, We can get same results when the bounding boxes are set same as the 54 | % COFW ground-truth bounding boxes and the minimum overlap is 70% (as here). 55 | bb = bboxesTr(i,:); 56 | test(i).bbox = [bb(1), bb(2), bb(3) + bb(1), bb(4) + bb(2)]; 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /data/LfwData.m: -------------------------------------------------------------------------------- 1 | function test = LfwData(lfw_anno, lfw_dir, first_char) 2 | 3 | fprintf('Reading lfw data starting with %c ...\n', first_char); 4 | 5 | file_id = fopen(lfw_anno,'r'); 6 | linedata = textscan(file_id,'%s','delimiter','\n','whitespace',''); 7 | linedata = linedata{1}; 8 | 9 | % Removes initial lines. 10 | linedata(1 : 6) = []; 11 | 12 | test = []; 13 | for i = 1 : length(linedata) 14 | s = linedata{i}; 15 | if all(s(1) ~= first_char) 16 | continue; 17 | end 18 | 19 | ind = min(find(s == ' ')); 20 | name = s(1 : ind - 1); 21 | test(end+1).im = [lfw_dir name]; 22 | 23 | nums = sscanf(s(ind + 1 : end), '%d '); 24 | test(end).bbox = [nums(1), nums(2), ... 25 | nums(1) + nums(3), nums(2) + nums(4)]; 26 | 27 | test(end).headpose = nums(5); 28 | 29 | test(end).pts = [nums(7:2:end) + nums(1) + 1, ... 30 | nums(8:2:end) + nums(2) + 1]; 31 | id = name(find(name == '/') + 1 : end - 4); 32 | test(end).id = id; 33 | end 34 | 35 | if 0 36 | % Visualizes ground-truth. 37 | for i = 1 : length(linedata) 38 | I = imread(test(i).im); 39 | imagesc(I); axis('equal'); hold on; 40 | 41 | bb = test(i).bbox; 42 | rectangle('Position', ... 43 | [bb(1), bb(2), bb(3) - bb(1), bb(4) - bb(2)], ... 44 | 'EdgeColor', 'b'); 45 | 46 | pts = test(i).pts; 47 | plot(pts(:, 1), pts(:, 2), '.r'); 48 | 49 | pause; 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /detection/BoxesFilename.m: -------------------------------------------------------------------------------- 1 | function filename = BoxesFilename(options) 2 | % Returns a filename based on the options. 3 | 4 | filename = [options.cachedir options.experiment_name '_' options.test_name ... 5 | '_boxes' '_interval' num2str(options.interval) '_incOB']; 6 | options.occ_bias_inc = floor((options.occ_bias_inc + eps) * 100); 7 | if(options.occ_bias_inc<0) 8 | filename = [filename 'n']; 9 | end 10 | filename = [filename num2str(abs(options.occ_bias_inc))]; 11 | if isfield(options,'overlap_threshold') 12 | filename = [filename '_ov' num2str(floor(options.overlap_threshold * 10))]; 13 | end 14 | filename = [filename '_rot' num2str(floor(options.rotation_range)) '_' ... 15 | num2str(floor(options.rotation_step))]; 16 | -------------------------------------------------------------------------------- /detection/BoxesInfo.m: -------------------------------------------------------------------------------- 1 | function bs = BoxesInfo(bs, model, offset, rotate_points) 2 | if(~exist('offset', 'var')) 3 | offset = [0 0]; 4 | end 5 | if(~exist('rotate_points', 'var')) 6 | rotate_points = true; 7 | end 8 | 9 | if ~isempty(bs) 10 | for j = 1:length(bs) 11 | if(rotate_points && bs(j).ang ~= 0) 12 | bs(j).xy(:, [1, 2]) = RotatePoints(bs(j).xy(:, [1, 2]), ... 13 | bs(j).rot_cent, bs(j).ang); 14 | bs(j).xy(:, [3, 4]) = RotatePoints(bs(j).xy(:, [3, 4]), ... 15 | bs(j).rot_cent, bs(j).ang); 16 | end 17 | 18 | bs(j).xy(:,[1 3]) = bs(j).xy(:,[1 3]) + offset(1); 19 | bs(j).xy(:,[2 4]) = bs(j).xy(:,[2 4]) + offset(2); 20 | bs(j).rot_cent = bs(j).rot_cent + offset; 21 | 22 | bs(j).xy_all = bs(j).xy; 23 | bs(j).m_all = bs(j).m; 24 | % Removes parts. 25 | bs(j).xy(model.opts.mixture(bs(j).c).part_level == 2,:) = []; 26 | bs(j).m(model.opts.mixture(bs(j).c).part_level == 2) = []; 27 | 28 | model_parts = model.components{bs(j).c}; 29 | model_parts(model.opts.mixture(bs(j).c).part_level == 2) = []; 30 | 31 | % Sets occlusion flags. 32 | is_occ = zeros(1,length(model_parts)); 33 | for it = 1 : length(model_parts) 34 | is_occ(it) = model_parts(it).occfilter(bs(j).m(it)); 35 | end 36 | bs(j).occ = is_occ; 37 | 38 | bs(j).det = [mean(bs(j).xy(:, [1, 3]), 2), ... 39 | mean(bs(j).xy(:, [2, 4]), 2)]; 40 | if size(bs(j).det, 1) == 68 41 | bs(j).det = inv(model.opts.mixture(1).anno2treeorder) * ... 42 | bs(j).det; 43 | bs(j).occ = (inv(model.opts.mixture(1).anno2treeorder) * ... 44 | bs(j).occ')'; 45 | bs(j).m = inv(model.opts.mixture(1).anno2treeorder) * ... 46 | bs(j).m; 47 | bs(j).xy = inv(model.opts.mixture(1).anno2treeorder) * ... 48 | bs(j).xy; 49 | bs(j).det68 = bs(j).det; 50 | bs(j).occ68 = bs(j).occ; 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /detection/DetectDetect.m: -------------------------------------------------------------------------------- 1 | function boxes = DetectDetect(input, model, min_level) 2 | 3 | if(~exist('min_level', 'var')) 4 | min_level = 1; 5 | end 6 | if(size(input,3) == 1) 7 | input = repmat(input, [1 1 3]); 8 | end 9 | 10 | BOXCACHESIZE = 100000; 11 | cnt = 0; 12 | boxes.s = 0; 13 | boxes.c = 0; 14 | boxes.xy = 0; 15 | boxes.level = 0; 16 | boxes.m = 0; 17 | boxes(BOXCACHESIZE) = boxes; 18 | 19 | % Computes the feature pyramid. 20 | pyra_or = featpyramid(input,model); 21 | 22 | % Computes fliped image and its feature pyramid for the 23 | % components which share their parameters with their flipped components. 24 | input_fl = input(:, end : -1 : 1, :); 25 | pyra_fl = featpyramid(input_fl,model); 26 | 27 | % If faces are big enough that we don't need to run the model on the 28 | % low levels of the pyramid. Levels 1 to interval corresponding to the 29 | % images larger with a scale larger than 1. 30 | % (corresponding image of level 1 has scale of 2) 31 | levels = min_level : length(pyra_or.feat); 32 | 33 | levels = levels(randperm(length(levels))); 34 | [components,filters,resp] = modelComponents(model, pyra_or); 35 | 36 | resp_fl = resp; 37 | resp_or = resp; 38 | % Finds filter set for the fliped components. 39 | filterid_fl_list = false(1, length(filters)); 40 | for c = 1 : length(model.flip_image) 41 | if(model.flip_image(c)) 42 | parts = components{c}; 43 | for i = 1 : length(parts) 44 | filterid_fl_list(parts(i).filterid(parts(i).filterid > 0)) = true; 45 | end 46 | end 47 | end 48 | 49 | % Computes the corresponding scale of the image for each level of the feature pyramid. 50 | pyramid_scales = model.sbin ./ pyra_or.scale; 51 | % Iterates over random permutation of scales and components, 52 | for rlevel = levels, 53 | for c = randperm(length(model.components)) 54 | 55 | min_s = model.min_scale(c); 56 | max_s = model.max_scale(c); 57 | if(min_s ~= -1 && pyramid_scales(rlevel) < min_s || ... 58 | max_s ~= -1 && pyramid_scales(rlevel) > max_s) 59 | continue; 60 | end 61 | 62 | parts = components{c}; 63 | num_parts = length(parts); 64 | 65 | % Calculates size of a response map for a filter of the component and current level 66 | % parts(end) is a leaf and it has a filter. 67 | filterids = parts(end).filterid; 68 | non_zero_filterid = filterids(find(filterids, 1, 'first')); 69 | % Local part scores 70 | for k = 1 : num_parts, 71 | f = parts(k).filterid; 72 | level = rlevel - parts(k).scale * model.interval; 73 | 74 | if ~model.flip_image(c) 75 | if isempty(resp_or{level}), 76 | resp_or{level} = fconv(pyra_or.feat{level}, ... 77 | filters, 1, length(model.filters)); 78 | end 79 | resp{level} = resp_or{level}; 80 | else 81 | if isempty(resp_fl{level}), 82 | filters_fl = filters(filterid_fl_list); 83 | resp_fl{level}(filterid_fl_list) = ... 84 | fconv(pyra_fl.feat{level}, filters_fl, ... 85 | 1, length(filters_fl)); 86 | end 87 | resp{level} = resp_fl{level}; 88 | end 89 | score_size = size(resp{level}{non_zero_filterid}); 90 | 91 | parts(k).score = zeros(score_size(1), score_size(2), length(f)); 92 | for fi = 1 : length(f) 93 | if(f(fi) ~= 0) 94 | parts(k).score(:, :, fi) = resp{level}{f(fi)}; 95 | end 96 | end 97 | parts(k).level = level; 98 | end 99 | 100 | % Walks from leaves to root of tree, passing message to parent. 101 | for k = num_parts : -1 : 2, 102 | par = parts(k).parent; 103 | [msg, parts(k).Ix, parts(k).Iy, parts(k).Im] = ... 104 | passmsg(parts(k),parts(par)); 105 | parts(par).score = parts(par).score + msg; 106 | end 107 | 108 | % Adds bias to root score. 109 | parts(1).score = parts(1).score + parts(1).b; 110 | [rscore, Im] = max(parts(1).score, [], 3); 111 | [Y, X] = find(rscore >= model.thresh); 112 | if ~isempty(X) 113 | I = (X - 1) * size(rscore, 1) + Y; 114 | XY = backtrack(X, Y, Im(I), parts, pyra_or); 115 | end 116 | 117 | % Walks back down tree following pointers. 118 | for i = 1 : length(X) 119 | x = X(i); 120 | y = Y(i); 121 | 122 | bb = XY(i, :, :); 123 | if(size(bb, 3) > 1) 124 | bb = squeeze(bb)'; 125 | end 126 | xy = [mean(bb(:, [1, 3]), 2) mean(bb(:, [2, 4]), 2)]; 127 | xy(model.opts.mixture(c).part_level == 2, :) = []; 128 | 129 | if 0 130 | if cnt == BOXCACHESIZE 131 | nboxes = nmsFace(boxes, 0.95); 132 | boxes = []; 133 | cnt = 0; 134 | boxes.s = 0; 135 | boxes.c = 0; 136 | boxes.xy = 0; 137 | boxes.level = 0; 138 | boxes.m = 0; 139 | boxes(BOXCACHESIZE) = boxes; 140 | cnt = length(nboxes); 141 | boxes(1 : cnt) = nboxes; 142 | fprintf('Number of boxes reached to the maximum number of boxes and after nms number of boxes is %d.\n',cnt); 143 | end 144 | end 145 | 146 | cnt = cnt + 1; 147 | boxes(cnt).c = c; 148 | boxes(cnt).s = rscore(y,x); 149 | boxes(cnt).level = rlevel; 150 | boxes(cnt).xy = bb(:,1:4); 151 | boxes(cnt).m = bb(:,5); 152 | end 153 | end 154 | end 155 | boxes = boxes(1 : cnt); 156 | boxes = FlipBoxesOfFlippedComp(boxes, model.flip_image, size(input_fl), ... 157 | model.opts); 158 | %fprintf('Number of boxes is %d.\n', length(boxes)); 159 | 160 | % Backtrack through DP msgs to collect ptrs to part locations 161 | function box = backtrack(x, y, mix, parts, pyra) 162 | num_x = length(x); 163 | num_parts = length(parts); 164 | 165 | xptr = zeros(num_x, num_parts); 166 | yptr = zeros(num_x, num_parts); 167 | mptr = zeros(num_x, num_parts); 168 | box = zeros(num_x, 5, num_parts); 169 | 170 | for k = 1 : num_parts, 171 | p = parts(k); 172 | if k == 1, 173 | xptr(:, k) = x; 174 | yptr(:, k) = y; 175 | mptr(:, k) = mix; 176 | else 177 | par = p.parent; 178 | [h, w, ~] = size(p.Ix); 179 | I = (mptr(:, par) - 1) * h * w + (xptr(:, par) - 1) * h + ... 180 | yptr(:, par); 181 | xptr(:, k) = p.Ix(I); 182 | yptr(:, k) = p.Iy(I); 183 | mptr(:, k) = p.Im(I); 184 | 185 | % Sets the location of occluded leaves at their rest location. 186 | if(p.leaf) 187 | occleaves = find(p.occfilter(mptr(:,k))==1); 188 | for i = 1 : length(occleaves) 189 | ic = occleaves(i); 190 | 191 | pm = mptr(ic,par); 192 | chm = mptr(ic,k); 193 | px = xptr(ic,par); 194 | py = yptr(ic,par); 195 | 196 | probex = ((px - 1) * p.step + p.startx(pm, chm)); 197 | probey = ((py - 1) * p.step + p.starty(pm, chm)); 198 | 199 | xptr(ic, k) = probex; 200 | yptr(ic, k) = probey; 201 | end 202 | end 203 | end 204 | scale = pyra.scale(p.level); 205 | x1 = (xptr(:, k) - 1 - pyra.padx) * scale + 1; 206 | y1 = (yptr(:, k) - 1 - pyra.pady) * scale + 1; 207 | x2 = x1 + p.sizx(mptr(:, k)) * scale - 1; 208 | y2 = y1 + p.sizy(mptr(:, k)) * scale - 1; 209 | box(:, :, k) = [x1, y1, x2, y2, mptr(:, k)]; 210 | end 211 | 212 | % Given a 2D array of filter scores 'child', 213 | % (1) Apply distance transform 214 | % (2) Shift by anchor position of part wrt parent 215 | function [score, Ix, Iy, Im] = passmsg(child, parent) 216 | 217 | n_y = size(parent.score, 1); 218 | n_x = size(parent.score, 2); 219 | 220 | num_req_dt = length(child.share_dt_computation); 221 | K = length(child.filterid); 222 | L = length(parent.filterid); 223 | 224 | dim1 = size(child.score, 1); 225 | dim2 = size(child.score, 2); 226 | 227 | scorep = zeros(dim1, dim2, num_req_dt); 228 | Ixp = int32(zeros(dim1, dim2, num_req_dt)); 229 | Iyp = int32(zeros(dim1, dim2, num_req_dt)); 230 | 231 | for i = 1:num_req_dt 232 | l = child.share_dt_computation(i).l; 233 | k = child.share_dt_computation(i).k; 234 | if(child.occfilter(k) == 0 || child.leaf == 0) 235 | 236 | %[scorep(:,:,i), Ixp(:,:,i), Iyp(:,:,i)] = ... 237 | % shiftdt(child.score(:, :, k), child.w(1, l, k), child.w(2, l, k), ... 238 | % child.w(3,l,k), child.w(4,l,k), child.startx(l, k), ... 239 | % child.starty(l, k), n_x, n_y, child.step); 240 | 241 | fast_bounded_shifdt(scorep, Ixp, Iyp, (i-1)*dim1*dim2, ... 242 | child.score(:, :, k), child.w(1, l, k), .... 243 | child.w(2, l, k), child.w(3,l,k), child.w(4,l,k), ... 244 | child.startx(l, k), child.starty(l, k), 2); 245 | end 246 | end 247 | 248 | % Second step of pass message method that is explained in the paper. 249 | [score, Ix, Iy, Im] = fast_update_score(scorep, Ixp, Iyp, child.defid, ... 250 | child.occfilter, child.leaf, child.b, ... 251 | child.def_id_for_share_dt,K,L,n_y,n_x); 252 | 253 | % Caches various statistics from the model data structure for later use. 254 | function [components, filters, resp] = modelComponents(model, pyra) 255 | 256 | components = cell(length(model.components), 1); 257 | for c = 1 : length(model.components), 258 | if(length(model.filters) == 1) 259 | non_zero_id = 1; 260 | else 261 | pids = model.opts.mixture(c).poolid(:); 262 | non_zero_id = pids(find(pids, 1)); 263 | end 264 | x = model.filters(non_zero_id); 265 | filter_size = size(x.w); 266 | 267 | for k = 1:length(model.components{c}), 268 | p = model.components{c}(k); 269 | [p.w,p.defI,p.starty,p.startx,p.step,p.level,p.Ix,p.Iy] = deal([]); 270 | [p.scale,p.level,p.Ix,p.Iy] = deal(0); 271 | 272 | par = p.parent; 273 | assert(par < k); 274 | INF = 1e10; 275 | p.b = -INF * ones(size(p.biasid)); 276 | p.biasI = zeros(size(p.biasid)); 277 | for fp = 1:size(p.biasid,1) 278 | for f = 1:size(p.biasid,2) 279 | if p.biasid(fp,f) == 0, continue, end; 280 | x = model.bias(p.biasid(fp,f)); 281 | p.b(fp,f) = x.w; 282 | p.biasI(fp,f) = x.i; 283 | end 284 | end 285 | p.b = reshape(p.b, [1 size(p.biasid)]); 286 | 287 | p.sizx = zeros(length(p.filterid),1); 288 | p.sizy = zeros(length(p.filterid),1); 289 | for f = 1:length(p.filterid) 290 | if(p.filterid(f)==0) 291 | p.sizy(f) = filter_size(1); 292 | p.sizx(f) = filter_size(2); 293 | else 294 | x = model.filters(p.filterid(f)); 295 | [p.sizy(f), p.sizx(f), ~] = size(x.w); 296 | end 297 | end 298 | 299 | for fp = 1:size(p.defid, 1) 300 | for f = 1:size(p.defid, 2) 301 | if p.defid(fp,f) == 0, continue, end; 302 | x = model.defs(p.defid(fp, f)); 303 | p.w(:,fp,f) = x.w'; 304 | p.defI(fp,f) = x.i; 305 | ax = x.anchor(1); 306 | ay = x.anchor(2); 307 | ds = x.anchor(3); 308 | p.scale = ds + components{c}(par).scale; 309 | % Amount of (virtual) padding to hallucinate. 310 | step = 2 ^ ds; 311 | virtpady = (step - 1) * pyra.pady; 312 | virtpadx = (step - 1) * pyra.padx; 313 | % Starting points (simulates additional padding at finer scales) 314 | p.starty(fp,f) = ay - virtpady; 315 | p.startx(fp,f) = ax - virtpadx; 316 | p.step = step; 317 | end 318 | end 319 | components{c}(k) = p; 320 | end 321 | 322 | for k = 1 : length(model.components{c}) 323 | components{c}(k).leaf = 1; 324 | end 325 | for k = 1 : length(model.components{c}) 326 | p = model.components{c}(k); 327 | if(p.parent > 0) 328 | components{c}(p.parent).leaf = 0; 329 | end 330 | end 331 | 332 | end 333 | 334 | resp = cell(length(pyra.feat), 1); 335 | filters = cell(length(model.filters), 1); 336 | for i = 1 : length(filters), 337 | filters{i} = model.filters(i).w; 338 | end 339 | -------------------------------------------------------------------------------- /detection/DetectGivendet.m: -------------------------------------------------------------------------------- 1 | function boxes = DetectGivendet(input, model, box, overlap, ... 2 | keypointsubset, rot, limited_level, min_level) 3 | 4 | if(~exist('rot', 'var')) 5 | rot = []; 6 | end 7 | if(~exist('limited_level', 'var')) 8 | limited_level = true; 9 | end 10 | if(~exist('min_level', 'var')) 11 | min_level = 1; 12 | end 13 | 14 | if(size(input,3)==1) 15 | input = repmat(input,[1 1 3]); 16 | end 17 | 18 | thresh_offset = 0.05; 19 | cnt = 0; 20 | boxes.s = 0; 21 | boxes.c = 0; 22 | boxes.xy = 0; 23 | boxes.level = 0; 24 | boxes.m = 0; 25 | boxes(200000) = boxes; 26 | 27 | box_or = box; 28 | % Computes the feature pyramid. 29 | pyra_or = featpyramid(input, model); 30 | % Computes fliped image and its feature pyramid for the 31 | % components which shares their parameters with their flip components. 32 | [input_fl, box_fl] = lrflip(input, box); 33 | pyra_fl = featpyramid(input_fl, model); 34 | 35 | % If faces are big enough that we don't need to run the model on the 36 | % low levels of the pyramid. Levels 1 to interval corresponding to the 37 | % images larger with a scale larger than 1. 38 | % (corresponding image of level 1 has scale of 2) 39 | levels = min_level : length(pyra_or.feat); 40 | 41 | if(limited_level) 42 | % Romoves levels that ground truth bounding box is too big or too small 43 | % at that level. This is for the speed up and without it the result is 44 | % the same. 45 | pyramid_scales = model.sbin ./ pyra_or.scale'; 46 | gt_boxsize = [((box(3) - box(1)) + (box(4) - box(2))) / 2]; 47 | min_bbox = model.min_bbox; 48 | max_bbox = model.max_bbox; 49 | r1 = find(pyramid_scales * gt_boxsize < min_bbox); 50 | r2 = find(pyramid_scales * gt_boxsize > max_bbox); 51 | levels([r1 r2]) = []; 52 | end 53 | 54 | levels = levels(randperm(length(levels))); 55 | [components, filters, resp] = modelComponents(model, pyra_or); 56 | resp_fl = resp; 57 | resp_or = resp; 58 | 59 | % Finds filter set for the fliped components. 60 | filter_ids_list_flip = false(1, length(filters)); 61 | for c = 1 : length(model.flip_image) 62 | if(model.flip_image(c)) 63 | parts = components{c}; 64 | for i = 1 : length(parts) 65 | filter_ids_list_flip(.... 66 | parts(i).filterid(parts(i).filterid > 0)) = true; 67 | end 68 | end 69 | end 70 | 71 | % Iterates over random permutation of scales and components, 72 | for rlevel = levels, 73 | % Iterates through mixture components. 74 | for c = randperm(length(model.components)) 75 | if(model.flip_image(c)) 76 | box = box_fl; 77 | else 78 | box = box_or; 79 | end 80 | 81 | parts = components{c}; 82 | num_parts = length(parts); 83 | thresh = -1e100; 84 | score_size = 0; 85 | 86 | % Local part scores 87 | for k = 1 : num_parts 88 | f = parts(k).filterid; 89 | level = rlevel - parts(k).scale * model.interval; 90 | assert(parts(k).scale == 0); 91 | 92 | 93 | if ~model.flip_image(c) 94 | if isempty(resp_or{level}), 95 | resp_or{level} = fconv(pyra_or.feat{level}, ... 96 | filters, 1, length(filters)); 97 | end 98 | resp{level} = resp_or{level}; 99 | else 100 | if isempty(resp_fl{level}), 101 | filters_fl = filters(filter_ids_list_flip); 102 | resp_fl{level}(filter_ids_list_flip) = ... 103 | fconv(pyra_fl.feat{level}, ... 104 | filters_fl, 1, length(filters_fl)); 105 | end 106 | resp{level} = resp_fl{level}; 107 | end 108 | for fi = 1 : length(f) 109 | if(f(fi) ~= 0) 110 | parts(k).score(:, :, fi) = resp{level}{f(fi)}; 111 | if(score_size == 0) 112 | score_size = size(resp{level}{f(fi)}); 113 | end 114 | end 115 | end 116 | parts(k).level = level; 117 | end 118 | for k = 1 : num_parts, 119 | f = parts(k).filterid; 120 | for fi = 1 : length(f) 121 | if(f(fi) == 0) 122 | parts(k).score(:, :, fi) = zeros(score_size); 123 | end 124 | end 125 | end 126 | 127 | 128 | % Walks from leaves to root of tree, passing message to parent. 129 | for k = num_parts : -1 : 2, 130 | par = parts(k).parent; 131 | [msg, parts(k).Ix, parts(k).Iy, parts(k).Im] = ... 132 | passmsg(parts(k), parts(par)); 133 | parts(par).score = parts(par).score + msg; 134 | end 135 | 136 | % Adds bias to root score. 137 | parts(1).score = parts(1).score + parts(1).b; 138 | 139 | [rscore, Im] = max(parts(1).score, [], 3); 140 | thresh = max(thresh, max(max(rscore))); 141 | 142 | [Y, X] = find(rscore >= thresh - thresh_offset); 143 | if ~isempty(X) 144 | I = (X - 1) * size(rscore, 1) + Y; 145 | XY = backtrack(X, Y, Im(I), parts, pyra_or); 146 | end 147 | 148 | % Walks back down tree following pointers 149 | for i = 1:length(X) 150 | x = X(i); 151 | y = Y(i); 152 | 153 | bb = squeeze(XY(i, :, :))'; 154 | xy = [mean(bb(:, [1 3]), 2) mean(bb(:, [2 4]), 2)]; 155 | xy(model.opts.mixture(c).part_level == 2, :) = []; 156 | xy = xy(keypointsubset, :); 157 | 158 | % Computes overlap. 159 | ov = testoverlap(xy, box, rot); 160 | 161 | if ov >= overlap 162 | cnt = cnt + 1; 163 | boxes(cnt).c = c; 164 | boxes(cnt).s = rscore(y, x); 165 | boxes(cnt).level = rlevel; 166 | boxes(cnt).xy = bb(:, 1 : 4); 167 | boxes(cnt).m = bb(:, 5); 168 | end 169 | end 170 | end 171 | end 172 | boxes = boxes(1 : cnt); 173 | boxes = FlipBoxesOfFlippedComp(boxes, model.flip_image, size(input_fl), ... 174 | model.opts); 175 | %fprintf('Number of boxes is %d.\n',length(boxes)); 176 | 177 | % Computes a mask of filter reponse locations (for a filter of size sizy,sizx) 178 | % that sufficiently overlap a ground-truth bounding box (bbox) 179 | % at a particular level in a feature pyramid 180 | function ov = testoverlap(xy, bbox,rot) 181 | if(~isempty(rot)) 182 | xy = RotatePoints(xy, rot.cent, rot.ang); 183 | end 184 | 185 | det = [min(xy(:,1)), min(xy(:,2)), max(xy(:,1)), max(xy(:,2))]; 186 | 187 | bx1 = bbox(1); 188 | by1 = bbox(2); 189 | bx2 = bbox(3); 190 | by2 = bbox(4); 191 | 192 | x1 = det(1); 193 | y1 = det(2); 194 | x2 = det(3); 195 | y2 = det(4); 196 | 197 | % Computes intersection with bbox. 198 | xx1 = max(x1, bx1); 199 | xx2 = min(x2, bx2); 200 | yy1 = max(y1, by1); 201 | yy2 = min(y2, by2); 202 | w = xx2 - xx1 + 1; 203 | h = yy2 - yy1 + 1; 204 | w(w < 0) = 0; 205 | h(h < 0) = 0; 206 | inter = h' * w; 207 | 208 | % Area of (possibly clipped) detection windows and original bbox. 209 | area = (y2 - y1 + 1)' * (x2 - x1 + 1); 210 | box = (by2 - by1 + 1) * (bx2 - bx1 + 1); 211 | ov = inter ./ (area + box - inter); 212 | 213 | % Backtracks through DP msgs to collect ptrs to part locations. 214 | function box = backtrack(x, y, mix, parts, pyra) 215 | num_x = length(x); 216 | num_parts = length(parts); 217 | 218 | xptr = zeros(num_x, num_parts); 219 | yptr = zeros(num_x, num_parts); 220 | mptr = zeros(num_x, num_parts); 221 | box = zeros(num_x, 5, num_parts); 222 | 223 | for k = 1 : num_parts, 224 | p = parts(k); 225 | if k == 1, 226 | xptr(:,k) = x; 227 | yptr(:,k) = y; 228 | mptr(:,k) = mix; 229 | else 230 | par = p.parent; 231 | [h, w, ~] = size(p.Ix); 232 | I = (mptr(:, par) - 1) * h * w + (xptr(:, par) - 1) * h + ... 233 | yptr(:, par); 234 | xptr(:, k) = p.Ix(I); 235 | yptr(:, k) = p.Iy(I); 236 | mptr(:, k) = p.Im(I); 237 | 238 | % Sets the location of occluded leaves at their rest location. 239 | if(p.leaf) 240 | occleaves = find(p.occfilter(mptr(:, k)) == 1); 241 | for i = 1 : length(occleaves) 242 | ic = occleaves(i); 243 | 244 | pm = mptr(ic,par); 245 | chm = mptr(ic,k); 246 | px = xptr(ic,par); 247 | py = yptr(ic,par); 248 | 249 | probex = ((px - 1) * p.step + p.startx(pm, chm)); 250 | probey = ((py - 1) * p.step + p.starty(pm, chm)); 251 | 252 | xptr(ic, k) = probex; 253 | yptr(ic, k) = probey; 254 | end 255 | end 256 | end 257 | scale = pyra.scale(p.level); 258 | x1 = (xptr(:, k) - 1 - pyra.padx) * scale +1; 259 | y1 = (yptr(:, k) - 1 - pyra.pady) * scale + 1; 260 | x2 = x1 + p.sizx(mptr(:,k)) * scale - 1; 261 | y2 = y1 + p.sizy(mptr(:,k)) * scale - 1; 262 | box(:, :, k) = [x1, y1, x2, y2, mptr(:, k)]; 263 | end 264 | 265 | % Given a 2D array of filter scores 'child', 266 | % (1) Apply distance transform 267 | % (2) Shift by anchor position of part wrt parent 268 | function [score, Ix, Iy, Im] = passmsg(child, parent) 269 | 270 | n_y = size(parent.score, 1); 271 | n_x = size(parent.score, 2); 272 | 273 | num_req_dt = length(child.share_dt_computation); 274 | K = length(child.filterid); 275 | L = length(parent.filterid); 276 | 277 | dim1 = size(child.score, 1); 278 | dim2 = size(child.score, 2); 279 | scorep = zeros(dim1, dim2, num_req_dt); 280 | Ixp = int32(zeros(dim1, dim2, num_req_dt)); 281 | Iyp = int32(zeros(dim1, dim2, num_req_dt)); 282 | 283 | for i = 1 : num_req_dt 284 | l = child.share_dt_computation(i).l; 285 | k = child.share_dt_computation(i).k; 286 | if(child.occfilter(k) == 0 || child.leaf == 0) 287 | %[scorep(:, :, i), Ixp(:, :, i), Iyp(:, :, i)] = ... 288 | % shiftdt(child.score(:, :, k), child.w(1, l, k), ... 289 | % child.w(2, l, k), child.w(3, l, k), child.w(4, l, k), ... 290 | % child.startx(l, k), child.starty(l, k), n_x, n_y, child.step); 291 | 292 | fast_bounded_shifdt(scorep, Ixp, Iyp, (i-1)*dim1*dim2, ... 293 | child.score(:, :, k), child.w(1, l, k), ... 294 | child.w(2, l, k), child.w(3,l,k), child.w(4,l,k), ... 295 | child.startx(l, k), child.starty(l, k), 2); 296 | end 297 | end 298 | 299 | % Second step of pass message method that is explained in the paper. 300 | [score, Ix, Iy, Im] = fast_update_score(scorep, Ixp, Iyp, child.defid, ... 301 | child.occfilter, child.leaf, child.b, ... 302 | child.def_id_for_share_dt, K, L, n_y, n_x); 303 | 304 | % Caches various statistics from the model data structure for later use. 305 | function [components, filters, resp] = modelComponents(model, pyra) 306 | 307 | components = cell(length(model.components), 1); 308 | for c = 1 : length(model.components), 309 | if(length(model.filters) == 1) 310 | non_zero_id = 1; 311 | else 312 | pids = model.opts.mixture(c).poolid(:); 313 | non_zero_id = pids(find(pids, 1)); 314 | end 315 | filter_size = size(model.filters(non_zero_id).w); 316 | 317 | for k = 1 : length(model.components{c}), 318 | p = model.components{c}(k); 319 | [p.w, p.defI, p.starty, p.startx, p.step, p.level, p.Ix, p.Iy] ... 320 | = deal([]); 321 | [p.scale, p.level, p.Ix, p.Iy] = deal(0); 322 | 323 | par = p.parent; 324 | assert(par < k); 325 | INF = 1e10; 326 | p.b = -INF * ones(size(p.biasid)); 327 | p.biasI = zeros(size(p.biasid)); 328 | for fp = 1 : size(p.biasid, 1) 329 | for f = 1 : size(p.biasid, 2) 330 | if p.biasid(fp,f) == 0, continue, end; 331 | p.b(fp, f) = model.bias(p.biasid(fp, f)).w; 332 | p.biasI(fp, f) = model.bias(p.biasid(fp, f)).i; 333 | end 334 | end 335 | p.b = reshape(p.b, [1, size(p.biasid)]); 336 | p.sizx = zeros(length(p.filterid), 1); 337 | p.sizy = zeros(length(p.filterid), 1); 338 | for f = 1 : length(p.filterid) 339 | if(p.filterid(f) == 0) 340 | p.sizy(f) = filter_size(1); 341 | p.sizx(f) = filter_size(2); 342 | else 343 | x = model.filters(p.filterid(f)); 344 | [p.sizy(f), p.sizx(f), ~] = size(x.w); 345 | end 346 | end 347 | 348 | for fp = 1 : size(p.defid, 1) 349 | for f = 1 : size(p.defid, 2) 350 | def_id = p.defid(fp, f); 351 | if def_id == 0, continue, end; 352 | p.w(:, fp, f) = model.defs(def_id).w'; 353 | p.defI(fp, f) = model.defs(def_id).i; 354 | ax = model.defs(def_id).anchor(1); 355 | ay = model.defs(def_id).anchor(2); 356 | ds = model.defs(def_id).anchor(3); 357 | p.scale = ds + components{c}(par).scale; 358 | % amount of (virtual) padding to hallucinate 359 | step = 2 ^ ds; 360 | virtpady = (step - 1) * pyra.pady; 361 | virtpadx = (step - 1) * pyra.padx; 362 | % starting points (simulates additional padding at finer scales) 363 | p.starty(fp, f) = ay - virtpady; 364 | p.startx(fp, f) = ax - virtpadx; 365 | p.step = step; 366 | end 367 | end 368 | components{c}(k) = p; 369 | end 370 | 371 | for k = 1 : length(model.components{c}) 372 | components{c}(k).leaf = 1; 373 | end 374 | for k = 1 : length(model.components{c}) 375 | p = model.components{c}(k); 376 | if(p.parent > 0) 377 | components{c}(p.parent).leaf = 0; 378 | end 379 | end 380 | 381 | end 382 | 383 | resp = cell(length(pyra.feat), 1); 384 | filters = cell(length(model.filters), 1); 385 | for i = 1 : length(filters), 386 | filters{i} = model.filters(i).w; 387 | end 388 | 389 | function [im, box] = lrflip(im, box) 390 | % Flips the image. 391 | im = im(:, end : -1 : 1, :); 392 | 393 | % Flips the box coordinates. 394 | im_size_x = size(im, 2); 395 | if exist('box', 'var') && ~isempty(box), 396 | x1 = box(:, 1); 397 | x3 = box(:, 3); 398 | box(:, 1) = im_size_x - x3 + 1; 399 | box(:, 3) = im_size_x - x1 + 1; 400 | else 401 | box = []; 402 | end 403 | -------------------------------------------------------------------------------- /detection/DetectionWRotation.m: -------------------------------------------------------------------------------- 1 | function boxes_nms = DetectionWRotation( ... 2 | model, im, nms_threshold, min_level, ... 3 | rotation_range, rotation_step) 4 | 5 | if(~exist('min_level', 'var')) 6 | min_level = 1; 7 | end 8 | if(~exist('rotation_range','var')) 9 | rotation_range = 0; 10 | rotation_step = 10; 11 | end 12 | 13 | tic 14 | all_boxes = []; 15 | cent = [size(im,1)/2, size(im,2)/2]; 16 | rots = -rotation_range : rotation_step : rotation_range; 17 | parfor r = 1 : length(rots) 18 | fprintf('rotation: %.2f\n', rots(r)); 19 | rot_angle = rots(r); 20 | im_rot = RotateAround(im, cent(2), cent(1), rot_angle); 21 | rot_boxes = DetectDetect(im_rot, model, min_level); 22 | 23 | if ~isempty(rot_boxes) 24 | for j = 1:length(rot_boxes) 25 | 26 | rot_boxes(j).cxy = [mean(rot_boxes(j).xy(:,[1 3]), 2) mean(rot_boxes(j).xy(:, [2 4]), 2)]; 27 | numpart = size(rot_boxes(j).xy, 1); 28 | if numpart == 1 29 | x1 = rot_boxes(j).xy(1); 30 | y1 = rot_boxes(j).xy(2); 31 | x2 = rot_boxes(j).xy(3); 32 | y2 = rot_boxes(j).xy(4); 33 | else 34 | x1 = min(rot_boxes(j).cxy(:, 1)); 35 | y1 = min(rot_boxes(j).cxy(:, 2)); 36 | x2 = max(rot_boxes(j).cxy(:, 1)); 37 | y2 = max(rot_boxes(j).cxy(:, 2)); 38 | end 39 | rect = [x1 y1; x1 y2; x2 y2; x2 y1]; 40 | rot_boxes(j).area = abs(x1 - x2) * abs(y1 - y2); 41 | rot_boxes(j).rect = RotatePoints(rect, cent, rot_angle); 42 | 43 | rot_boxes(j).ang = rot_angle; 44 | rot_boxes(j).rot_cent = cent; 45 | 46 | rot_boxes(j).xy(:, [1 2]) = RotatePoints(rot_boxes(j).xy(:, [1 2]), cent, rot_angle); 47 | rot_boxes(j).xy(:, [3 4]) = RotatePoints(rot_boxes(j).xy(:, [3 4]), cent, rot_angle); 48 | rot_boxes(j).cxy = RotatePoints(rot_boxes(j).cxy, cent, rot_angle); 49 | end 50 | all_boxes = [all_boxes rot_boxes]; 51 | end 52 | end 53 | 54 | boxes_nms = NmsFaceWRotation(all_boxes, nms_threshold); 55 | fprintf('Number of boxes after nms (with threshold %.2f) is %d (out of %d).\n', ... 56 | nms_threshold, length(boxes_nms), length(all_boxes)); 57 | image_id = 0;%warning 58 | boxes_nms = BoxesInfo(boxes_nms, model, [0 0], false); 59 | for j = 1 : length(boxes_nms) 60 | boxes_nms(j).id = image_id; 61 | end 62 | toc 63 | 64 | -------------------------------------------------------------------------------- /detection/FlipBoxesOfFlippedComp.m: -------------------------------------------------------------------------------- 1 | function boxes = FlipBoxesOfFlippedComp(boxes, flip_component, size_im, opts) 2 | % Flips the detection results of the components that their parameters 3 | % are shared with the component with their flip viewpoint. 4 | flipmaps = cell(1,0); 5 | for i = 1 : length(opts.mixture) 6 | flipmap = []; 7 | if length(opts.mixture(i).pa) == 78 8 | num_level2_parts = sum(opts.mixture(i).part_level == 2); 9 | if num_level2_parts == 10 10 | fmap = FlipMapTreeOrder(opts); 11 | orig = 1 : length(fmap); 12 | flipmap = 1 : (length(fmap) + num_level2_parts); 13 | flipmap(orig + num_level2_parts) = fmap + num_level2_parts; 14 | 15 | assert(length(flipmap) == 78); 16 | assert(all(flipmap <= 78)); 17 | end 18 | elseif length(opts.mixture(i).pa) == 7 19 | flipmap = FlipMap7Parts(); 20 | end 21 | flipmaps{i} = flipmap; 22 | end 23 | 24 | for i = 1 : length(boxes) 25 | if flip_component(boxes(i).c) 26 | xy = boxes(i).xy; 27 | x1 = xy(:, 1); 28 | x3 = xy(:, 3); 29 | xy(:, 1) = size_im(2) + 1 - x3; 30 | xy(:, 3) = size_im(2) + 1 - x1; 31 | flipmap = flipmaps{boxes(i).c}; 32 | if ~isempty(flipmap) 33 | xy(1 : end, :) = xy(flipmap, :); 34 | boxes(i).m = boxes(i).m(flipmap); 35 | end 36 | boxes(i).xy = xy; 37 | end 38 | end 39 | 40 | function flipmap = FlipMap7Parts() 41 | flipmap = zeros(1,7); 42 | flipmap(1) = 1; 43 | flipmap(2) = 3; 44 | flipmap(3) = 2; 45 | flipmap(4) = 4; 46 | flipmap(5) = 5; 47 | flipmap(6) = 7; 48 | flipmap(7) = 6; 49 | 50 | function fmap = FlipMapTreeOrder(opts) 51 | flip_map = zeros(1, 68); 52 | flip_map(1) = 17; 53 | flip_map(2) = 16; 54 | flip_map(3) = 15; 55 | flip_map(4) = 14; 56 | flip_map(5) = 13; 57 | flip_map(6) = 12; 58 | flip_map(7) = 11; 59 | flip_map(8) = 10; 60 | flip_map(9) = 9; 61 | flip_map(10) = 8; 62 | flip_map(11) = 7; 63 | flip_map(12) = 6; 64 | flip_map(13) = 5; 65 | flip_map(14) = 4; 66 | flip_map(15) = 3; 67 | flip_map(16) = 2; 68 | flip_map(17) = 1; 69 | flip_map(18) = 27; 70 | flip_map(19) = 26; 71 | flip_map(20) = 25; 72 | flip_map(21) = 24; 73 | flip_map(22) = 23; 74 | flip_map(23) = 22; 75 | flip_map(24) = 21; 76 | flip_map(25) = 20; 77 | flip_map(26) = 19; 78 | flip_map(27) = 18; 79 | flip_map(28) = 28; 80 | flip_map(29) = 29; 81 | flip_map(30) = 30; 82 | flip_map(31) = 31; 83 | flip_map(32) = 36; 84 | flip_map(33) = 35; 85 | flip_map(34) = 34; 86 | flip_map(35) = 33; 87 | flip_map(36) = 32; 88 | flip_map(37) = 46; 89 | flip_map(38) = 45; 90 | flip_map(39) = 44; 91 | flip_map(40) = 43; 92 | flip_map(41) = 48; 93 | flip_map(42) = 47; 94 | flip_map(43) = 40; 95 | flip_map(44) = 39; 96 | flip_map(45) = 38; 97 | flip_map(46) = 37; 98 | flip_map(47) = 42; 99 | flip_map(48) = 41; 100 | flip_map(49) = 55; 101 | flip_map(50) = 54; 102 | flip_map(51) = 53; 103 | flip_map(52) = 52; 104 | flip_map(53) = 51; 105 | flip_map(54) = 50; 106 | flip_map(55) = 49; 107 | flip_map(56) = 60; 108 | flip_map(57) = 59; 109 | flip_map(58) = 58; 110 | flip_map(59) = 57; 111 | flip_map(60) = 56; 112 | flip_map(61) = 65; 113 | flip_map(62) = 64; 114 | flip_map(63) = 63; 115 | flip_map(64) = 62; 116 | flip_map(65) = 61; 117 | flip_map(66) = 68; 118 | flip_map(67) = 67; 119 | flip_map(68) = 66; 120 | 121 | tree_2_orig = (opts.mixture(1).anno2treeorder * (1 : 68)')'; 122 | orig_2_tree(tree_2_orig) = 1 : 68; 123 | 124 | fmap(orig_2_tree(1 : 68)) = orig_2_tree(flip_map); 125 | -------------------------------------------------------------------------------- /detection/LocalizationsWORotation.m: -------------------------------------------------------------------------------- 1 | function boxes = LocalizationsWORotation(test, model, options) 2 | % This method uses the ground-truth locations (validation data) of the 3 | % eyes to find the rotation of the face and rotates the image so that the 4 | % face be vertical. Then, it performs the landmark localization. 5 | 6 | filename = BoxesFilename(options); 7 | 8 | try 9 | load(filename) 10 | catch 11 | save_boxes_add = [options.cachedir 'boxes_' filename(7 : end) '/']; 12 | if(~exist(save_boxes_add, 'dir')) 13 | mkdir(save_boxes_add); 14 | end 15 | 16 | startmatlabpool(); 17 | parfor i = 1 : length(test) 18 | fprintf('testing on (%d/%d) : %s\n', i, length(test), test(i).id); 19 | box_filename = [save_boxes_add 'box_' test(i).id]; 20 | try 21 | lb = load(box_filename); 22 | boxes{i} = lb.bs; 23 | fprintf('Saved result loaded.\n'); 24 | catch 25 | I = imread(test(i).im); 26 | bbox = test(i).bbox; 27 | 28 | % Finds the rotation of the face. 29 | [rot_ang, I_ver, pts_ver, rot_cent]= RotateFace( ... 30 | I, test(i).pts, options.left_eye_inds, options.right_eye_inds); 31 | bs = TestLocalization(... 32 | model, I, bbox, options.keypoint_subset,... 33 | options.overlap_threshold, options.limited_level, ... 34 | options.min_level, 2, rot_ang, rot_ang, ... 35 | options.pad_ratio, rot_cent); 36 | parforsave(box_filename, bs); 37 | boxes{i} = bs; 38 | end 39 | 40 | end 41 | closematlabpool(); 42 | save(filename, 'boxes'); 43 | rmdir(save_boxes_add, 's'); 44 | end 45 | 46 | function parforsave(filename, bs) 47 | save(filename, 'bs'); 48 | -------------------------------------------------------------------------------- /detection/LocalizationsWRotation.m: -------------------------------------------------------------------------------- 1 | function boxes = LocalizationsWRotation(test, model, options) 2 | filename = BoxesFilename(options); 3 | 4 | try 5 | load(filename); 6 | catch 7 | save_boxes_add = [options.cachedir 'boxes_' filename(7 : end) '/']; 8 | if(~exist(save_boxes_add, 'dir')) 9 | mkdir(save_boxes_add); 10 | end 11 | 12 | startmatlabpool(); 13 | for i = 1: length(test) 14 | fprintf('testing on (%d/%d) : %s\n', i, length(test), test(i).id); 15 | box_filename = [save_boxes_add 'box_' test(i).id]; 16 | try 17 | lb = load(box_filename); 18 | boxes{i} = lb.bs; 19 | fprintf('Saved result loaded.\n'); 20 | catch 21 | I = imread(test(i).im); 22 | bs = TestLocalization( ... 23 | model, I, test(i).bbox, options.keypoint_subset,... 24 | options.overlap_threshold, options.limited_level, ... 25 | 1, options.rotation_step, ... 26 | -options.rotation_range, options.rotation_range, ... 27 | options.pad_ratio); 28 | boxes{i} = bs; 29 | parforsave(box_filename, bs); 30 | 31 | figure(1); clf; imagesc(I); axis off; axis image; 32 | hold on; drawnow; 33 | if(size(I, 3) == 1) 34 | colormap(gray); 35 | end 36 | if ~isempty(bs) 37 | pts = bs.det68; 38 | occ = bs.occ68; 39 | plot(pts(occ == 0, 1), pts(occ == 0, 2), '.g', ... 40 | 'MarkerSize', 10); 41 | plot(pts(occ == 1, 1), pts(occ == 1, 2), '.r', ... 42 | 'MarkerSize', 10); 43 | drawnow; 44 | end 45 | end 46 | end 47 | closematlabpool(); 48 | save(filename, 'boxes', 'test'); 49 | rmdir(save_boxes_add, 's'); 50 | end 51 | 52 | function parforsave(filename, bs) 53 | save(filename, 'bs'); 54 | -------------------------------------------------------------------------------- /detection/NmsFaceWRotation.m: -------------------------------------------------------------------------------- 1 | function top = NmsFaceWRotation(boxes, overlap) 2 | % Non-maximum suppression. 3 | % Greedily select high-scoring detections and skip detections 4 | % that are significantly covered by a previously selected detection. 5 | 6 | N = length(boxes); 7 | 8 | if isempty(boxes) 9 | top = []; 10 | else 11 | if 0 12 | % Throws away boxes with low score if there are too many candidates 13 | if N > 30000 14 | s = [boxes.s]; 15 | [vals, I] = sort(s); 16 | boxes = boxes(I(end-29999:end)); 17 | end 18 | N = min(30000,N); 19 | end 20 | 21 | x1 = zeros(N,1); 22 | y1 = zeros(N,1); 23 | x2 = zeros(N,1); 24 | y2 = zeros(N,1); 25 | area = zeros(N,1); 26 | for nb = 1 : N 27 | numpart = size(boxes(nb).xy, 1); 28 | if numpart == 1 29 | x1(nb) = boxes(nb).xy(1); 30 | y1(nb) = boxes(nb).xy(2); 31 | x2(nb) = boxes(nb).xy(3); 32 | y2(nb) = boxes(nb).xy(4); 33 | else 34 | x1(nb) = min(boxes(nb).cxy(:, 1)); 35 | y1(nb) = min(boxes(nb).cxy(:, 2)); 36 | x2(nb) = max(boxes(nb).cxy(:, 1)); 37 | y2(nb) = max(boxes(nb).cxy(:, 2)); 38 | end 39 | area(nb) = boxes(nb).area; 40 | end 41 | 42 | s = [boxes.s]; 43 | [~, I] = sort(s); 44 | pick = []; 45 | while ~isempty(I) 46 | last = length(I); 47 | i = I(last); 48 | pick = [pick; i]; 49 | suppress = last; 50 | 51 | j = I(1 : last - 1); 52 | 53 | xx1 = max(x1(i), x1(j)); 54 | yy1 = max(y1(i), y1(j)); 55 | xx2 = min(x2(i), x2(j)); 56 | yy2 = min(y2(i), y2(j)); 57 | 58 | w = xx2-xx1+1; 59 | w(w<0) = 0; 60 | h = yy2-yy1+1; 61 | h(h<0) = 0; 62 | 63 | inter = w.*h; 64 | for k = 1:length(j) 65 | if (inter(k)/area(j(k))>overlap || inter(k)/area(i)>overlap) ... 66 | && (inter(k)/area(j(k)) overlap) | (o2 > overlap))); 79 | 80 | suppress = [suppress ; idx]; 81 | I(suppress) = []; 82 | end 83 | top = boxes(pick); 84 | end 85 | -------------------------------------------------------------------------------- /detection/TestLocalization.m: -------------------------------------------------------------------------------- 1 | function bs = TestLocalization( ... 2 | model, im, bbox, keypoint_subset, min_overlap, ... 3 | limited_level, min_level, rotation_step, ... 4 | min_rotation_range, max_rotation_range, pad_ratio, ... 5 | rot_center) 6 | 7 | tic 8 | 9 | [im_crop, bbcrop, offset] = croppos(im, bbox, pad_ratio); 10 | rots = min_rotation_range : rotation_step : max_rotation_range; 11 | if(~exist('rot_center', 'var')) 12 | cent = [size(im_crop, 1) / 2, size(im_crop, 2) / 2]; 13 | else 14 | % For the validation data we pre computed the rotation of the image and 15 | % the center of the rotation. 16 | cent = rot_center - offset; 17 | end 18 | % Finds best detection for different rotation of the image. 19 | for min_overlap_ = [min_overlap, 0.5, 0.1 0.05] 20 | all_boxes = cell(1, length(rots)); 21 | 22 | if(length(rots) == 1) 23 | rot_ang = rots; 24 | fprintf('rotation angle : %.3f\n', rot_ang); 25 | im_rotated = RotateAround(im_crop, cent(2), cent(1), rot_ang); 26 | 27 | rot = struct('cent', cent, 'ang', rot_ang); 28 | bs = DetectGivendet(im_rotated, model, bbcrop, min_overlap_, ... 29 | keypoint_subset, rot, limited_level, ... 30 | min_level); 31 | if(~isempty(bs)) 32 | [bs.ang] = deal(rot_ang); 33 | [bs.rot_cent] = deal(cent); 34 | all_boxes{1} = bs; 35 | end 36 | else 37 | 38 | parfor r = 1 : length(rots) 39 | rot_ang = rots(r); 40 | fprintf('%d : rotation angle : %.3f\n', r, rot_ang); 41 | im_rotated = RotateAround(im_crop, cent(2), cent(1), rot_ang); 42 | 43 | rot = struct('cent', cent, 'ang', rot_ang); 44 | bs = DetectGivendet(im_rotated, model, bbcrop, min_overlap_, ... 45 | keypoint_subset, rot, limited_level); 46 | if(~isempty(bs)) 47 | [bs.ang] = deal(rot_ang); 48 | [bs.rot_cent] = deal(cent); 49 | all_boxes{r} = bs; 50 | end 51 | end 52 | end 53 | 54 | all_boxes = [all_boxes{:}]; 55 | bs = []; 56 | if(~isempty(all_boxes)) 57 | % keeps the highest scoring one. 58 | s = [all_boxes.s]; 59 | [~, I] = sort(s); 60 | all_boxes = all_boxes(fliplr(I)); 61 | bs = all_boxes(1); 62 | 63 | bs = BoxesInfo(bs, model, offset); 64 | bs = clipboxes(im, bs); 65 | 66 | % We have found a detection with overlap of min_overlap_. 67 | % So we don't need to test lower overlaps. 68 | break; 69 | end 70 | end 71 | 72 | toc 73 | 74 | 75 | function boxes = clipboxes(im, boxes) 76 | % Clips boxes to image boundary. 77 | imy = size(im, 1); 78 | imx = size(im, 2); 79 | for i = 1 : length(boxes), 80 | b = boxes(i).xy; 81 | b(:, 1) = max(b(:, 1), 1); 82 | b(:, 2) = max(b(:, 2), 1); 83 | b(:, 3) = min(b(:, 3), imx); 84 | b(:, 4) = min(b(:, 4), imy); 85 | boxes(i).xy = b; 86 | if(isfield(boxes(i), 'xy68')) 87 | b = boxes(i).xy68; 88 | b(:, 1) = max(b(:, 1), 1); 89 | b(:, 2) = max(b(:, 2), 1); 90 | b(:, 3) = min(b(:, 3), imx); 91 | b(:, 4) = min(b(:, 4), imy); 92 | boxes(i).xy68 = b; 93 | end 94 | end 95 | 96 | function [im, box, offset] = croppos(im, box, pad_ratio) 97 | % Crops image around bounding box. 98 | pad = pad_ratio * ((box(3) - box(1) + 1) + (box(4) - box(2) + 1)); 99 | x1 = max(1, round(box(1) - pad)); 100 | y1 = max(1, round(box(2) - pad)); 101 | x2 = min(size(im, 2), round(box(3) + pad)); 102 | y2 = min(size(im, 1), round(box(4) + pad)); 103 | 104 | im = im(y1 : y2, x1 : x2, :); 105 | box([1 3]) = box([1 3]) - x1 + 1; 106 | box([2 4]) = box([2 4]) - y1 + 1; 107 | 108 | offset(1) = x1 - 1; 109 | offset(2) = y1 - 1; 110 | -------------------------------------------------------------------------------- /detection/featpyramid.m: -------------------------------------------------------------------------------- 1 | function pyra = featpyramid(im, model) 2 | % pyra = featpyramid(im, model, padx, pady); 3 | % Compute feature pyramid. 4 | % 5 | % pyra.feat{i} is the i-th level of the feature pyramid. 6 | % pyra.scales{i} is the scaling factor used for the i-th level. 7 | % pyra.feat{i+interval} is computed at exactly half the resolution of feat{i}. 8 | % first octave halucinates higher resolution data. 9 | 10 | interval = model.interval; 11 | sbin = model.sbin; 12 | 13 | % Select padding, allowing for one cell in model to be visible 14 | % Even padding allows for consistent spatial relations across 2X scales 15 | %maxsize = max([model.maxsize{:}]); 16 | maxsize = model.feature_map_padding; 17 | padx = max(maxsize + 1, 0); 18 | pady = max(maxsize + 1, 0); 19 | 20 | sc = 2 ^(1/interval); 21 | imsize = [size(im, 1) size(im, 2)]; 22 | max_scale = 1 + floor(log(min(imsize)/(5*sbin))/log(sc)); 23 | pyra.feat = cell(max_scale + interval, 1); 24 | pyra.scale = zeros(max_scale + interval, 1); 25 | % our resize function wants floating point values 26 | im = double(im); 27 | for i = 1:interval 28 | scaled = resize(im, 1/sc^(i-1)); 29 | % "first" 2x interval 30 | pyra.feat{i} = features(scaled, sbin/2); 31 | pyra.scale(i) = 2/sc^(i-1); 32 | % "second" 2x interval 33 | pyra.feat{i+interval} = features(scaled, sbin); 34 | pyra.scale(i+interval) = 1/sc^(i-1); 35 | % remaining interals 36 | for j = i+interval:interval:max_scale 37 | scaled = reduce(scaled); 38 | pyra.feat{j+interval} = features(scaled, sbin); 39 | pyra.scale(j+interval) = 0.5 * pyra.scale(j); 40 | end 41 | end 42 | 43 | for i = 1:length(pyra.feat) 44 | % Adds 1 to padding because feature generation deletes a 1-cell 45 | % wide border around the feature map 46 | pyra.feat{i} = padarray(pyra.feat{i}, [pady+1 padx+1 0], 0); 47 | % write boundary occlusion feature 48 | pyra.feat{i}(1:pady+1, :, end) = 1; 49 | pyra.feat{i}(end-pady:end, :, end) = 1; 50 | pyra.feat{i}(:, 1:padx+1, end) = 1; 51 | pyra.feat{i}(:, end-padx:end, end) = 1; 52 | end 53 | 54 | pyra.scale = model.sbin./pyra.scale; 55 | pyra.interval = interval; 56 | pyra.imy = imsize(1); 57 | pyra.imx = imsize(2); 58 | pyra.pady = pady; 59 | pyra.padx = padx; 60 | -------------------------------------------------------------------------------- /evaluation/LandmarkLocalizationEval.m: -------------------------------------------------------------------------------- 1 | function [errors, precision, recall] = LandmarkLocalizationEval( ... 2 | boxes, pts_name, occ_name, test, left_eye_inds, .... 3 | right_eye_inds, vis_subset) 4 | 5 | if(~exist('vis_subset', 'var')) 6 | vis_subset = false; 7 | end 8 | 9 | gr_occ = []; 10 | dt_occ = []; 11 | errors = nan(length(test), 1); 12 | for i = 1 : length(test) 13 | if isempty(boxes{i}) 14 | errors(i) = nan; 15 | continue; 16 | end 17 | 18 | pts_gt = test(i).pts; 19 | % Computes distance between centers of the eyes. 20 | p1 = mean(pts_gt(left_eye_inds, :), 1); 21 | p2 = mean(pts_gt(right_eye_inds, :), 1); 22 | normalization_dist = norm(p1 - p2); 23 | 24 | b = boxes{i}(1); 25 | pts_det = getfield(b, pts_name); 26 | occ_det = getfield(b, occ_name); 27 | 28 | % The numbers of parts are different! 29 | if(size(pts_det, 1) ~= size(pts_gt, 1)) 30 | errors(i) = nan; 31 | continue; 32 | end 33 | 34 | if(vis_subset && isfield(test(i), 'occ')) 35 | occ_gt = test(i).occ; 36 | pts_gt = pts_gt(occ_gt == 0, :); 37 | pts_det = pts_det(occ_gt == 0, :); 38 | end 39 | 40 | dif = pts_gt - pts_det; 41 | e = (dif(:,1) .^ 2 + dif(:,2) .^ 2) .^ 0.5; 42 | err_in_pixel = mean(e); 43 | errors(i) = err_in_pixel / normalization_dist; 44 | 45 | if(isfield(test(i), 'occ') && size(pts_gt, 1) == 29) 46 | occ_gt = test(i).occ; 47 | gr_occ = [gr_occ, occ_gt]; 48 | dt_occ = [dt_occ, occ_det']; 49 | end 50 | end 51 | 52 | precision = 0; 53 | recall = 0; 54 | if(isfield(test(1), 'occ')) 55 | gr_occ = gr_occ(:); 56 | dt_occ = dt_occ(:); 57 | C = confusionmat(gr_occ, dt_occ); 58 | recall = C(2, 2) / sum(C(2, :)) * 100; 59 | precision = C(2, 2) / sum(C(:, 2)) * 100; 60 | end 61 | -------------------------------------------------------------------------------- /evaluation/PrintDetectionResToFile.m: -------------------------------------------------------------------------------- 1 | function PrintDetectionResToFile(BB, ids, confidence, file_path) 2 | 3 | file_id = fopen(file_path, 'w'); 4 | 5 | for i = 1 : length(confidence) 6 | name = ids{i}; 7 | if(length(name) > 3 && name(end - 3) == '.') 8 | name = name(1 : end - 4); 9 | end 10 | fprintf(file_id, '%s ', name); 11 | fprintf(file_id, '%.2f ', BB(i, 1), BB(i, 2), BB(i, 3), BB(i, 4)); 12 | fprintf(file_id, '%.3f\n', confidence(i)); 13 | end 14 | -------------------------------------------------------------------------------- /evaluation/multipie_eval_landmark.m: -------------------------------------------------------------------------------- 1 | function [err] = multipie_eval_landmark(boxes,test) 2 | 3 | % number of test images 4 | N = length(test); 5 | 6 | % mean error 7 | err = nan(N,1); 8 | 9 | for i = 1:N % loop over all test images 10 | % ground truth 11 | pts = test(i).pts; 12 | 13 | % face size 14 | w = test(i).bbox(3) - test(i).bbox(1); 15 | h = test(i).bbox(4) - test(i).bbox(2); 16 | siz = (w+h)/2; 17 | 18 | % detection is empty 19 | if isempty(boxes{i}) 20 | % Missed detection has infinite localization error 21 | err(i) = nan; 22 | continue; 23 | end 24 | 25 | b = boxes{i}(1); 26 | % detection 27 | det = b.det_afw; 28 | 29 | % the numbers of parts are different 30 | if(size(det,1)~=size(pts,1)) 31 | err(i) = nan; 32 | continue; 33 | end 34 | 35 | dif = pts-det; 36 | e = (dif(:,1).^2+dif(:,2).^2).^0.5; 37 | err_in_pixel = nanmean(e); 38 | err(i) = err_in_pixel/siz; 39 | end 40 | 41 | 42 | 43 | 44 | function ov = testoverlap(box,pts,thresh) 45 | boxc = [mean(box(:,[1 3]),2) mean(box(:,[2 4]),2)]; 46 | 47 | b1 = [min(boxc(:,1)) min(boxc(:,2)) max(boxc(:,1)) max(boxc(:,2))]; 48 | b2 = [min(pts(:,1)) min(pts(:,2)) max(pts(:,1)) max(pts(:,2))]; 49 | 50 | bi=[max(b1(1),b2(1)) ; max(b1(2),b2(2)) ; min(b1(3),b2(3)) ; ... 51 | min(b1(4),b2(4))]; 52 | iw=bi(3)-bi(1)+1; 53 | ih=bi(4)-bi(2)+1; 54 | if iw>0 && ih>0 55 | % compute overlap as area of intersection / area of union 56 | ua=(b1(3)-b1(1)+1)*(b1(4)-b1(2)+1)+... 57 | (b2(3)-b2(1)+1)*(b2(4)-b2(2)+1)-... 58 | iw*ih; 59 | ov=iw*ih/ua; 60 | end 61 | ov = (ov>thresh); 62 | 63 | -------------------------------------------------------------------------------- /init.m: -------------------------------------------------------------------------------- 1 | close all; 2 | clear; 3 | addpath visualization 4 | addpath detection 5 | addpath evaluation 6 | addpath matlabPool 7 | addpath mex_unix 8 | addpath utility 9 | addpath data 10 | 11 | %dbstop if error 12 | -------------------------------------------------------------------------------- /matlabPool/closematlabpool.m: -------------------------------------------------------------------------------- 1 | function closematlabpool() 2 | 3 | if (verLessThan('matlab', '8.3.0')) 4 | if matlabpool('size') > 0 5 | return 6 | end 7 | matlabpool close 8 | else 9 | if ~isempty(gcp('nocreate')) 10 | return 11 | end 12 | delete(gcp('nocreate')); 13 | end 14 | 15 | % clean up pool folder 16 | dirs = dir('~/.matlab/local_pools'); 17 | for d=1:numel(dirs) 18 | if strcmp(dirs(d).name,'.') || strcmp(dirs(d).name,'..') 19 | continue; 20 | end 21 | files = what(['~/.matlab/local_pools/',dirs(d).name]); 22 | % delete the leftover metadata mat file 23 | if numel(files.mat) == 1 24 | delete(sprintf('%s/%s',files.path,files.mat{1})); 25 | end 26 | % delete the directory 27 | if numel(files.mat) < 2 28 | rmdir(files.path); 29 | end 30 | end 31 | 32 | -------------------------------------------------------------------------------- /matlabPool/startmatlabpool.m: -------------------------------------------------------------------------------- 1 | function startmatlabpool(max_workers) 2 | 3 | if (verLessThan('matlab', '8.3.0')) 4 | if matlabpool('size') > 0 5 | return 6 | end 7 | else 8 | if ~isempty(gcp('nocreate')) 9 | return 10 | end 11 | end 12 | 13 | found = false; 14 | for i = 1 : 1000 15 | fname = sprintf('~/.matlab/local_pools/pool.%d', i); 16 | if ~isdir(fname) 17 | found = true; 18 | break; 19 | end 20 | end 21 | if (~found) 22 | error('Couldn''t find an empty MATLAB pool to use.') 23 | end 24 | mkdir(fname); 25 | disp(fname) 26 | if (verLessThan('matlab', '8.3.0')) 27 | schd = findResource('scheduler', 'configuration', defaultParallelConfig); 28 | schd.DataLocation = fname; 29 | if(exist('max_workers', 'var')) 30 | matlabpool(schd, max_workers); 31 | else 32 | matlabpool(schd); 33 | end 34 | else 35 | schd = parcluster(parallel.defaultClusterProfile); 36 | set(schd, 'JobStorageLocation', fname); 37 | if(exist('max_workers', 'var')) 38 | parpool(schd, max_workers); 39 | else 40 | parpool(schd); 41 | end 42 | end 43 | 44 | 45 | -------------------------------------------------------------------------------- /mex_unix/bounded_dt.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/bounded_dt.mexa64 -------------------------------------------------------------------------------- /mex_unix/bounded_shiftdt.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/bounded_shiftdt.mexa64 -------------------------------------------------------------------------------- /mex_unix/bounded_shiftdt_.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/bounded_shiftdt_.mexa64 -------------------------------------------------------------------------------- /mex_unix/fast_bounded_dt.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fast_bounded_dt.mexa64 -------------------------------------------------------------------------------- /mex_unix/fast_bounded_shifdt.cc: -------------------------------------------------------------------------------- 1 | // This is a part of the code from 2 | // (http://people.cs.uchicago.edu/~rbg/latent/) 3 | // Copyright (C) 2013 Ross Girshick 4 | // 5 | // with the modification that it doesn't allocate new memory for the output. 6 | // Also, it computes shifted dt rather than dt. 7 | // ------------------------------------------------------- 8 | 9 | #include "mex.h" 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | // Generalized distance transforms based on Felzenszwalb and Huttenlocher's 17 | // "Distance Transform of Sampled Functions." Theory of Computing, Vol. 8, 18 | // No. 19, 2012. 19 | // 20 | // The cost function implemented here is a bounded quadratic: 21 | // d(x) = a*x^2 + b*x if x \in [-range, range] 22 | // d(x) = inf otherwise 23 | // 24 | // Some ideas for speeding up this code (i.e. precomputing division factors) 25 | // were taken from Charles Dubout's ffld code (http://www.idiap.ch/~cdubout/ 26 | // code/ffld.tar.gz). 27 | 28 | static double eps = 0.00001; 29 | 30 | static inline int square(int x) { return x*x; } 31 | 32 | void dt1d(const double *src, double *dst, int *ptr, 33 | int step, int n, double a, double b, int dshift, double range, 34 | int *v, double *z, double *t) { 35 | int k = 0; 36 | v[0] = 0; 37 | z[0] = -INFINITY; 38 | z[1] = +INFINITY; 39 | 40 | double a_inv = 1/a; 41 | 42 | for (int q = 1; q <= n-1; q++) { 43 | // compute unbounded point of intersection 44 | double s = 0.5 * ((src[q*step] - src[v[k]*step]) * t[q - v[k]] 45 | + q + v[k] 46 | - b * a_inv); 47 | 48 | // bound point of intersection; +/- eps to handle boundary conditions 49 | s = min(v[k]+range+eps, max(q-range-eps, s)); 50 | 51 | while (s <= z[k]) { 52 | // delete dominiated parabola 53 | k--; 54 | s = 0.5 * ((src[q*step] - src[v[k]*step]) * t[q - v[k]] 55 | + q + v[k] 56 | - b * a_inv); 57 | s = min(v[k]+range+eps, max(q-range-eps, s)); 58 | } 59 | k++; 60 | v[k] = q; 61 | z[k] = s; 62 | } 63 | z[k+1] = INFINITY; 64 | 65 | k = 0; 66 | for (int i = 0, q = dshift; i <= n-1; i++, q++) { 67 | while (z[k+1] < q) 68 | { 69 | k++; 70 | } 71 | dst[i*step] = a*square(q-v[k]) + b*(q-v[k]) + src[v[k]*step]; 72 | ptr[i*step] = v[k]; 73 | } 74 | } 75 | 76 | // matlab entry point 77 | // fast_bounded_dt(M, Ix, Iy, start_idx, vals, ax, bx, ay, by, offx, offy, range) 78 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 79 | if (nrhs != 12) 80 | mexErrMsgTxt("Wrong number of inputs"); 81 | if (nlhs != 0) 82 | mexErrMsgTxt("Wrong number of outputs"); 83 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) 84 | mexErrMsgTxt("Invalid input"); 85 | 86 | enum { 87 | OUT_VALS = 0, 88 | OUT_IXP, 89 | OUT_IYP, 90 | OUT_VALS_ST, 91 | IN_VALS, 92 | IN_AX, 93 | IN_BX, 94 | IN_AY, 95 | IN_BY, 96 | OFF_X, 97 | OFF_Y, 98 | IN_RANGE 99 | }; 100 | 101 | const int *out_dims = mxGetDimensions(prhs[OUT_VALS]); 102 | double *M = (double *)mxGetPr(prhs[OUT_VALS]); 103 | int M_st = (int)mxGetScalar(prhs[OUT_VALS_ST]); 104 | M = M + M_st; 105 | int32_t *Ix = (int32_t *)mxGetPr(prhs[OUT_IXP]); 106 | Ix = Ix + M_st; 107 | int32_t *Iy = (int32_t *)mxGetPr(prhs[OUT_IYP]); 108 | Iy = Iy + M_st; 109 | 110 | const int *dims = mxGetDimensions(prhs[IN_VALS]); 111 | const double *vals = (double *)mxGetPr(prhs[IN_VALS]); 112 | 113 | double ax = mxGetScalar(prhs[IN_AX]); 114 | double bx = mxGetScalar(prhs[IN_BX]); 115 | double ay = mxGetScalar(prhs[IN_AY]); 116 | double by = mxGetScalar(prhs[IN_BY]); 117 | int offx = (int)mxGetScalar(prhs[OFF_X])-1; 118 | int offy = (int)mxGetScalar(prhs[OFF_Y])-1; 119 | double range = mxGetScalar(prhs[IN_RANGE]); 120 | 121 | double *tmpM = (double *)mxCalloc(dims[0]*dims[1], sizeof(double)); 122 | int32_t *tmpIx = (int32_t *)mxCalloc(dims[0]*dims[1], sizeof(int32_t)); 123 | int32_t *tmpIy = (int32_t *)mxCalloc(dims[0]*dims[1], sizeof(int32_t)); 124 | 125 | // temporary storage used by 1d distance transforms 126 | int *v = new int[max(dims[0], dims[1])]; 127 | double *z = new double[max(dims[0], dims[1]) + 1]; 128 | double *t = new double[max(dims[0], dims[1])]; 129 | 130 | // cache divisive factors used in 1d distance transforms 131 | t[0] = INFINITY; 132 | for (int y = 1; y < dims[0]; y++) 133 | t[y] = 1 / (-ay * y); 134 | 135 | for (int x = 0; x < dims[1]; x++) 136 | dt1d(vals+x*dims[0], tmpM+x*dims[0], tmpIy+x*dims[0], 1, dims[0], 137 | -ay, -by, offy, range, v, z, t); 138 | 139 | // cache divisive factors used in 1d distance transforms 140 | for (int x = 1; x < dims[1]; x++) 141 | t[x] = 1 / (-ax * x); 142 | 143 | for (int y = 0; y < dims[0]; y++) 144 | dt1d(tmpM+y, M+y, tmpIx+y, dims[0], dims[1], 145 | -ax, -bx, offx, range, v, z, t); 146 | 147 | // get argmaxes and adjust for matlab indexing from 1 148 | for (int x = 0; x < dims[1]; x++) { 149 | for (int y = 0; y < dims[0]; y++) { 150 | int p = x*dims[0]+y; 151 | Ix[p] = tmpIx[p]+1; 152 | Iy[p] = tmpIy[tmpIx[p]*dims[0]+y]+1; 153 | } 154 | } 155 | 156 | delete [] v; 157 | delete [] z; 158 | delete [] t; 159 | 160 | mxFree(tmpM); 161 | mxFree(tmpIx); 162 | mxFree(tmpIy); 163 | } 164 | -------------------------------------------------------------------------------- /mex_unix/fast_bounded_shifdt.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fast_bounded_shifdt.mexa64 -------------------------------------------------------------------------------- /mex_unix/fast_bounded_shifdt_nc.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fast_bounded_shifdt_nc.mexa64 -------------------------------------------------------------------------------- /mex_unix/fast_bounded_shifdt_ncw.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fast_bounded_shifdt_ncw.mexa64 -------------------------------------------------------------------------------- /mex_unix/fast_update_score.cc: -------------------------------------------------------------------------------- 1 | #define INF 1E20 2 | #include 3 | #include 4 | #include 5 | #include "mex.h" 6 | 7 | /* 8 | * updatescore.cc 9 | * A helper function for message passing. 10 | * This function performs second step of message passing which is explained in section 11 | * 3.2 of our "Occlusion Coherence: Detecting and Localizing Occluded Faces" paper. 12 | */ 13 | #define gv2(A,dim1,i,j) A[(i)+(j)*dim1] 14 | 15 | // matlab entry point 16 | // [score, Ix, Iy, Im] = dt(scorep, Ixp, Iyp, defid, occfilter, leaf, b, localdefid, K, L, Ny, Nx) 17 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 18 | if (nrhs != 12) 19 | mexErrMsgTxt("Wrong number of inputs"); 20 | if (nlhs != 4) 21 | mexErrMsgTxt("Wrong number of outputs"); 22 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) 23 | mexErrMsgTxt("Invalid input"); 24 | 25 | // Read in ... 26 | double *scorep = (double *)mxGetPr(prhs[0]); 27 | int32_t *Ixp = (int32_t *)mxGetPr(prhs[1]); 28 | int32_t *Iyp = (int32_t *)mxGetPr(prhs[2]); 29 | double *defid = (double *)mxGetPr(prhs[3]); 30 | mxLogical *occfilter = (mxLogical *)mxGetLogicals(prhs[4]); 31 | int leaf = (int)mxGetScalar(prhs[5]); 32 | double *b = (double *)mxGetPr(prhs[6]); 33 | double *localdefid = (double *)mxGetPr(prhs[7]); 34 | int K = (int)mxGetScalar(prhs[8]); 35 | int L = (int)mxGetScalar(prhs[9]); 36 | int Ny = (int)mxGetScalar(prhs[10]); 37 | int Nx = (int)mxGetScalar(prhs[11]); 38 | 39 | mwSize ndim = 3; 40 | mwSize dims[3] = {Ny, Nx, L}; 41 | mxArray *mxScore = mxCreateNumericArray(ndim, dims, mxDOUBLE_CLASS, mxREAL); 42 | mxArray *mxIy = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL); 43 | mxArray *mxIx = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL); 44 | mxArray *mxIm = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL); 45 | double *score = (double *)mxGetPr(mxScore); 46 | int *Iy = (int *)mxGetPr(mxIy); 47 | int *Ix = (int *)mxGetPr(mxIx); 48 | int *Im = (int *)mxGetPr(mxIm); 49 | 50 | double init_value[20]; 51 | int init_im[20]; 52 | 53 | for(int l=0; l0 && gv2(b,L,l,k)>init_value[l]) 64 | { 65 | init_value[l] = gv2(b,L,l,k); 66 | init_im[l] = k+1; 67 | } 68 | } 69 | } 70 | int * Im_ind = Im; 71 | double *score_ind = score; 72 | for(int l=0; l 0 && (occfilter[k]==0 || leaf==0)) 92 | { 93 | double bias = b[ind_2d]; 94 | int lid = localdefid[ind_2d] - 1; // 0-base 95 | double * scorep_ind = scorep + lid*Ny*Nx; 96 | int * Ixp_ind = Ixp + lid*Ny*Nx; 97 | int * Iyp_ind = Iyp + lid*Ny*Nx; 98 | for(int j=0; j *score_ind) // Computes the max 104 | { 105 | *score_ind = score0; 106 | *Ix_ind = *Ixp_ind; 107 | *Iy_ind = *Iyp_ind; 108 | *Im_ind = k+1; 109 | } 110 | scorep_ind++; 111 | Ixp_ind++; 112 | Iyp_ind++; 113 | 114 | score_ind++; 115 | Im_ind++; 116 | Ix_ind++; 117 | Iy_ind++; 118 | } 119 | } 120 | } 121 | else 122 | { 123 | score_ind += Ny*Nx; 124 | Im_ind += Ny*Nx; 125 | Ix_ind += Ny*Nx; 126 | Iy_ind += Ny*Nx; 127 | } 128 | ind_2d++; 129 | } 130 | } 131 | 132 | plhs[0] = mxScore; 133 | plhs[1] = mxIx; 134 | plhs[2] = mxIy; 135 | plhs[3] = mxIm; 136 | } 137 | -------------------------------------------------------------------------------- /mex_unix/fast_update_score.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fast_update_score.mexa64 -------------------------------------------------------------------------------- /mex_unix/fconv.cc: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include 3 | #include 4 | 5 | /* 6 | * This code is used for computing filter responses. It computes the 7 | * response of a set of filters with a feature map. 8 | * 9 | * Basic version, relatively slow but very compatible. 10 | */ 11 | 12 | struct thread_data { 13 | double *A; 14 | double *B; 15 | double *C; 16 | mxArray *mxC; 17 | const mwSize *A_dims; 18 | const mwSize *B_dims; 19 | mwSize C_dims[2]; 20 | }; 21 | 22 | // convolve A and B 23 | void *process(void *thread_arg) { 24 | thread_data *args = (thread_data *)thread_arg; 25 | double *A = args->A; 26 | double *B = args->B; 27 | double *C = args->C; 28 | const mwSize *A_dims = args->A_dims; 29 | const mwSize *B_dims = args->B_dims; 30 | const mwSize *C_dims = args->C_dims; 31 | int num_features = args->A_dims[2]; 32 | 33 | for (int f = 0; f < num_features; f++) { 34 | double *dst = C; 35 | double *A_src = A + f*A_dims[0]*A_dims[1]; 36 | double *B_src = B + f*B_dims[0]*B_dims[1]; 37 | for (int x = 0; x < C_dims[1]; x++) { 38 | for (int y = 0; y < C_dims[0]; y++) { 39 | double val = 0; 40 | for (int xp = 0; xp < B_dims[1]; xp++) { 41 | double *A_off = A_src + (x+xp)*A_dims[0] + y; 42 | double *B_off = B_src + xp*B_dims[0]; 43 | switch(B_dims[0]) { 44 | case 20: val += A_off[19] * B_off[19]; 45 | case 19: val += A_off[18] * B_off[18]; 46 | case 18: val += A_off[17] * B_off[17]; 47 | case 17: val += A_off[16] * B_off[16]; 48 | case 16: val += A_off[15] * B_off[15]; 49 | case 15: val += A_off[14] * B_off[14]; 50 | case 14: val += A_off[13] * B_off[13]; 51 | case 13: val += A_off[12] * B_off[12]; 52 | case 12: val += A_off[11] * B_off[11]; 53 | case 11: val += A_off[10] * B_off[10]; 54 | case 10: val += A_off[9] * B_off[9]; 55 | case 9: val += A_off[8] * B_off[8]; 56 | case 8: val += A_off[7] * B_off[7]; 57 | case 7: val += A_off[6] * B_off[6]; 58 | case 6: val += A_off[5] * B_off[5]; 59 | case 5: val += A_off[4] * B_off[4]; 60 | case 4: val += A_off[3] * B_off[3]; 61 | case 3: val += A_off[2] * B_off[2]; 62 | case 2: val += A_off[1] * B_off[1]; 63 | case 1: val += A_off[0] * B_off[0]; 64 | break; 65 | default: 66 | for (int yp = 0; yp < B_dims[0]; yp++) { 67 | val += *(A_off++) * *(B_off++); 68 | } 69 | } 70 | } 71 | *(dst++) += val; 72 | } 73 | } 74 | } 75 | return NULL; 76 | } 77 | 78 | // matlab entry point 79 | // C = fconv(A, cell of B, start, end); 80 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 81 | if (nrhs != 4) 82 | mexErrMsgTxt("Wrong number of inputs"); 83 | if (nlhs != 1) 84 | mexErrMsgTxt("Wrong number of outputs"); 85 | 86 | // get A 87 | const mxArray *mxA = prhs[0]; 88 | if (mxGetNumberOfDimensions(mxA) != 3 || 89 | mxGetClassID(mxA) != mxDOUBLE_CLASS) 90 | mexErrMsgTxt("Invalid input: A"); 91 | 92 | // get B and start/end 93 | const mxArray *cellB = prhs[1]; 94 | mwSize num_bs = mxGetNumberOfElements(cellB); 95 | int start = (int)mxGetScalar(prhs[2]) - 1; 96 | int end = (int)mxGetScalar(prhs[3]) - 1; 97 | if (start < 0 || end >= num_bs || start > end) 98 | mexErrMsgTxt("Invalid input: start/end"); 99 | int len = end-start+1; 100 | 101 | // output cell 102 | plhs[0] = mxCreateCellMatrix(1, len); 103 | 104 | // do convolutions 105 | thread_data td; 106 | const mwSize *A_dims = mxGetDimensions(mxA); 107 | double *A = (double *)mxGetPr(mxA); 108 | for (int i = 0; i < len; i++) { 109 | const mxArray *mxB = mxGetCell(cellB, i+start); 110 | td.A_dims = A_dims; 111 | td.A = A; 112 | td.B_dims = mxGetDimensions(mxB); 113 | td.B = (double *)mxGetPr(mxB); 114 | if (mxGetNumberOfDimensions(mxB) != 3 || 115 | mxGetClassID(mxB) != mxDOUBLE_CLASS || 116 | td.A_dims[2] != td.B_dims[2]) 117 | mexErrMsgTxt("Invalid input: B"); 118 | 119 | // compute size of output 120 | int height = td.A_dims[0] - td.B_dims[0] + 1; 121 | int width = td.A_dims[1] - td.B_dims[1] + 1; 122 | if (height < 1 || width < 1) 123 | mexErrMsgTxt("Invalid input: B should be smaller than A"); 124 | td.C_dims[0] = height; 125 | td.C_dims[1] = width; 126 | td.mxC = mxCreateNumericArray(2, td.C_dims, mxDOUBLE_CLASS, mxREAL); 127 | td.C = (double *)mxGetPr(td.mxC); 128 | process((void *)&td); 129 | mxSetCell(plhs[0], i, td.mxC); 130 | } 131 | } 132 | 133 | 134 | -------------------------------------------------------------------------------- /mex_unix/fconv.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/fconv.mexa64 -------------------------------------------------------------------------------- /mex_unix/fconvMT.cc: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include 3 | #include 4 | #include 5 | 6 | /* 7 | * This code is used for computing filter responses. It computes the 8 | * response of a set of filters with a feature map. 9 | * 10 | * Multithreaded version. 11 | */ 12 | 13 | struct thread_data { 14 | double *A; 15 | double *B; 16 | double *C; 17 | mxArray *mxC; 18 | const mwSize *A_dims; 19 | const mwSize *B_dims; 20 | mwSize C_dims[2]; 21 | }; 22 | 23 | // convolve A and B 24 | void *process(void *thread_arg) { 25 | thread_data *args = (thread_data *)thread_arg; 26 | double *A = args->A; 27 | double *B = args->B; 28 | double *C = args->C; 29 | const mwSize *A_dims = args->A_dims; 30 | const mwSize *B_dims = args->B_dims; 31 | const mwSize *C_dims = args->C_dims; 32 | int num_features = args->A_dims[2]; 33 | 34 | for (int f = 0; f < num_features; f++) { 35 | double *dst = C; 36 | double *A_src = A + f*A_dims[0]*A_dims[1]; 37 | double *B_src = B + f*B_dims[0]*B_dims[1]; 38 | for (int x = 0; x < C_dims[1]; x++) { 39 | for (int y = 0; y < C_dims[0]; y++) { 40 | double val = 0; 41 | for (int xp = 0; xp < B_dims[1]; xp++) { 42 | double *A_off = A_src + (x+xp)*A_dims[0] + y; 43 | double *B_off = B_src + xp*B_dims[0]; 44 | switch(B_dims[0]) { 45 | case 20: val += A_off[19] * B_off[19]; 46 | case 19: val += A_off[18] * B_off[18]; 47 | case 18: val += A_off[17] * B_off[17]; 48 | case 17: val += A_off[16] * B_off[16]; 49 | case 16: val += A_off[15] * B_off[15]; 50 | case 15: val += A_off[14] * B_off[14]; 51 | case 14: val += A_off[13] * B_off[13]; 52 | case 13: val += A_off[12] * B_off[12]; 53 | case 12: val += A_off[11] * B_off[11]; 54 | case 11: val += A_off[10] * B_off[10]; 55 | case 10: val += A_off[9] * B_off[9]; 56 | case 9: val += A_off[8] * B_off[8]; 57 | case 8: val += A_off[7] * B_off[7]; 58 | case 7: val += A_off[6] * B_off[6]; 59 | case 6: val += A_off[5] * B_off[5]; 60 | case 5: val += A_off[4] * B_off[4]; 61 | case 4: val += A_off[3] * B_off[3]; 62 | case 3: val += A_off[2] * B_off[2]; 63 | case 2: val += A_off[1] * B_off[1]; 64 | case 1: val += A_off[0] * B_off[0]; 65 | break; 66 | default: 67 | for (int yp = 0; yp < B_dims[0]; yp++) { 68 | val += *(A_off++) * *(B_off++); 69 | } 70 | } 71 | } 72 | *(dst++) += val; 73 | } 74 | } 75 | } 76 | pthread_exit(NULL); 77 | } 78 | 79 | // matlab entry point 80 | // C = fconv(A, cell of B, start, end); 81 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 82 | if (nrhs != 4) 83 | mexErrMsgTxt("Wrong number of inputs"); 84 | if (nlhs != 1) 85 | mexErrMsgTxt("Wrong number of outputs"); 86 | 87 | // get A 88 | const mxArray *mxA = prhs[0]; 89 | if (mxGetNumberOfDimensions(mxA) != 3 || 90 | mxGetClassID(mxA) != mxDOUBLE_CLASS) 91 | mexErrMsgTxt("Invalid input: A"); 92 | 93 | // get B and start/end 94 | const mxArray *cellB = prhs[1]; 95 | mwSize num_bs = mxGetNumberOfElements(cellB); 96 | int start = (int)mxGetScalar(prhs[2]) - 1; 97 | int end = (int)mxGetScalar(prhs[3]) - 1; 98 | if (start < 0 || end >= num_bs || start > end) 99 | mexErrMsgTxt("Invalid input: start/end"); 100 | int len = end-start+1; 101 | 102 | // start threads 103 | thread_data *td = (thread_data *)mxCalloc(len, sizeof(thread_data)); 104 | pthread_t *ts = (pthread_t *)mxCalloc(len, sizeof(pthread_t)); 105 | const mwSize *A_dims = mxGetDimensions(mxA); 106 | double *A = (double *)mxGetPr(mxA); 107 | for (int i = 0; i < len; i++) { 108 | const mxArray *mxB = mxGetCell(cellB, i+start); 109 | td[i].A_dims = A_dims; 110 | td[i].A = A; 111 | td[i].B_dims = mxGetDimensions(mxB); 112 | td[i].B = (double *)mxGetPr(mxB); 113 | if (mxGetNumberOfDimensions(mxB) != 3 || 114 | mxGetClassID(mxB) != mxDOUBLE_CLASS || 115 | td[i].A_dims[2] != td[i].B_dims[2]) 116 | mexErrMsgTxt("Invalid input: B"); 117 | 118 | // compute size of output 119 | int height = td[i].A_dims[0] - td[i].B_dims[0] + 1; 120 | int width = td[i].A_dims[1] - td[i].B_dims[1] + 1; 121 | if (height < 1 || width < 1) 122 | mexErrMsgTxt("Invalid input: B should be smaller than A"); 123 | td[i].C_dims[0] = height; 124 | td[i].C_dims[1] = width; 125 | td[i].mxC = mxCreateNumericArray(2, td[i].C_dims, mxDOUBLE_CLASS, mxREAL); 126 | td[i].C = (double *)mxGetPr(td[i].mxC); 127 | 128 | if (pthread_create(&ts[i], NULL, process, (void *)&td[i])) 129 | mexErrMsgTxt("Error creating thread"); 130 | } 131 | 132 | // wait for the treads to finish and set return values 133 | void *status; 134 | plhs[0] = mxCreateCellMatrix(1, len); 135 | for (int i = 0; i < len; i++) { 136 | pthread_join(ts[i], &status); 137 | mxSetCell(plhs[0], i, td[i].mxC); 138 | } 139 | mxFree(td); 140 | mxFree(ts); 141 | } 142 | 143 | 144 | -------------------------------------------------------------------------------- /mex_unix/fconvblas.cc: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include "blas.h" 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * This code is used for computing filter responses. It computes the 9 | * response of a set of filters with a feature map. 10 | * 11 | * Multithreaded blas version. 12 | */ 13 | 14 | struct thread_data { 15 | double *A; 16 | double *B; 17 | double *C; 18 | mxArray *mxC; 19 | const mwSize *A_dims; 20 | const mwSize *B_dims; 21 | mwSize C_dims[2]; 22 | }; 23 | 24 | double *prepare_filter(double *B, const mwSize *B_dims) { 25 | double *F = (double *)mxCalloc(B_dims[0]*B_dims[1]*B_dims[2], sizeof(double)); 26 | for (int f = 0; f < B_dims[2]; f++) { 27 | for (int x = 0; x < B_dims[1]; x++) { 28 | for (int y = 0; y < B_dims[0]; y++) { 29 | F[f + x*(B_dims[2]) + y*(B_dims[2]*B_dims[1])] = 30 | B[y + x*B_dims[0] + f*(B_dims[0]*B_dims[1])]; 31 | } 32 | } 33 | } 34 | return F; 35 | } 36 | 37 | double *prepare_map(double *A, const mwSize *A_dims) { 38 | double *F = (double *)mxCalloc(A_dims[0]*A_dims[1]*A_dims[2], sizeof(double)); 39 | for (int f = 0; f < A_dims[2]; f++) { 40 | for (int x = 0; x < A_dims[1]; x++) { 41 | for (int y = 0; y < A_dims[0]; y++) { 42 | F[y + f*A_dims[0] + x*(A_dims[0]*A_dims[2])] = 43 | A[y + x*A_dims[0] + f*(A_dims[0]*A_dims[1])]; 44 | } 45 | } 46 | } 47 | return F; 48 | } 49 | 50 | // convolve A and B using blas 51 | void *process(void *thread_arg) { 52 | thread_data *args = (thread_data *)thread_arg; 53 | double *A = args->A; 54 | double *B = args->B; 55 | double *C = args->C; 56 | const mwSize *A_dims = args->A_dims; 57 | const mwSize *B_dims = args->B_dims; 58 | const mwSize *C_dims = args->C_dims; 59 | 60 | for (int x = 0; x < C_dims[1]; x++) { 61 | for (int y = 0; y < B_dims[0]; y++) { 62 | double *A_off = A + x*(A_dims[0]*A_dims[2]) + y; 63 | double *B_off = B + y*(B_dims[1]*B_dims[2]); 64 | double *C_off = C + x*C_dims[0]; 65 | char chn = 'N'; 66 | double one = 1; 67 | ptrdiff_t m = C_dims[0]; 68 | ptrdiff_t n = B_dims[1]*B_dims[2]; 69 | ptrdiff_t lda = A_dims[0]; 70 | ptrdiff_t incx = 1; 71 | ptrdiff_t incy = 1; 72 | dgemv(&chn, &m, &n, &one, A_off, &lda, B_off, &incx, &one, C_off, &incy); 73 | } 74 | } 75 | } 76 | 77 | // matlab entry point 78 | // C = fconv(A, cell of B, start, end); 79 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 80 | if (nrhs != 4) 81 | mexErrMsgTxt("Wrong number of inputs"); 82 | if (nlhs != 1) 83 | mexErrMsgTxt("Wrong number of outputs"); 84 | 85 | // get A 86 | const mxArray *mxA = prhs[0]; 87 | if (mxGetNumberOfDimensions(mxA) != 3 || 88 | mxGetClassID(mxA) != mxDOUBLE_CLASS) 89 | mexErrMsgTxt("Invalid input: A"); 90 | 91 | // get B and start/end 92 | const mxArray *cellB = prhs[1]; 93 | mwSize num_bs = mxGetNumberOfElements(cellB); 94 | int start = (int)mxGetScalar(prhs[2]) - 1; 95 | int end = (int)mxGetScalar(prhs[3]) - 1; 96 | if (start < 0 || end >= num_bs || start > end) 97 | mexErrMsgTxt("Invalid input: start/end"); 98 | int len = end-start+1; 99 | 100 | // output cell 101 | plhs[0] = mxCreateCellMatrix(1, len); 102 | 103 | // do convolutions 104 | thread_data td; 105 | const mwSize *A_dims = mxGetDimensions(mxA); 106 | double *A = prepare_map((double *)mxGetPr(mxA), A_dims); 107 | for (int i = 0; i < len; i++) { 108 | const mxArray *mxB = mxGetCell(cellB, i+start); 109 | td.A_dims = A_dims; 110 | td.A = A; 111 | td.B_dims = mxGetDimensions(mxB); 112 | td.B = prepare_filter((double *)mxGetPr(mxB), td.B_dims); 113 | if (mxGetNumberOfDimensions(mxB) != 3 || 114 | mxGetClassID(mxB) != mxDOUBLE_CLASS || 115 | td.A_dims[2] != td.B_dims[2]) 116 | mexErrMsgTxt("Invalid input: B"); 117 | 118 | // compute size of output 119 | int height = td.A_dims[0] - td.B_dims[0] + 1; 120 | int width = td.A_dims[1] - td.B_dims[1] + 1; 121 | if (height < 1 || width < 1) 122 | mexErrMsgTxt("Invalid input: B should be smaller than A"); 123 | td.C_dims[0] = height; 124 | td.C_dims[1] = width; 125 | td.mxC = mxCreateNumericArray(2, td.C_dims, mxDOUBLE_CLASS, mxREAL); 126 | td.C = (double *)mxGetPr(td.mxC); 127 | process((void *)&td); 128 | mxSetCell(plhs[0], i, td.mxC); 129 | } 130 | mxFree(A); 131 | } 132 | 133 | -------------------------------------------------------------------------------- /mex_unix/fconvblas_gcc445.cc: -------------------------------------------------------------------------------- 1 | #include "mex.h" 2 | #include "blas.h" 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * This code is used for computing filter responses. It computes the 9 | * response of a set of filters with a feature map. 10 | * 11 | * Multithreaded blas version. 12 | */ 13 | 14 | struct thread_data { 15 | double *A; 16 | double *B; 17 | double *C; 18 | mxArray *mxC; 19 | const mwSize *A_dims; 20 | const mwSize *B_dims; 21 | mwSize C_dims[2]; 22 | }; 23 | 24 | double *prepare_filter(double *B, const mwSize *B_dims) { 25 | double *F = (double *)mxCalloc(B_dims[0]*B_dims[1]*B_dims[2], sizeof(double)); 26 | for (int f = 0; f < B_dims[2]; f++) { 27 | for (int x = 0; x < B_dims[1]; x++) { 28 | for (int y = 0; y < B_dims[0]; y++) { 29 | F[f + x*(B_dims[2]) + y*(B_dims[2]*B_dims[1])] = 30 | B[y + x*B_dims[0] + f*(B_dims[0]*B_dims[1])]; 31 | } 32 | } 33 | } 34 | return F; 35 | } 36 | 37 | double *prepare_map(double *A, const mwSize *A_dims) { 38 | double *F = (double *)mxCalloc(A_dims[0]*A_dims[1]*A_dims[2], sizeof(double)); 39 | for (int f = 0; f < A_dims[2]; f++) { 40 | for (int x = 0; x < A_dims[1]; x++) { 41 | for (int y = 0; y < A_dims[0]; y++) { 42 | F[y + f*A_dims[0] + x*(A_dims[0]*A_dims[2])] = 43 | A[y + x*A_dims[0] + f*(A_dims[0]*A_dims[1])]; 44 | } 45 | } 46 | } 47 | return F; 48 | } 49 | 50 | // convolve A and B using blas 51 | void *process(void *thread_arg) { 52 | thread_data *args = (thread_data *)thread_arg; 53 | double *A = args->A; 54 | double *B = args->B; 55 | double *C = args->C; 56 | const mwSize *A_dims = args->A_dims; 57 | const mwSize *B_dims = args->B_dims; 58 | const mwSize *C_dims = args->C_dims; 59 | 60 | for (int x = 0; x < C_dims[1]; x++) { 61 | for (int y = 0; y < B_dims[0]; y++) { 62 | double *A_off = A + x*(A_dims[0]*A_dims[2]) + y; 63 | double *B_off = B + y*(B_dims[1]*B_dims[2]); 64 | double *C_off = C + x*C_dims[0]; 65 | //const char *chn = "N"; 66 | char chn[] = "N"; 67 | double one = 1; 68 | /* 69 | int m = C_dims[0]; 70 | int n = B_dims[1]*B_dims[2]; 71 | int lda = A_dims[0]; 72 | int incx = 1; 73 | int incy = 1; 74 | */ 75 | ptrdiff_t m = C_dims[0]; 76 | ptrdiff_t n = B_dims[1]*B_dims[2]; 77 | ptrdiff_t lda = A_dims[0]; 78 | ptrdiff_t incx = 1; 79 | ptrdiff_t incy = 1; 80 | dgemv(chn, &m, &n, &one, A_off, &lda, B_off, &incx, &one, C_off, &incy); 81 | } 82 | } 83 | pthread_exit(NULL); 84 | } 85 | 86 | // matlab entry point 87 | // C = fconv(A, cell of B, start, end); 88 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 89 | if (nrhs != 4) 90 | mexErrMsgTxt("Wrong number of inputs"); 91 | if (nlhs != 1) 92 | mexErrMsgTxt("Wrong number of outputs"); 93 | 94 | // get A 95 | const mxArray *mxA = prhs[0]; 96 | if (mxGetNumberOfDimensions(mxA) != 3 || 97 | mxGetClassID(mxA) != mxDOUBLE_CLASS) 98 | mexErrMsgTxt("Invalid input: A"); 99 | 100 | // get B and start/end 101 | const mxArray *cellB = prhs[1]; 102 | mwSize num_bs = mxGetNumberOfElements(cellB); 103 | int start = (int)mxGetScalar(prhs[2]) - 1; 104 | int end = (int)mxGetScalar(prhs[3]) - 1; 105 | if (start < 0 || end >= num_bs || start > end) 106 | mexErrMsgTxt("Invalid input: start/end"); 107 | int len = end-start+1; 108 | 109 | // start threads 110 | thread_data *td = (thread_data *)mxCalloc(len, sizeof(thread_data)); 111 | pthread_t *ts = (pthread_t *)mxCalloc(len, sizeof(pthread_t)); 112 | const mwSize *A_dims = mxGetDimensions(mxA); 113 | double *A = prepare_map((double *)mxGetPr(mxA), A_dims); 114 | for (int i = 0; i < len; i++) { 115 | const mxArray *mxB = mxGetCell(cellB, i+start); 116 | td[i].A_dims = A_dims; 117 | td[i].A = A; 118 | td[i].B_dims = mxGetDimensions(mxB); 119 | td[i].B = prepare_filter((double *)mxGetPr(mxB), td[i].B_dims); 120 | if (mxGetNumberOfDimensions(mxB) != 3 || 121 | mxGetClassID(mxB) != mxDOUBLE_CLASS || 122 | td[i].A_dims[2] != td[i].B_dims[2]) 123 | mexErrMsgTxt("Invalid input: B"); 124 | 125 | // compute size of output 126 | int height = td[i].A_dims[0] - td[i].B_dims[0] + 1; 127 | int width = td[i].A_dims[1] - td[i].B_dims[1] + 1; 128 | if (height < 1 || width < 1) 129 | mexErrMsgTxt("Invalid input: B should be smaller than A"); 130 | td[i].C_dims[0] = height; 131 | td[i].C_dims[1] = width; 132 | td[i].mxC = mxCreateNumericArray(2, td[i].C_dims, mxDOUBLE_CLASS, mxREAL); 133 | td[i].C = (double *)mxGetPr(td[i].mxC); 134 | 135 | if (pthread_create(&ts[i], NULL, process, (void *)&td[i])) 136 | mexErrMsgTxt("Error creating thread"); 137 | } 138 | 139 | // wait for the treads to finish and set return values 140 | void *status; 141 | plhs[0] = mxCreateCellMatrix(1, len); 142 | for (int i = 0; i < len; i++) { 143 | pthread_join(ts[i], &status); 144 | mxSetCell(plhs[0], i, td[i].mxC); 145 | mxFree(td[i].B); 146 | } 147 | mxFree(A); 148 | mxFree(td); 149 | mxFree(ts); 150 | } 151 | 152 | -------------------------------------------------------------------------------- /mex_unix/features.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "mex.h" 3 | 4 | #define round(x) ((x-floor(x))>0.5 ? ceil(x) : floor(x)) 5 | 6 | // small value, used to avoid division by zero 7 | #define eps 0.0001 8 | 9 | // unit vectors used to compute gradient orientation 10 | double uu[9] = {1.0000, 11 | 0.9397, 12 | 0.7660, 13 | 0.500, 14 | 0.1736, 15 | -0.1736, 16 | -0.5000, 17 | -0.7660, 18 | -0.9397}; 19 | double vv[9] = {0.0000, 20 | 0.3420, 21 | 0.6428, 22 | 0.8660, 23 | 0.9848, 24 | 0.9848, 25 | 0.8660, 26 | 0.6428, 27 | 0.3420}; 28 | 29 | static inline double min(double x, double y) { return (x <= y ? x : y); } 30 | static inline double max(double x, double y) { return (x <= y ? y : x); } 31 | 32 | static inline int min(int x, int y) { return (x <= y ? x : y); } 33 | static inline int max(int x, int y) { return (x <= y ? y : x); } 34 | 35 | // main function: 36 | // takes a double color image and a bin size 37 | // returns HOG features 38 | mxArray *process(const mxArray *mximage, const mxArray *mxsbin) { 39 | double *im = (double *)mxGetPr(mximage); 40 | const int *dims = mxGetDimensions(mximage); 41 | if (mxGetNumberOfDimensions(mximage) != 3 || 42 | dims[2] != 3 || 43 | mxGetClassID(mximage) != mxDOUBLE_CLASS) 44 | mexErrMsgTxt("Invalid input"); 45 | 46 | int sbin = (int)mxGetScalar(mxsbin); 47 | 48 | // memory for caching orientation histograms & their norms 49 | int blocks[2]; 50 | blocks[0] = (int)round((double)dims[0]/(double)sbin); 51 | blocks[1] = (int)round((double)dims[1]/(double)sbin); 52 | double *hist = (double *)mxCalloc(blocks[0]*blocks[1]*18, sizeof(double)); 53 | double *norm = (double *)mxCalloc(blocks[0]*blocks[1], sizeof(double)); 54 | 55 | // memory for HOG features 56 | int out[3]; 57 | out[0] = max(blocks[0]-2, 0); 58 | out[1] = max(blocks[1]-2, 0); 59 | out[2] = 27+4+1; 60 | mxArray *mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL); 61 | double *feat = (double *)mxGetPr(mxfeat); 62 | 63 | int visible[2]; 64 | visible[0] = blocks[0]*sbin; 65 | visible[1] = blocks[1]*sbin; 66 | 67 | for (int x = 1; x < visible[1]-1; x++) { 68 | for (int y = 1; y < visible[0]-1; y++) { 69 | // first color channel 70 | double *s = im + min(x, dims[1]-2)*dims[0] + min(y, dims[0]-2); 71 | double dy = *(s+1) - *(s-1); 72 | double dx = *(s+dims[0]) - *(s-dims[0]); 73 | double v = dx*dx + dy*dy; 74 | 75 | // second color channel 76 | s += dims[0]*dims[1]; 77 | double dy2 = *(s+1) - *(s-1); 78 | double dx2 = *(s+dims[0]) - *(s-dims[0]); 79 | double v2 = dx2*dx2 + dy2*dy2; 80 | 81 | // third color channel 82 | s += dims[0]*dims[1]; 83 | double dy3 = *(s+1) - *(s-1); 84 | double dx3 = *(s+dims[0]) - *(s-dims[0]); 85 | double v3 = dx3*dx3 + dy3*dy3; 86 | 87 | // pick channel with strongest gradient 88 | if (v2 > v) { 89 | v = v2; 90 | dx = dx2; 91 | dy = dy2; 92 | } 93 | if (v3 > v) { 94 | v = v3; 95 | dx = dx3; 96 | dy = dy3; 97 | } 98 | 99 | // snap to one of 18 orientations 100 | double best_dot = 0; 101 | int best_o = 0; 102 | for (int o = 0; o < 9; o++) { 103 | double dot = uu[o]*dx + vv[o]*dy; 104 | if (dot > best_dot) { 105 | best_dot = dot; 106 | best_o = o; 107 | } else if (-dot > best_dot) { 108 | best_dot = -dot; 109 | best_o = o+9; 110 | } 111 | } 112 | 113 | // add to 4 histograms around pixel using linear interpolation 114 | double xp = ((double)x+0.5)/(double)sbin - 0.5; 115 | double yp = ((double)y+0.5)/(double)sbin - 0.5; 116 | int ixp = (int)floor(xp); 117 | int iyp = (int)floor(yp); 118 | double vx0 = xp-ixp; 119 | double vy0 = yp-iyp; 120 | double vx1 = 1.0-vx0; 121 | double vy1 = 1.0-vy0; 122 | v = sqrt(v); 123 | 124 | if (ixp >= 0 && iyp >= 0) { 125 | *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 126 | vx1*vy1*v; 127 | } 128 | 129 | if (ixp+1 < blocks[1] && iyp >= 0) { 130 | *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) += 131 | vx0*vy1*v; 132 | } 133 | 134 | if (ixp >= 0 && iyp+1 < blocks[0]) { 135 | *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 136 | vx1*vy0*v; 137 | } 138 | 139 | if (ixp+1 < blocks[1] && iyp+1 < blocks[0]) { 140 | *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) += 141 | vx0*vy0*v; 142 | } 143 | } 144 | } 145 | 146 | // compute energy in each block by summing over orientations 147 | for (int o = 0; o < 9; o++) { 148 | double *src1 = hist + o*blocks[0]*blocks[1]; 149 | double *src2 = hist + (o+9)*blocks[0]*blocks[1]; 150 | double *dst = norm; 151 | double *end = norm + blocks[1]*blocks[0]; 152 | while (dst < end) { 153 | *(dst++) += (*src1 + *src2) * (*src1 + *src2); 154 | src1++; 155 | src2++; 156 | } 157 | } 158 | 159 | // compute features 160 | for (int x = 0; x < out[1]; x++) { 161 | for (int y = 0; y < out[0]; y++) { 162 | double *dst = feat + x*out[0] + y; 163 | double *src, *p, n1, n2, n3, n4; 164 | 165 | p = norm + (x+1)*blocks[0] + y+1; 166 | n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 167 | p = norm + (x+1)*blocks[0] + y; 168 | n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 169 | p = norm + x*blocks[0] + y+1; 170 | n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 171 | p = norm + x*blocks[0] + y; 172 | n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps); 173 | 174 | double t1 = 0; 175 | double t2 = 0; 176 | double t3 = 0; 177 | double t4 = 0; 178 | 179 | // contrast-sensitive features 180 | src = hist + (x+1)*blocks[0] + (y+1); 181 | for (int o = 0; o < 18; o++) { 182 | double h1 = min(*src * n1, 0.2); 183 | double h2 = min(*src * n2, 0.2); 184 | double h3 = min(*src * n3, 0.2); 185 | double h4 = min(*src * n4, 0.2); 186 | *dst = 0.5 * (h1 + h2 + h3 + h4); 187 | t1 += h1; 188 | t2 += h2; 189 | t3 += h3; 190 | t4 += h4; 191 | dst += out[0]*out[1]; 192 | src += blocks[0]*blocks[1]; 193 | } 194 | 195 | // contrast-insensitive features 196 | src = hist + (x+1)*blocks[0] + (y+1); 197 | for (int o = 0; o < 9; o++) { 198 | double sum = *src + *(src + 9*blocks[0]*blocks[1]); 199 | double h1 = min(sum * n1, 0.2); 200 | double h2 = min(sum * n2, 0.2); 201 | double h3 = min(sum * n3, 0.2); 202 | double h4 = min(sum * n4, 0.2); 203 | *dst = 0.5 * (h1 + h2 + h3 + h4); 204 | dst += out[0]*out[1]; 205 | src += blocks[0]*blocks[1]; 206 | } 207 | 208 | // texture features 209 | *dst = 0.2357 * t1; 210 | dst += out[0]*out[1]; 211 | *dst = 0.2357 * t2; 212 | dst += out[0]*out[1]; 213 | *dst = 0.2357 * t3; 214 | dst += out[0]*out[1]; 215 | *dst = 0.2357 * t4; 216 | 217 | // truncation feature 218 | dst += out[0]*out[1]; 219 | *dst = 0; 220 | } 221 | } 222 | 223 | mxFree(hist); 224 | mxFree(norm); 225 | return mxfeat; 226 | } 227 | 228 | // matlab entry point 229 | // F = features(image, bin) 230 | // image should be color with double values 231 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 232 | if (nrhs != 2) 233 | mexErrMsgTxt("Wrong number of inputs"); 234 | if (nlhs != 1) 235 | mexErrMsgTxt("Wrong number of outputs"); 236 | plhs[0] = process(prhs[0], prhs[1]); 237 | } 238 | 239 | 240 | 241 | -------------------------------------------------------------------------------- /mex_unix/features.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/features.mexa64 -------------------------------------------------------------------------------- /mex_unix/passmessage.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/passmessage.mexa64 -------------------------------------------------------------------------------- /mex_unix/reduce.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mex.h" 5 | 6 | #define round(x) ((x-floor(x))>0.5 ? ceil(x) : floor(x)) 7 | 8 | // reduce(im) resizes im to half its size, using a 5-tap binomial filter for anti-aliasing 9 | // (see Burt & Adelson's Laplacian Pyramid paper) 10 | 11 | // reduce each column 12 | // result is transposed, so we can apply it twice for a complete reduction 13 | void reduce1dtran(double *src, int sheight, double *dst, int dheight, 14 | int width, int chan) { 15 | // resize each column of each color channel 16 | //bzero(dst, chan*width*dheight*sizeof(double)); 17 | memset(dst, 0, chan*width*dheight*sizeof(double)); 18 | int y; 19 | double *s, *d; 20 | 21 | for (int c = 0; c < chan; c++) { 22 | for (int x = 0; x < width; x++) { 23 | s = src + c*width*sheight + x*sheight; 24 | d = dst + c*dheight*width + x; 25 | 26 | // First row 27 | *d = s[0]*.6875 + s[1]*.2500 + s[2]*.0625; 28 | 29 | for (y = 1; y < dheight-2; y++) { 30 | s += 2; 31 | d += width; 32 | *d = s[-2]*0.0625 + s[-1]*.25 + s[0]*.375 + s[1]*.25 + s[2]*.0625; 33 | } 34 | 35 | // Last two rows 36 | s += 2; 37 | d += width; 38 | if (dheight*2 <= sheight) { 39 | *d = s[-2]*0.0625 + s[-1]*.25 + s[0]*.375 + s[1]*.25 + s[2]*.0625; 40 | } else { 41 | *d = s[1]*.3125 + s[0]*.3750 + s[-1]*.2500 + s[-2]*.0625; 42 | } 43 | s += 2; 44 | d += width; 45 | *d = s[0]*.6875 + s[-1]*.2500 + s[-2]*.0625; 46 | } 47 | } 48 | } 49 | 50 | // main function 51 | // takes a double color image and a scaling factor 52 | // returns resized image 53 | mxArray *reduce(const mxArray *mxsrc) { 54 | double *src = (double *)mxGetPr(mxsrc); 55 | const int *sdims = mxGetDimensions(mxsrc); 56 | if (mxGetNumberOfDimensions(mxsrc) != 3 || 57 | mxGetClassID(mxsrc) != mxDOUBLE_CLASS) 58 | mexErrMsgTxt("Invalid input"); 59 | 60 | int ddims[3]; 61 | ddims[0] = (int)round(sdims[0]*.5); 62 | ddims[1] = (int)round(sdims[1]*.5); 63 | ddims[2] = sdims[2]; 64 | mxArray *mxdst = mxCreateNumericArray(3, ddims, mxDOUBLE_CLASS, mxREAL); 65 | double *dst = (double *)mxGetPr(mxdst); 66 | 67 | double *tmp = (double *)mxCalloc(ddims[0]*sdims[1]*sdims[2], sizeof(double)); 68 | reduce1dtran(src, sdims[0], tmp, ddims[0], sdims[1], sdims[2]); 69 | reduce1dtran(tmp, sdims[1], dst, ddims[1], ddims[0], sdims[2]); 70 | mxFree(tmp); 71 | 72 | return mxdst; 73 | } 74 | 75 | // matlab entry point 76 | // dst = resize(src, scale) 77 | // image should be color with double values 78 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 79 | if (nrhs != 1) 80 | mexErrMsgTxt("Wrong number of inputs"); 81 | if (nlhs != 1) 82 | mexErrMsgTxt("Wrong number of outputs"); 83 | plhs[0] = reduce(prhs[0]); 84 | } 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /mex_unix/reduce.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/reduce.mexa64 -------------------------------------------------------------------------------- /mex_unix/resize.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "mex.h" 5 | 6 | #define round(x) ((x-floor(x))>0.5 ? ceil(x) : floor(x)) 7 | 8 | /* 9 | * Fast image subsampling. 10 | * This is used to construct the feature pyramid. 11 | */ 12 | 13 | // struct used for caching interpolation values 14 | struct alphainfo { 15 | int si, di; 16 | double alpha; 17 | }; 18 | 19 | // copy src into dst using pre-computed interpolation values 20 | void alphacopy(double *src, double *dst, struct alphainfo *ofs, int n) { 21 | struct alphainfo *end = ofs + n; 22 | while (ofs != end) { 23 | dst[ofs->di] += ofs->alpha * src[ofs->si]; 24 | ofs++; 25 | } 26 | } 27 | 28 | // resize along each column 29 | // result is transposed, so we can apply it twice for a complete resize 30 | void resize1dtran(double *src, int sheight, double *dst, int dheight, 31 | int width, int chan) { 32 | double scale = (double)dheight/(double)sheight; 33 | double invscale = (double)sheight/(double)dheight; 34 | 35 | // we cache the interpolation values since they can be 36 | // shared among different columns 37 | int len = (int)ceil(dheight*invscale) + 2*dheight; 38 | //alphainfo ofs[len]; 39 | alphainfo *ofs = new alphainfo[len]; 40 | int k = 0; 41 | for (int dy = 0; dy < dheight; dy++) { 42 | double fsy1 = dy * invscale; 43 | double fsy2 = fsy1 + invscale; 44 | int sy1 = (int)ceil(fsy1); 45 | int sy2 = (int)floor(fsy2); 46 | 47 | if (sy1 - fsy1 > 1e-3) { 48 | assert(k < len); 49 | assert(sy-1 >= 0); 50 | ofs[k].di = dy*width; 51 | ofs[k].si = sy1-1; 52 | ofs[k++].alpha = (sy1 - fsy1) * scale; 53 | } 54 | 55 | for (int sy = sy1; sy < sy2; sy++) { 56 | assert(k < len); 57 | assert(sy < sheight); 58 | ofs[k].di = dy*width; 59 | ofs[k].si = sy; 60 | ofs[k++].alpha = scale; 61 | } 62 | 63 | if (fsy2 - sy2 > 1e-3) { 64 | assert(k < len); 65 | assert(sy2 < sheight); 66 | ofs[k].di = dy*width; 67 | ofs[k].si = sy2; 68 | ofs[k++].alpha = (fsy2 - sy2) * scale; 69 | } 70 | } 71 | 72 | // resize each column of each color channel 73 | // bzero(dst, chan*width*dheight*sizeof(double)); 74 | memset(dst, 0, chan*width*dheight*sizeof(double)); 75 | for (int c = 0; c < chan; c++) { 76 | for (int x = 0; x < width; x++) { 77 | double *s = src + c*width*sheight + x*sheight; 78 | double *d = dst + c*width*dheight + x; 79 | alphacopy(s, d, ofs, k); 80 | } 81 | } 82 | } 83 | 84 | // main function 85 | // takes a double color image and a scaling factor 86 | // returns resized image 87 | mxArray *resize(const mxArray *mxsrc, const mxArray *mxscale) { 88 | double *src = (double *)mxGetPr(mxsrc); 89 | const int *sdims = mxGetDimensions(mxsrc); 90 | if (mxGetNumberOfDimensions(mxsrc) != 3 || 91 | mxGetClassID(mxsrc) != mxDOUBLE_CLASS) 92 | mexErrMsgTxt("Invalid input"); 93 | 94 | double scale = mxGetScalar(mxscale); 95 | if (scale > 1) 96 | mexErrMsgTxt("Invalid scaling factor"); 97 | 98 | int ddims[3]; 99 | ddims[0] = (int)round(sdims[0]*scale); 100 | ddims[1] = (int)round(sdims[1]*scale); 101 | ddims[2] = sdims[2]; 102 | mxArray *mxdst = mxCreateNumericArray(3, ddims, mxDOUBLE_CLASS, mxREAL); 103 | double *dst = (double *)mxGetPr(mxdst); 104 | 105 | double *tmp = (double *)mxCalloc(ddims[0]*sdims[1]*sdims[2], sizeof(double)); 106 | resize1dtran(src, sdims[0], tmp, ddims[0], sdims[1], sdims[2]); 107 | resize1dtran(tmp, sdims[1], dst, ddims[1], ddims[0], sdims[2]); 108 | mxFree(tmp); 109 | 110 | return mxdst; 111 | } 112 | 113 | // matlab entry point 114 | // dst = resize(src, scale) 115 | // image should be color with double values 116 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 117 | if (nrhs != 2) 118 | mexErrMsgTxt("Wrong number of inputs"); 119 | if (nlhs != 1) 120 | mexErrMsgTxt("Wrong number of outputs"); 121 | plhs[0] = resize(prhs[0], prhs[1]); 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /mex_unix/resize.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/resize.mexa64 -------------------------------------------------------------------------------- /mex_unix/shiftdt.cc: -------------------------------------------------------------------------------- 1 | #define INF 1E20 2 | #include 3 | #include 4 | #include "mex.h" 5 | #include "assert.h" 6 | 7 | /* 8 | * shiftdt.cc 9 | * Generalized distance transforms based on Felzenswalb and Huttenlocher. 10 | * This applies computes a min convolution of an arbitrary quadratic function ax^2 + bx 11 | * This outputs results on an shifted, subsampled grid (useful for passing messages between variables in different domains) 12 | */ 13 | 14 | static inline int square(int x) { return x*x; } 15 | 16 | // dt1d(source,destination_val,destination_ptr,source_step,source_length, 17 | // a,b,dest_shift,dest_length,dest_step) 18 | void dt1d(double *src, double *dst, int *ptr, int step, int len, double a, double b, int dshift, int dlen, double dstep) { 19 | int *v = new int[len]; 20 | float *z = new float[len+1]; 21 | int k = 0; 22 | int q = 0; 23 | v[0] = 0; 24 | z[0] = -INF; 25 | z[1] = +INF; 26 | 27 | for (q = 1; q <= len-1; q++) { 28 | float s = ((src[q*step] - src[v[k]*step]) - b*(q - v[k]) + a*(square(q) - square(v[k]))) / (2*a*(q-v[k])); 29 | while (s <= z[k]) { 30 | k--; 31 | s = ((src[q*step] - src[v[k]*step]) - b*(q - v[k]) + a*(square(q) - square(v[k]))) / (2*a*(q-v[k])); 32 | } 33 | k++; 34 | v[k] = q; 35 | z[k] = s; 36 | z[k+1] = +INF; 37 | } 38 | 39 | k = 0; 40 | q = dshift; 41 | 42 | for (int i=0; i <= dlen-1; i++) { 43 | while (z[k+1] < q) 44 | k++; 45 | dst[i*step] = a*square(q-v[k]) + b*(q-v[k]) + src[v[k]*step]; 46 | ptr[i*step] = v[k]; 47 | q += dstep; 48 | } 49 | 50 | delete [] v; 51 | delete [] z; 52 | } 53 | 54 | 55 | 56 | // matlab entry point 57 | // [M, Ix, Iy] = dt(vals, ax, bx, ay, by) 58 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 59 | if (nrhs != 10) 60 | mexErrMsgTxt("Wrong number of inputs"); 61 | if (nlhs != 3) 62 | mexErrMsgTxt("Wrong number of outputs"); 63 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) 64 | mexErrMsgTxt("Invalid input"); 65 | 66 | // Read in deformation coefficients, negating to define a cost 67 | // Read in offsets for output grid, fixing MATLAB 0-1 indexing 68 | double *vals = (double *)mxGetPr(prhs[0]); 69 | int sizx = mxGetN(prhs[0]); 70 | int sizy = mxGetM(prhs[0]); 71 | double ax = -mxGetScalar(prhs[1]); 72 | double bx = -mxGetScalar(prhs[2]); 73 | double ay = -mxGetScalar(prhs[3]); 74 | double by = -mxGetScalar(prhs[4]); 75 | int offx = (int)mxGetScalar(prhs[5])-1; 76 | int offy = (int)mxGetScalar(prhs[6])-1; 77 | int lenx = (int)mxGetScalar(prhs[7]); 78 | int leny = (int)mxGetScalar(prhs[8]); 79 | double step = mxGetScalar(prhs[9]); 80 | 81 | 82 | mxArray *mxM = mxCreateNumericMatrix(leny,lenx,mxDOUBLE_CLASS, mxREAL); 83 | mxArray *mxIy = mxCreateNumericMatrix(leny,lenx,mxINT32_CLASS, mxREAL); 84 | mxArray *mxIx = mxCreateNumericMatrix(leny,lenx,mxINT32_CLASS, mxREAL); 85 | double *M = (double *)mxGetPr(mxM); 86 | int *Iy = (int *)mxGetPr(mxIy); 87 | int *Ix = (int *)mxGetPr(mxIx); 88 | 89 | double *tmpM = (double *)mxCalloc(leny*sizx, sizeof(double)); 90 | int *tmpIy = (int *)mxCalloc(leny*sizx, sizeof(int)); 91 | 92 | //printf("(%d,%d),(%d,%d),(%d,%d), step:%.3f\n",offx,offy,lenx,leny,sizx,sizy,step); 93 | assert(step == 1); 94 | 95 | // dt1d(source,destination_val,destination_ptr,source_step,source_length, 96 | // a,b,dest_shift,dest_length,dest_step) 97 | for (int x = 0; x < sizx; x++) 98 | dt1d(vals+x*sizy, tmpM+x*leny, tmpIy+x*leny, 1, sizy, ay, by, offy, leny, step); 99 | 100 | for (int y = 0; y < leny; y++) 101 | dt1d(tmpM+y, M+y, Ix+y, leny, sizx, ax, bx, offx, lenx, step); 102 | 103 | // get argmins and adjust for matlab indexing from 1 104 | for (int x = 0; x < lenx; x++) { 105 | for (int y = 0; y < leny; y++) { 106 | int p = x*leny+y; 107 | Iy[p] = tmpIy[Ix[p]*leny+y]+1; 108 | Ix[p] = Ix[p]+1; 109 | } 110 | } 111 | 112 | mxFree(tmpM); 113 | mxFree(tmpIy); 114 | plhs[0] = mxM; 115 | plhs[1] = mxIx; 116 | plhs[2] = mxIy; 117 | return; 118 | } 119 | -------------------------------------------------------------------------------- /mex_unix/shiftdt.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/shiftdt.mexa64 -------------------------------------------------------------------------------- /mex_unix/update_score.cc: -------------------------------------------------------------------------------- 1 | #define INF 1E20 2 | #include 3 | #include 4 | #include 5 | #include "mex.h" 6 | 7 | /* 8 | * updatescore.cc 9 | * A helper function for message passing. 10 | * This function performs second step of message passing which is explained in section 11 | * 3.2 of our "Occlusion Coherence: Detecting and Localizing Occluded Faces" paper. 12 | */ 13 | #define gv2(A,dim1,i,j) A[(i)+(j)*dim1] 14 | #define gv3(A,dim1,dim2,i,j,k) A[(i)+((j)+(k)*dim2)*dim1] 15 | 16 | #define sv2(A,dim1,i,j,v) A[(i)+(j)*dim1] = v 17 | #define sv3(A,dim1,dim2,i,j,k,v) A[(i)+((j)+(k)*dim2)*dim1] = v 18 | 19 | // matlab entry point 20 | // [score, Ix, Iy, Im] = dt(scorep, Ixp, Iyp, defid, occfilter, b, localdefid, K, L, Ny, Nx) 21 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 22 | if (nrhs != 12) 23 | mexErrMsgTxt("Wrong number of inputs"); 24 | if (nlhs != 4) 25 | mexErrMsgTxt("Wrong number of outputs"); 26 | if (mxGetClassID(prhs[0]) != mxDOUBLE_CLASS) 27 | mexErrMsgTxt("Invalid input"); 28 | 29 | // Read in ... 30 | double *scorep = (double *)mxGetPr(prhs[0]); 31 | int32_t *Ixp = (int32_t *)mxGetPr(prhs[1]); 32 | int32_t *Iyp = (int32_t *)mxGetPr(prhs[2]); 33 | double *defid = (double *)mxGetPr(prhs[3]); 34 | mxLogical *occfilter = (mxLogical *)mxGetLogicals(prhs[4]); 35 | int leaf = (int)mxGetScalar(prhs[5]); 36 | double *b = (double *)mxGetPr(prhs[6]); 37 | double *localdefid = (double *)mxGetPr(prhs[7]); 38 | int K = (int)mxGetScalar(prhs[8]); 39 | int L = (int)mxGetScalar(prhs[9]); 40 | int Ny = (int)mxGetScalar(prhs[10]); 41 | int Nx = (int)mxGetScalar(prhs[11]); 42 | 43 | mwSize ndim = 3; 44 | mwSize dims[3] = {Ny, Nx, L}; 45 | mxArray *mxScore = mxCreateNumericArray(ndim, dims, mxDOUBLE_CLASS, mxREAL ); 46 | mxArray *mxIy = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL ); 47 | mxArray *mxIx = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL ); 48 | mxArray *mxIm = mxCreateNumericArray(ndim, dims, mxINT32_CLASS, mxREAL ); 49 | double *score = (double *)mxGetPr(mxScore); 50 | int *Iy = (int *)mxGetPr(mxIy); 51 | int *Ix = (int *)mxGetPr(mxIx); 52 | int *Im = (int *)mxGetPr(mxIm); 53 | 54 | for(int l = 0; l 0) 66 | { 67 | if(occfilter[k]==1 && leaf==1) 68 | { 69 | for(int j=0; jgv3(score,Ny,Nx,i,j,l)) 87 | { 88 | sv3(score,Ny,Nx,i,j,l,score0); 89 | sv3(Ix,Ny,Nx,i,j,l,(int)gv3(Ixp,Ny,Nx,i,j,lid)); 90 | sv3(Iy,Ny,Nx,i,j,l,(int)gv3(Iyp,Ny,Nx,i,j,lid)); 91 | sv3(Im,Ny,Nx,i,j,l,k+1); 92 | } 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | plhs[0] = mxScore; 100 | plhs[1] = mxIx; 101 | plhs[2] = mxIy; 102 | plhs[3] = mxIm; 103 | } 104 | -------------------------------------------------------------------------------- /mex_unix/updatescore.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/updatescore.mexa64 -------------------------------------------------------------------------------- /mex_unix/updatescore_ref.mexa64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/mex_unix/updatescore_ref.mexa64 -------------------------------------------------------------------------------- /photos/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/.DS_Store -------------------------------------------------------------------------------- /photos/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/img1.jpg -------------------------------------------------------------------------------- /photos/img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/img2.jpg -------------------------------------------------------------------------------- /photos/img3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/img3.jpg -------------------------------------------------------------------------------- /photos/img4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/img4.jpg -------------------------------------------------------------------------------- /photos/img5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golnazghiasi/hpm-detection-code/28323b2ba35f3e273693f6b6bf50bce081b76edb/photos/img5.jpg -------------------------------------------------------------------------------- /utility/ApplyRegression.m: -------------------------------------------------------------------------------- 1 | function det = ApplyRegression(box, reg_coef, I) 2 | % Applys linear regression to map 68 keypoint to size(reg_coef, 2) keypoints. 3 | 4 | det = []; 5 | if(~isempty(box)) 6 | if exist('I', 'var') 7 | figure(10); 8 | clf; imagesc(I); axis 'equal'; hold on; 9 | plot(box.det68(:,1), box.det68(:,2), '.r'); 10 | end 11 | 12 | % We learn the linear regression for the vertical faces, so we need 13 | % to ratate faces to be vertical before applying the regression. 14 | if(isfield(box, 'ang')) 15 | pts = RotatePoints(box.det68, box.rot_cent, -box.ang); 16 | else 17 | pts = box.det68; 18 | end 19 | 20 | if exist('I', 'var') 21 | figure(11); 22 | Ir = RotateAround(I, box.rot_cent(2), box.rot_cent(1), box.ang); 23 | clf; imagesc(Ir); axis 'equal'; hold on; 24 | plot(pts(:,1), pts(:,2), '.r'); 25 | end 26 | 27 | n_pts = ApplyReg(pts, reg_coef); 28 | 29 | if exist('I', 'var') 30 | figure(11); 31 | plot(n_pts(:,1), n_pts(:,2), '.b'); 32 | end 33 | if(isfield(box, 'ang')) 34 | n_pts = RotatePoints(n_pts, box.rot_cent, box.ang); 35 | end 36 | det = n_pts; 37 | if(exist('I','var')) 38 | figure(10); 39 | plot(det(:,1), det(:,2), '.b'); 40 | pause; 41 | end 42 | end 43 | 44 | 45 | function det = ApplyReg(det68, reg_coef) 46 | h = max(det68(:,1)) - min(det68(:,1)); 47 | w = max(det68(:,2)) - min(det68(:,2)); 48 | r = (h+w)/2; 49 | det68 = det68 /r; 50 | x = [1 det68(:,1)' det68(:,2)']; 51 | det = x* reg_coef; 52 | det = reshape(det, [], 2); 53 | det = det*r; 54 | -------------------------------------------------------------------------------- /utility/ChangeAlpha.m: -------------------------------------------------------------------------------- 1 | function changeAlpha(model, test, test_name, options) 2 | compare_eval = 'cache/changealpha/'; 3 | if(~exist(compare_eval,'dir')) 4 | mkdir(compare_eval); 5 | end 6 | 7 | 8 | testres_filenames = cell(1,0); 9 | legend_names = cell(1,0); 10 | 11 | modelo = model; 12 | pvalues = ([-.6:0.1:0.3]); 13 | for occ_bias_inc = pvalues 14 | options.occ_bias_inc = occ_bias_inc; 15 | filename = BoxesFilename(options); 16 | fprintf('%s\n', filename); 17 | save_eval_res = [compare_eval filename(7:end) '_forplot']; 18 | 19 | testres_filenames{end+1} = save_eval_res; 20 | legend_names{end+1} = ['alpha:' sprintf(' .2%f', options.occ_bias_inc)]; 21 | 22 | try 23 | load(save_eval_res); 24 | catch 25 | model = ChangeBiasOfOccKeypoints(modelo, options.occ_bias_inc); 26 | boxes = LocalizationsWRotation(test, model, options); 27 | 28 | for i = 1 : length(boxes) 29 | if ~isempty(boxes{i}) 30 | [boxes{i}.det_sim_29, boxes{i}.occ29] = ... 31 | Mapping68kToCofw29(boxes{i}.det68, boxes{i}.occ68); 32 | boxes{i}.det29 = ApplyRegression(boxes{i}, model.c68to29); 33 | end 34 | end 35 | 36 | % Evaluation 37 | [errors, precision, recall] = LandmarkLocalizationEval( ... 38 | boxes, 'det29', 'occ29', test, options.left_eye_inds, ... 39 | options.right_eye_inds); 40 | % Prints results. 41 | ave_errors = nanmean(errors); 42 | failure_rate = sum(errors > 0.1) / length(errors) * 100; 43 | fprintf('Average Error: %.3f, Failure rate: %.2f%%\n', ave_errors, ... 44 | failure_rate); 45 | fprintf('recall: %.3f precision %.3f\n', recall, precision); 46 | fprintf('number of nan: %d\n', sum(isnan(errors))); 47 | 48 | % Saves result for plotting results for different parameters 49 | save(save_eval_res,'precision','recall','errors'); 50 | end 51 | end 52 | PlotGraphs(testres_filenames,legend_names, pvalues, 'alpha', ... 53 | [compare_eval test_name '_changealpha'], 'cache/res/'); 54 | -------------------------------------------------------------------------------- /utility/ChangeBiasOfOccKeypoints.m: -------------------------------------------------------------------------------- 1 | function model = ChangeBiasOfOccKeypoints(model, occ_coef) 2 | % Changes bias of the model twoard predicting more (less) occluded 3 | % configurations, by increasing (decreasing) the bias of the occluded leaves. 4 | 5 | for m = 1 : length(model.components) 6 | parts = model.components{m}; 7 | for p = 11 : length(parts) 8 | for j = 1 : size(parts(p).biasid, 2) 9 | if(parts(p).occfilter(j) == 0) 10 | continue; 11 | end 12 | for i = 1 : size(parts(p).biasid, 1) 13 | bid = parts(p).biasid(i, j); 14 | if(bid ~= 0) 15 | ov = model.bias(bid).w; 16 | model.bias(bid).w = ov + abs(ov) * occ_coef; 17 | end 18 | end 19 | end 20 | 21 | end 22 | end 23 | 24 | 25 | -------------------------------------------------------------------------------- /utility/CofwKeypointsToHpmParts.m: -------------------------------------------------------------------------------- 1 | function cofw_to_hpm_part = CofwKeypointsToHpmParts() 2 | 3 | % Number of keypoints of lfw annotations. 4 | num_keypoints = 29; 5 | 6 | cofw_to_hpm_part = zeros(1, num_keypoints); 7 | 8 | % nose 9 | cofw_to_hpm_part([19, 20, 21, 22]) = 1; 10 | 11 | % right eye 12 | cofw_to_hpm_part([9, 11, 13, 14, 17]) = 2; 13 | 14 | % right eyebrow 15 | cofw_to_hpm_part([1, 3, 5, 6]) = 3; 16 | 17 | % left eye 18 | cofw_to_hpm_part([10, 12, 15, 16, 18]) = 4; 19 | 20 | % left eyebrow 21 | cofw_to_hpm_part([2, 4, 7, 8]) = 5; 22 | 23 | % upper lip 24 | cofw_to_hpm_part([23, 24, 25, 26]) = 6; 25 | 26 | % lower lip 27 | cofw_to_hpm_part([27, 28]) = 7; 28 | 29 | % lower jaw 30 | cofw_to_hpm_part(29) = 8; 31 | -------------------------------------------------------------------------------- /utility/ComputeReqLevels.m: -------------------------------------------------------------------------------- 1 | function [min_bbox, max_bbox, min_bbox68, max_bbox68] = ComputeReqLevels(test, boxes, interval) 2 | 3 | % In the landmark localization we have the sizes of bounding boxes. 4 | % So if the training and testing data have similar bounding boxes, 5 | % we can use thsese sizes and avoid to run the model on the levels of the 6 | % feature pyramid that the ground-truth bounding box is very large or very 7 | % small. 8 | % This method computes the minimum and maximum size of the ground-truth 9 | % boxes on the levels that model is fired. 10 | 11 | % test : ground truth 12 | % boxes: Results of running of the model on all the levels for vericication 13 | % data. We need the level which the model was fired. 14 | 15 | levels = []; 16 | box_sizes = []; 17 | box_sizes_68 = []; 18 | for i = 1 : length(test) 19 | bs = boxes{i}; 20 | if(isempty(bs)) 21 | continue; 22 | end 23 | bbox = test(i).bbox; 24 | bbox68 = [min(bs.det68(:, 1)), min(bs.det68(:, 2)), ... 25 | max(bs.det68(:, 1)), max(bs.det68(:, 2))]; 26 | 27 | if(isfield(bs, 'level')) 28 | levels = [levels, bs.level]; 29 | box_sizes = [box_sizes, (bbox(3) - bbox(1) + bbox(4) - bbox(2)) / 2]; 30 | box_sizes_68 = [box_sizes_68, ... 31 | ((bbox68(3) - bbox68(1)) + (bbox68(4) - bbox68(2))) / 2]; 32 | end 33 | end 34 | 35 | sc = 2 ^ (1 / interval); 36 | x = 2 ./ (sc .^ [0 : 60]); 37 | sizes = x(levels) .* box_sizes; 38 | min_bbox = min(sizes) - 10; 39 | max_bbox = max(sizes) + 10; 40 | 41 | % Computes the min and max of the sizes of the 68 keypoints boundig boxes. 42 | sizes = x(levels) .* box_sizes_68; 43 | min_bbox68 = min(sizes) - 10; 44 | max_bbox68 = max(sizes) + 10; 45 | 46 | -------------------------------------------------------------------------------- /utility/LearnRegression.m: -------------------------------------------------------------------------------- 1 | function reg_coef = LearnRegression(test, boxes, errors, model, ... 2 | mapping_2_hpm_parts) 3 | % errors: some estimation of the errors (e.g. errors for a simple mapping). 4 | % mapping_2_hpm_parts: a matrix that shows the corresponding hpm part number 5 | % (e.g. nose, eye, upper lip) for each keypoing of the target mapping. 6 | % reg_coef: learned coeficient for linear regression. 7 | 8 | fprintf('learning linear regression to map 68 landmarks to %d landmarks.\n', ... 9 | length(mapping_2_hpm_parts)); 10 | 11 | X1 = []; 12 | X2 = []; 13 | y1 = []; 14 | y2 = []; 15 | 16 | for i = 1 : length(test) 17 | % Uses only examples with small prediction errors for learning linear 18 | % regression. 19 | if isempty(boxes{i}) || errors(i) > 0.1 20 | continue; 21 | end 22 | 23 | gt_pts = test(i).pts; 24 | b = boxes{i}(1); 25 | det68 = b.det68; 26 | if(isfield(boxes{i}, 'ang')) 27 | % Learns the regression with the assumption that faces are vertical. 28 | gt_pts = RotatePoints(gt_pts, boxes{i}.rot_cent, -boxes{i}.ang); 29 | det68 = RotatePoints(det68, boxes{i}.rot_cent, -boxes{i}.ang); 30 | end 31 | 32 | X1 = [X1 ; det68(:, 1)']; 33 | X2 = [X2 ; det68(:, 2)']; 34 | y1 = [y1 ; gt_pts(:, 1)']; 35 | y2 = [y2 ; gt_pts(:, 2)']; 36 | end 37 | 38 | reg_coef = LearnRegressionCoef(model.opts, X1, X2, y1, y2, ... 39 | mapping_2_hpm_parts); 40 | 41 | %debugging(boxes, test, left_eye_inds, right_eye_inds, reg_coef, ... 42 | % errors, model, mapping_2_hpm_parts); 43 | 44 | function coef = LearnRegressionCoef(opts, X1,X2,y1,y2, mapping_2_hpm_parts) 45 | x_num_elements = size(X1, 2); 46 | y_num_elements = size(y1, 2); 47 | 48 | for i = 1 : size(X1, 1) 49 | h = max(X1(i, :)) - min(X1(i, :)); 50 | w = max(X2(i, :)) - min(X2(i, :)); 51 | r = (h + w) / 2; 52 | X1(i, :) = X1(i, :) / r; 53 | X2(i, :) = X2(i, :) / r; 54 | y1(i, :) = y1(i, :) / r; 55 | y2(i, :) = y2(i, :) / r; 56 | end 57 | 58 | X = [X1, X2]; 59 | X = [ones(size(X, 1), 1), X]; 60 | y = [y1, y2]; 61 | 62 | level_2_parts = opts.mixture(1).level_2_parts; 63 | for i = 1 : length(level_2_parts) 64 | x_part_num(level_2_parts(i).children) = i; 65 | end 66 | x_part_num(x_part_num == 0) = []; 67 | x_part_num = inv(opts.mixture(1).anno2treeorder)*(x_part_num'); 68 | x_part_num = [0 x_part_num' x_part_num']; 69 | 70 | y_num_elements = length(mapping_2_hpm_parts); 71 | y_part_num = [mapping_2_hpm_parts mapping_2_hpm_parts]; 72 | 73 | coef = zeros(x_num_elements * 2 + 1, y_num_elements * 2); 74 | 75 | % To prevent overfeating, learns a separate regression for the keypoints 76 | % of each part (eg. nose, eye, eyebrow) 77 | for i = 1 : length(level_2_parts) 78 | x_sub_parts = find(x_part_num == i) ; 79 | y_sub_parts = find(y_part_num == i); 80 | xi = X(:, [1 x_sub_parts]); 81 | yi = y(:, y_sub_parts); 82 | 83 | xx = xi' * xi; 84 | a = eye(size(xx, 1)); a(1, 1) = 0; 85 | %A = inv(xx + a * 0.02) * xi' * yi; 86 | A = inv(xx + a * 0.02) * xi' * yi; 87 | coef([1, x_sub_parts], y_sub_parts) = A; 88 | end 89 | 90 | 91 | function debugging(boxes, test, left_eye_inds, right_eye_inds, ... 92 | reg_coef, errors, model, mapping_2_hpm_parts) 93 | % Updates the 29 keypoints prediction. 94 | % TODO check it later when the center of rotation are saved correctly! 95 | for i = 1 : length(boxes) 96 | if ~isempty(boxes{i}) 97 | boxes{i}.det29 = ApplyRegression(boxes{i}, reg_coef); 98 | end 99 | end 100 | 101 | aft_errors = LandmarkLocalizationEval( ... 102 | boxes, 'det29', 'occ29', test, left_eye_inds, ... 103 | right_eye_inds); 104 | disp(mean(errors)); 105 | disp(mean(aft_errors)); 106 | if 1 107 | for i = 1 : length(boxes) 108 | %errors(i) 109 | figure(1); clf; 110 | I = imread(test(i).im); 111 | imagesc(I); axis 'equal'; hold on; colormap('gray'); 112 | pts = boxes{i}.det_sim_29; 113 | plot(pts(:, 1), pts(:, 2), '.g', 'MarkerSize', 10); 114 | pts = boxes{i}.det29; 115 | plot(pts(:, 1), pts(:, 2), '.b', 'MarkerSize', 10); 116 | pts = test(i).pts; 117 | plot(pts(:, 1), pts(:, 2), '.c', 'MarkerSize', 10); 118 | if 1 119 | level_2_parts = model.opts.mixture(1).level_2_parts; 120 | x_part_num = zeros( ... 121 | 1, sum(model.opts.mixture(1).part_level == 1) * 2 + 1); 122 | for j=1:length(level_2_parts) 123 | x_part_num(level_2_parts(j).children) = j; 124 | end 125 | x_part_num(x_part_num == 0) = []; 126 | x_part_num = inv(model.opts.mixture(1).anno2treeorder) * ... 127 | (x_part_num'); 128 | 129 | for j = 1 : length(level_2_parts) 130 | y_sub = find(mapping_2_hpm_parts == j); 131 | x_sub = find(x_part_num == j); 132 | 133 | clf; imagesc(I); axis 'equal'; hold on; 134 | pts10 = boxes{i}.det_sim_29; 135 | pts68 = boxes{i}.det68; 136 | plot(pts68(x_sub, 1), pts68(x_sub, 2), '.g', ... 137 | 'MarkerSize', 10); 138 | plot(pts10(y_sub, 1), pts10(y_sub, 2), '.b', ... 139 | 'MarkerSize', 10); 140 | 141 | pause; 142 | end 143 | end 144 | pause; 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /utility/LfwKeypointsToHpmParts.m: -------------------------------------------------------------------------------- 1 | function lfw_to_hpm_part = LfwKeypointsToHpmParts() 2 | 3 | % Number of keypoints of lfw annotations. 4 | num_keypoints = 10; 5 | 6 | lfw_to_hpm_part = zeros(1, num_keypoints); 7 | 8 | % nose 9 | lfw_to_hpm_part([9, 10]) = 1; 10 | 11 | % right eye 12 | lfw_to_hpm_part([1, 2]) = 2; 13 | 14 | % right eyebrow 15 | %lfw_to_hpm_part() = 3; 16 | 17 | % left eye 18 | lfw_to_hpm_part([7, 8]) = 4; 19 | 20 | % left eyebrow 21 | %lfw_to_hpm_part() = 5; 22 | 23 | % upper lip 24 | lfw_to_hpm_part([6, 3, 4]) = 6; 25 | 26 | % lower lip 27 | lfw_to_hpm_part(5) = 7; 28 | 29 | % lower jaw 30 | %lfw_to_hpm_part() = 8; 31 | -------------------------------------------------------------------------------- /utility/Mapping68kToAfw.m: -------------------------------------------------------------------------------- 1 | function [map_pts, map_occ] = Mapping68kToAfw(pts, occ) 2 | % Maps keypoints based on the nearest keypoint(s). 3 | 4 | T{1} = 37 : 42; 5 | T{2} = 43 : 48; 6 | T{3} = 31; 7 | T{4} = [49, 61]; 8 | T{4} = [61]; 9 | T{5} = 49 : 68; 10 | T{6} = [55, 65]; 11 | 12 | map_pts = zeros(length(T), 2); 13 | map_occ = zeros(1, length(T)); 14 | for i = 1 : size(map_pts, 1) 15 | map_pts(i, :) = mean(pts(T{i}, :), 1); 16 | map_occ(i) = max(occ(T{i})); 17 | end 18 | -------------------------------------------------------------------------------- /utility/Mapping68kToCofw29.m: -------------------------------------------------------------------------------- 1 | function [map_pts, map_occ] = Mapping68kToCofw29(pts, occ) 2 | % Maps keypoints based on the nearest keypoint(s). 3 | 4 | T{1} = 18; 5 | T{2} = 27; 6 | T{3} = 22; 7 | T{4} = 23; 8 | T{5} = 20; 9 | T{6} = [18, 22]; 10 | T{7} = 25; 11 | T{8} = [23, 27]; 12 | T{9} = 37; 13 | T{10} = 46; 14 | T{11} = 40; 15 | T{12} = 43; 16 | T{13} = 38 : 39; 17 | T{14} = 41 : 42; 18 | T{15} = 44 : 45; 19 | T{16} = 47 : 48; 20 | T{17} = 37 : 42; 21 | T{18} = 43 : 48; 22 | T{19} = 32; 23 | T{20} = 36; 24 | T{21} = 31; 25 | T{22} = 34; 26 | T{23} = [49, 61]; 27 | T{24} = [55, 65]; 28 | T{25} = 51 : 53; 29 | T{26} = 63; 30 | T{27} = 67; 31 | T{28} = 57 : 59; 32 | T{29} = 8 : 10; 33 | 34 | 35 | map_pts = zeros(length(T), 2); 36 | map_occ = zeros(1, length(T)); 37 | for i = 1 : size(map_pts, 1) 38 | map_pts(i, :) = mean(pts(T{i}, :), 1); 39 | map_occ(i) = max(occ(T{i})); 40 | end 41 | -------------------------------------------------------------------------------- /utility/Mapping68kToLfw.m: -------------------------------------------------------------------------------- 1 | function [map_pts, map_occ] = Mapping68kToLfw9k(pts, occ) 2 | % Maps keypoints based on the nearest keypoint(s). 3 | 4 | T{1} = 37; 5 | T{2} = 40; 6 | T{3} = [49, 61]; 7 | T{4} = [55, 65]; 8 | T{5} = [57, 58, 59]; 9 | T{6} = [51, 52, 53]; 10 | T{7} = 43; 11 | T{8} = 46; 12 | T{9} = 32; 13 | T{10} = 36; 14 | 15 | map_pts = zeros(length(T), 2); 16 | map_occ = zeros(1, length(T)); 17 | for i = 1 : size(map_pts, 1) 18 | map_pts(i, :) = mean(pts(T{i}, :), 1); 19 | map_occ(i) = max(occ(T{i})); 20 | end 21 | -------------------------------------------------------------------------------- /utility/RotateAround.m: -------------------------------------------------------------------------------- 1 | function output = RotateAround(image, pointY, pointX, angle) 2 | output = image; 3 | if abs(angle)1) 9 | output(:,:,2) = rotateAround1(image(:,:,2), pointY, pointX, angle, ... 10 | method); 11 | output(:,:,3) = rotateAround1(image(:,:,3), pointY, pointX, angle, ... 12 | method); 13 | end 14 | 15 | 16 | function output=rotateAround1(image, pointY, pointX, angle, varargin) 17 | % ROTATEAROUND rotates an image. 18 | % ROTATED=ROTATEAROUND(IMAGE, POINTY, POINTX, ANGLE) rotates IMAGE around 19 | % the point [POINTY, POINTX] by ANGLE degrees. To rotate the image 20 | % clockwise, specify a negative value for ANGLE. 21 | % 22 | % ROTATED=ROTATEAROUND(IMAGE, POINTY, POINTX, ANGLE, METHOD) rotates the 23 | % image with specified method: 24 | % 'nearest' Nearest-neighbor interpolation 25 | % 'bilinear' Bilinear interpolation 26 | % 'bicubic' Bicubic interpolation 27 | % The default is 'nearest'. 28 | % 29 | % Example 30 | % ------- 31 | % image=imread('eight.tif'); 32 | % imshow(rotateAround(image, 1, 1, 10)); 33 | % 34 | % See also IMROTATE, PADARRAY. 35 | 36 | % Contributed by Jan Motl (jan@motl.us) 37 | % $Revision: 1.0 $ $Date: 2013/02/22 16:58:01 $ 38 | 39 | % Parameter checking. 40 | numvarargs = length(varargin); 41 | if numvarargs > 1 42 | error('myfuns:somefun2Alt:TooManyInputs', ... 43 | 'requires at most 1 optional input'); 44 | end 45 | optargs = {'nearest'}; % Set defaults for optional inputs 46 | optargs(1:numvarargs) = varargin; 47 | [method] = optargs{:}; % Place optional args in memorable variable names 48 | 49 | % Initialization. 50 | [imageHeight, imageWidth] = size(image); 51 | centerX = floor(imageWidth/2+1); 52 | centerY = floor(imageHeight/2+1); 53 | 54 | dy = centerY-pointY; 55 | dx = centerX-pointX; 56 | 57 | % How much would the watched point shift if rotated around the img center. 58 | [theta, rho] = cart2pol(-dx,dy); 59 | [newX, newY] = pol2cart(theta+deg2rad(angle), rho); 60 | shiftX = round(pointX-(centerX+newX)); 61 | shiftY = round(pointY-(centerY-newY)); 62 | 63 | % Pad the image to preserve the whole image during the rotation. 64 | padX = abs(shiftX); 65 | padY = abs(shiftY); 66 | 67 | padded = padarray(image, [padY padX]); 68 | 69 | % Rotate the image around the center. 70 | rot = imrotate(padded, angle, method, 'crop'); 71 | 72 | % Crop the image. 73 | output = rot(padY+1-shiftY:end-padY-shiftY, padX+1-shiftX:end-padX-shiftX); 74 | -------------------------------------------------------------------------------- /utility/RotateFace.m: -------------------------------------------------------------------------------- 1 | function [rot_deg, Ir, ptsr, rot_cent] = RotateFace( ... 2 | I, pts, left_eye_ind, right_eye_ind) 3 | % Rotates face to be vertical using the locations of eyes' centers. 4 | 5 | if(~exist('left_eye_ind', 'var')) 6 | if(size(pts, 1) == 68) 7 | left_eye_ind = 37:42; 8 | right_eye_ind = 43:48; 9 | else 10 | assert(size(pts, 1) == 29); 11 | left_eye_ind = 17; 12 | right_eye_ind = 18; 13 | end 14 | end 15 | left_eye_loc = mean(pts(left_eye_ind, :), 1); 16 | right_eye_loc = mean(pts(right_eye_ind, :), 1); 17 | 18 | % Calculates amount of rotation in the given face using the locations of eyes 19 | % centers. 20 | rot_cent = (left_eye_loc + right_eye_loc) / 2; 21 | rot_rad = atan2(right_eye_loc(1,2) - rot_cent(1,2), ... 22 | right_eye_loc(1,1) - rot_cent(1,1)); 23 | rot_deg = radtodeg(rot_rad); 24 | 25 | Ir(:, :, 1) = RotateAround(I(: ,: ,1), rot_cent(1, 2), ... 26 | rot_cent(1, 1), rot_deg); 27 | if(size(I, 3) > 1) 28 | Ir(:, :, 2) = RotateAround(I(:, :, 2), rot_cent(1, 2), ... 29 | rot_cent(1, 1), rot_deg); 30 | Ir(:, :, 3) = RotateAround(I(:, :, 3), rot_cent(1, 2), ... 31 | rot_cent(1, 1), rot_deg); 32 | end 33 | ptsr = RotatePoints(pts, rot_cent, -rot_deg); 34 | 35 | if 0 36 | figure(199); clf; imagesc(I); axis 'equal'; hold on; 37 | plot(pts(:, 1), pts(:, 2), '.g'); 38 | plot(left_eye_loc(1), left_eye_loc(2), '*b'); 39 | plot(right_eye_loc(1), right_eye_loc(2), '*b'); 40 | figure(200); clf; imagesc(Ir); axis 'equal'; hold on; 41 | plot(ptsr(:, 1), ptsr(:, 2), '.g'); 42 | plot(left_eye_loc(1), left_eye_loc(2), '*b'); 43 | plot(right_eye_loc(1), right_eye_loc(2), '*b'); 44 | end 45 | 46 | -------------------------------------------------------------------------------- /utility/RotatePoints.m: -------------------------------------------------------------------------------- 1 | function pts = RotatePoints(pts, cent, theta) 2 | % Rotates points (pts) around center (cent) theta degrees. 3 | 4 | theta = degtorad(theta); 5 | if abs(theta) 0.1) / ... 23 | length(errors) * 100]; 24 | ave_error = nanmean(errors); 25 | ave_errors = [ave_errors, ave_error]; 26 | 27 | if(exist('precision', 'var')) 28 | precs = [precs, precision]; 29 | recalls = [recalls, recall]; 30 | clear precision recall 31 | end 32 | end 33 | fprintf('failure rate :'); disp(failure_rate_p1); 34 | fprintf('ave error :'); disp(ave_errors); 35 | fprintf('precision :'); disp(precs); 36 | fprintf('recall :'); disp(recalls); 37 | fprintf('alpha :'); disp(params); 38 | 39 | 40 | 41 | disp(experiment_save_res); 42 | if ~exist(experiment_save_res, 'dir') 43 | mkdir(experiment_save_res); 44 | end 45 | save([experiment_save_res 'changealpha'], 'params', 'failure_rate_p1', ... 46 | 'x_label', 'ave_errors', 'precs', 'recalls'); 47 | 48 | figure; 49 | plot(recalls, precs); 50 | grid on; 51 | ylabel('Precision', 'fontsize',14); 52 | xlabel('Recall','fontsize', 14); 53 | set(gcf, 'color', 'w'); 54 | 55 | figure; 56 | plot(recalls, ave_errors); 57 | grid on; 58 | ylabel('Average localization error', 'fontsize',14); 59 | xlabel('Recall','fontsize', 14); 60 | set(gcf, 'color', 'w'); 61 | 62 | figure; 63 | plot(recalls, failure_rate_p1); 64 | grid on; 65 | ylabel('Failure rate', 'fontsize',14); 66 | xlabel('Recall','fontsize', 14); 67 | set(gcf, 'color', 'w') 68 | 69 | %export_fig([experiment_save_plot_res '_comp'], '-pdf'); 70 | -------------------------------------------------------------------------------- /visualization/ShowPoints.m: -------------------------------------------------------------------------------- 1 | function ShowPoints(im, boxes) 2 | clf; 3 | imagesc(im); 4 | axis equal; 5 | axis off; 6 | grid off; 7 | hold on; 8 | for b = boxes, 9 | plot(b.det(b.occ==1, 1), b.det(b.occ == 1, 2), '.r', 'MarkerSize', 4); 10 | plot(b.det(b.occ==0, 1), b.det(b.occ == 0, 2), '.g', 'MarkerSize', 4); 11 | end 12 | drawnow; 13 | -------------------------------------------------------------------------------- /visualization/VisualizeData.m: -------------------------------------------------------------------------------- 1 | function VisualizeData(test, show_num) 2 | 3 | if ~exist('show_num', 'var') 4 | show_num = 0; 5 | end 6 | 7 | for i = 1 : length(test) 8 | I = imread(test(i).im); 9 | clf; imagesc(I); hold on; axis('equal'); 10 | if size(I, 3) == 1 11 | colormap('gray'); 12 | end 13 | pts = test(i).pts; 14 | if isfield(test(i), 'occ') 15 | occ = test(i).occ; 16 | else 17 | occ = zeros(1, size(pts, 1)); 18 | end 19 | plot(pts(occ == 0, 1), pts(occ == 0, 2), '.g'); 20 | plot(pts(occ == 1, 1), pts(occ == 1, 2), '.r'); 21 | 22 | bbox = test(i).bbox; 23 | rectangle('Position', ... 24 | [bbox(1) bbox(2) bbox(3)-bbox(1) bbox(4)-bbox(2)]); 25 | 26 | if show_num 27 | for j = 1 : size(pts, 1) 28 | text(pts(j, 1), pts(j, 2), num2str(j)); 29 | end 30 | end 31 | 32 | pause; 33 | end 34 | 35 | 36 | -------------------------------------------------------------------------------- /visualization/VisualizeDetectionRes.m: -------------------------------------------------------------------------------- 1 | function VisualizeDetectionRes(gt, boxes, save_vis_res, thresh, max_to_show, ... 2 | draw_rectangle, show_score_size) 3 | 4 | if(~exist('max_to_show', 'var')) 5 | max_to_show = length(gt); 6 | end 7 | 8 | if(~exist('draw_rectangle', 'var')) 9 | draw_rectangle = false; 10 | end 11 | if(~exist('show_score_size', 'var')) 12 | show_score_size = false; 13 | end 14 | if(~exist(save_vis_res,'dir')) 15 | mkdir(save_vis_res); 16 | end 17 | 18 | fprintf(['Visualizing detection result of first %d images ...\n'], max_to_show); 19 | 20 | for i = 1 : max_to_show 21 | im = imread(gt(i).im); 22 | boxes_out = boxes{i}; 23 | sc = [boxes_out.s]; 24 | boxes_out = boxes_out(sc > thresh); 25 | ShowPoints(im, boxes_out); 26 | hold on; 27 | for j = 1:length(boxes_out) 28 | c = boxes_out(j).c; 29 | bb = boxes_out(j).BB; 30 | x = bb(1); 31 | y = bb(2); 32 | x_r = bb(3) - bb(1); 33 | y_r = bb(4) - bb(2); 34 | if show_score_size 35 | text(x - 5, y - 5, sprintf('%.2f',boxes_out(j).s), ... 36 | 'color', 'b', 'fontsize', 4); 37 | text(x - 20, y - 5, sprintf('%.1f', x_r), ... 38 | 'color', 'r', 'fontsize', 4); 39 | end 40 | if draw_rectangle 41 | rectangle('Position',[x, y, x_r, y_r], ... 42 | 'edgecolor', 'g', 'linewidth', 0.5); 43 | end 44 | end 45 | pause(2); 46 | if 0 47 | name = boxes{i}(1).id; 48 | if(length(name) > 3 && name(end - 3) == '.') 49 | name = name(1 : end - 4); 50 | end 51 | export_fig([save_vis_res name '_' num2str(abs(thresh)*100)], '-pdf'); 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /visualization/VisualizeLocalizationRes.m: -------------------------------------------------------------------------------- 1 | function [] = VisualizeLocalizationRes( ... 2 | boxes, pts_name, occ_name, test, testname, figdir, ... 3 | crop_images, show_groundtruth, show_keypoint_num, errors, ... 4 | max_to_show) 5 | if(~exist('max_to_show', 'var') || max_to_show > length(test)) 6 | max_to_show = length(test); 7 | end 8 | 9 | fprintf(['Visualizing landmark localization predication of first %d images' ... 10 | ' of %s dataset ...\n'], max_to_show, testname); 11 | save_res = [figdir testname '_68res/']; 12 | if(~exist(save_res, 'dir')) 13 | mkdir(save_res); 14 | end 15 | for i = 1 : max_to_show 16 | if(isempty(boxes{i})) 17 | im = imread(test(i).im); 18 | clf; imagesc(im); axis off; axis image; hold on; 19 | 20 | if isfield(test(i), 'bbox') 21 | bbox = test(i).bbox; 22 | [im, offset] = CropImage(im, bbox, 0); 23 | clf; imagesc(im); axis off; axis image; hold on; 24 | end 25 | continue; 26 | end 27 | 28 | pts_gt = test(i).pts; 29 | if isfield(test(i), 'occ') && ~isempty(test(i).occ) 30 | occ_gt = test(i).occ; 31 | else 32 | occ_gt = zeros(1, size(pts_gt, 1)); 33 | end 34 | 35 | im = imread(test(i).im); 36 | b = boxes{i}(1); 37 | pts_det = getfield(b, pts_name); 38 | occ_det = getfield(b, occ_name); 39 | 40 | if crop_images 41 | if isfield(test(i), 'bbox') 42 | bbox = test(i).bbox; 43 | [im, offset] = CropImage(im, bbox, 0.3); 44 | else 45 | bbox = [min(pts_gt(:, 1)), min(pts_gt(:, 2)), ... 46 | max(pts_gt(:, 1)), max(pts_gt(:, 2))]; 47 | [im, offset] = CropImage(im, bbox, 0.3); 48 | end 49 | 50 | pts_gt(:, 1) = pts_gt(:, 1) - offset(1); 51 | pts_gt(:, 2) = pts_gt(:, 2) - offset(2); 52 | 53 | pts_det(:, 1) = pts_det(:, 1) - offset(1); 54 | pts_det(:, 2) = pts_det(:, 2) - offset(2); 55 | end 56 | 57 | clf; imagesc(im); axis off; axis image; hold on; 58 | if(size(im, 3) == 1) 59 | colormap(gray); 60 | end 61 | plot(pts_det(occ_det == 0, 1), pts_det(occ_det == 0, 2), '.g', ... 62 | 'MarkerSize', 20); 63 | plot(pts_det(occ_det == 1, 1), pts_det(occ_det == 1, 2), '.r', ... 64 | 'MarkerSize', 20); 65 | 66 | if show_keypoint_num 67 | ShowKeypointNums(pts_det, 'k'); 68 | end 69 | 70 | if show_groundtruth 71 | plot(pts_gt(occ_gt == 0, 1), pts_gt(occ_gt == 0, 2), '.b', ... 72 | 'MarkerSize', 20); 73 | plot(pts_gt(occ_gt == 1, 1), pts_gt(occ_gt == 1, 2), '.m', ... 74 | 'MarkerSize', 20); 75 | 76 | if show_keypoint_num 77 | ShowKeypointNums(pts_gt, 'm'); 78 | end 79 | end 80 | title(sprintf('%.3f', errors(i))); 81 | pause(2); 82 | %export_fig(sprintf('%s%d', save_res, i), '-pdf'); 83 | end 84 | 85 | 86 | function [im, offset] = CropImage(im, box, pad_ratio) 87 | % Crops image around the bounding box. 88 | pad = pad_ratio * ((box(3) - box(1) + 1) + (box(4) - box(2) + 1)); 89 | x1 = max(1, round(box(1) - pad)); 90 | y1 = max(1, round(box(2) - pad)); 91 | x2 = min(size(im, 2), round(box(3) + pad)); 92 | y2 = min(size(im, 1), round(box(4) + pad)); 93 | 94 | im = im(y1:y2, x1:x2, :); 95 | 96 | offset(1) = x1 -1; 97 | offset(2) = y1 -1; 98 | 99 | function ShowKeypointNums(pts, color) 100 | for i = 1 : size(pts, 1) 101 | text(pts(i, 1), pts(i, 2), num2str(i), 'Color', color, 'FontSize', 10); 102 | end 103 | -------------------------------------------------------------------------------- /visualization/VisualizeModel.m: -------------------------------------------------------------------------------- 1 | function VisualizeModel(model, num_instances) 2 | 3 | if nargin < 2 4 | num_instances = 1; 5 | end 6 | 7 | for compid = 1 : length(model.components) 8 | if(model.flip_image(compid)) 9 | % This component shares its parameters with the component for opposite viewpoint. 10 | continue; 11 | end 12 | c = model.components{compid}; 13 | numparts = length(c); 14 | 15 | % Samples part mixtures to show. 16 | m = zeros(numparts, num_instances); 17 | for i = 1 : num_instances 18 | m(1, i) = randi(length(c(1).filterid)); 19 | for k = 2 : numparts 20 | pa = c(k).parent; 21 | I = find(c(k).defid(m(pa, i), :) > 0); 22 | m(k, i) = I(randi(length(I))); 23 | end 24 | end 25 | 26 | for i = 1 : num_instances 27 | % Visualizes HOG templates. 28 | figure; 29 | VisualizeComponentFilters(model, compid, m(:, i)); axis on; 30 | 31 | % Visualizes skeleton. 32 | figure; 33 | VisualizeComponentSkeleton(model, compid, m(:, i), model.opts); 34 | end 35 | end 36 | 37 | function VisualizeComponentFilters(model, compid, mix) 38 | 39 | pad = 2; 40 | bs = 20; 41 | 42 | c = model.components{compid}; 43 | num_parts = length(c); 44 | k = 1; 45 | part = c(1); 46 | 47 | fids = [model.components{compid}.filterid]; 48 | non_zero_fid = fids(find(fids, 1)); 49 | zero_w = zeros(size(model.filters(non_zero_fid).w)); 50 | 51 | % Part filter 52 | if(part.filterid(mix(k)) ~= 0) 53 | w = model.filters(part.filterid(mix(k))).w; 54 | w = foldHOG(w); 55 | scale = max(abs(w(:))); 56 | else 57 | fids = [model.components{compid}.filterid]; 58 | w = foldHOG(zero_w); 59 | scale = 1; 60 | end 61 | p = HOGpicture(w, bs); 62 | p = padarray(p, [pad, pad], 0); 63 | p = uint8(p * (255 / scale)); 64 | % border 65 | p(:, 1 : 2 * pad) = 128; 66 | p(:, end - 2 * pad + 1 : end) = 128; 67 | p(1 : 2 * pad, :) = 128; 68 | p(end - 2 * pad + 1 : end, :) = 128; 69 | im = p; 70 | startpoint = zeros(num_parts, 2); 71 | startpoint(1,:) = [0, 0]; 72 | 73 | partsize = zeros(num_parts, 1); 74 | partsize(1) = size(p, 1); 75 | 76 | for k = 2 : num_parts 77 | part = c(k); 78 | parent = c(k).parent; 79 | % part filter 80 | if(part.filterid(mix(k)) ~= 0) 81 | w = model.filters(part.filterid(mix(k))).w; 82 | w = foldHOG(w); 83 | scale = max(abs(w(:))); 84 | else 85 | w = foldHOG(zero_w); 86 | scale = 1; 87 | end 88 | p = HOGpicture(w, bs); 89 | p = padarray(p, [pad, pad], 0); 90 | p = uint8(p * (255 / scale)); 91 | % border 92 | p(:, 1 : 2 * pad) = 128; 93 | p(:, end - 2 * pad + 1 : end) = 128; 94 | p(1 : 2 * pad, :) = 128; 95 | p(end - 2 * pad + 1 : end, :) = 128; 96 | 97 | % paste into root 98 | def = model.defs(part.defid(mix(parent), mix(k))); 99 | 100 | x1 = (def.anchor(1) - 1) * bs + 1 + startpoint(parent, 1); 101 | y1 = (def.anchor(2) - 1) * bs + 1 + startpoint(parent, 2); 102 | 103 | [H W] = size(im); 104 | imnew = zeros(H + max(0, 1 - y1), W + max(0, 1 - x1)); 105 | imnew(1 + max(0, 1 - y1) : H + max(0, 1 - y1), ... 106 | 1 + max(0, 1 - x1) : W + max(0, 1 - x1)) = im; 107 | im = imnew; 108 | 109 | startpoint = startpoint + repmat([max(0, 1 - x1), ... 110 | max(0, 1 - y1)], [num_parts, 1]); 111 | 112 | x1 = max(1, x1); 113 | y1 = max(1, y1); 114 | x2 = x1 + size(p, 2) - 1; 115 | y2 = y1 + size(p, 1) - 1; 116 | 117 | startpoint(k, 1) = x1 - 1; 118 | startpoint(k, 2) = y1 - 1; 119 | 120 | im(y1 : y2, x1 : x2) = p; 121 | partsize(k) = size(p, 1); 122 | end 123 | 124 | % plot parts 125 | imagesc(im); 126 | colormap gray; axis equal; axis off; drawnow; 127 | 128 | function f = foldHOG(w) 129 | % f = foldHOG(w) 130 | % Condense HOG features into one orientation histogram. 131 | % Used for displaying a feature. 132 | 133 | f=max(w(:,:,1:9),0)+max(w(:,:,10:18),0)+max(w(:,:,19:27),0); 134 | 135 | function im = HOGpicture(w, bs) 136 | % HOGpicture(w, bs) 137 | % Make picture of positive HOG weights. 138 | 139 | % construct a "glyph" for each orientaion 140 | bim1 = zeros(bs, bs); 141 | bim1(:,round(bs/2):round(bs/2)+1) = 1; 142 | bim = zeros([size(bim1) 9]); 143 | bim(:,:,1) = bim1; 144 | for i = 2:9, 145 | bim(:,:,i) = imrotate(bim1, -(i-1)*20, 'crop'); 146 | end 147 | 148 | % make pictures of positive weights bs adding up weighted glyphs 149 | s = size(w); 150 | w(w < 0) = 0; 151 | im = zeros(bs*s(1), bs*s(2)); 152 | for i = 1:s(1), 153 | iis = (i-1)*bs+1:i*bs; 154 | for j = 1:s(2), 155 | jjs = (j-1)*bs+1:j*bs; 156 | for k = 1:9, 157 | im(iis,jjs) = im(iis,jjs) + bim(:,:,k) * w(i,j,k); 158 | end 159 | end 160 | end 161 | 162 | function VisualizeComponentSkeleton(model, compid, mix, opts) 163 | 164 | c = model.components{compid}; 165 | numparts = length(c); 166 | 167 | bs = 4; 168 | 169 | varCol = 'g'; 170 | varInvCol = 'r'; 171 | hlevelCol = 'k'; 172 | 173 | point(1, :) = [bs * 5 / 2 + 1, bs * 5 / 2 + 1]; 174 | startpoint = zeros(numparts, 2); 175 | startpoint(1, :) = [0 0]; 176 | isoc = false(1, numparts); 177 | for k = 2 : numparts 178 | part = c(k); 179 | pa = c(k).parent; 180 | 181 | if(part.occfilter(mix(k))) 182 | isoc(k) =true; 183 | end 184 | 185 | % Pastes into root 186 | def = model.defs(part.defid(mix(pa),mix(k))); 187 | 188 | %x1 = (def.anchor(1)-1+round(def.w(2)/def.w(1)/2))*bs+1 + startpoint(pa,1); 189 | %y1 = (def.anchor(2)-1+round(def.w(4)/def.w(3)/2))*bs+1 + startpoint(pa,2); 190 | x1 = (def.anchor(1)-1)*bs+1 + startpoint(pa,1); 191 | y1 = (def.anchor(2)-1)*bs+1 + startpoint(pa,2); 192 | x2 = x1 + bs*5+1 -1; 193 | y2 = y1 + bs*5+1 -1; 194 | 195 | startpoint(k,1) = x1 - 1; 196 | startpoint(k,2) = y1 - 1; 197 | 198 | point(k,:) = [(x1+x2)/2,(y1+y2)/2]; 199 | 200 | radius(k,:) = [sqrt(1/2/def.w(1)) sqrt(1/2/def.w(3))]; 201 | end 202 | 203 | x1 = min(point(:,1)); 204 | y1 = min(point(:,2)); 205 | x2 = max(point(:,1)); 206 | y2 = max(point(:,2)); 207 | 208 | % Plots anchor points 209 | plot(point(opts.mixture(compid).part_level==1,1), ... 210 | -point(opts.mixture(compid).part_level==1,2), 'b.', 'markersize', 20); 211 | hold on; 212 | plot(point(opts.mixture(compid).part_level==2,1), ... 213 | -point(opts.mixture(compid).part_level==2,2), 'k.', 'markersize', 25); 214 | 215 | % Plots occlsion 216 | plot(point(isoc,1), -point(isoc,2), 'r.', 'markersize', 20); 217 | 218 | % Draw skeletons 219 | for k = 2 : numparts 220 | pa = c(k).parent; 221 | line([point(pa,1) point(k,1)], -[point(pa,2) point(k,2)], ... 222 | 'linewidth', 4); 223 | end 224 | % Draw variance of deformations 225 | for k = 1 : numparts 226 | if(opts.mixture(compid).part_level(k) == 2) 227 | col = hlevelCol; 228 | elseif(isoc(k)) 229 | col = varInvCol; 230 | else 231 | col = varCol; 232 | end 233 | ellipse(radius(k, 1), radius(k, 2), 0, point(k, 1), -point(k, 2), col); 234 | end 235 | axis off; axis equal; 236 | xlim([x1-10, x2+10]); ylim([-y2-10, -y1+10]); 237 | set(gcf, 'Color', 'w'); 238 | -------------------------------------------------------------------------------- /visualization/distinguishable_colors.m: -------------------------------------------------------------------------------- 1 | function colors = distinguishable_colors(n_colors,bg,func) 2 | % DISTINGUISHABLE_COLORS: pick colors that are maximally perceptually distinct 3 | % 4 | % When plotting a set of lines, you may want to distinguish them by color. 5 | % By default, Matlab chooses a small set of colors and cycles among them, 6 | % and so if you have more than a few lines there will be confusion about 7 | % which line is which. To fix this problem, one would want to be able to 8 | % pick a much larger set of distinct colors, where the number of colors 9 | % equals or exceeds the number of lines you want to plot. Because our 10 | % ability to distinguish among colors has limits, one should choose these 11 | % colors to be "maximally perceptually distinguishable." 12 | % 13 | % This function generates a set of colors which are distinguishable 14 | % by reference to the "Lab" color space, which more closely matches 15 | % human color perception than RGB. Given an initial large list of possible 16 | % colors, it iteratively chooses the entry in the list that is farthest (in 17 | % Lab space) from all previously-chosen entries. While this "greedy" 18 | % algorithm does not yield a global maximum, it is simple and efficient. 19 | % Moreover, the sequence of colors is consistent no matter how many you 20 | % request, which facilitates the users' ability to learn the color order 21 | % and avoids major changes in the appearance of plots when adding or 22 | % removing lines. 23 | % 24 | % Syntax: 25 | % colors = distinguishable_colors(n_colors) 26 | % Specify the number of colors you want as a scalar, n_colors. This will 27 | % generate an n_colors-by-3 matrix, each row representing an RGB 28 | % color triple. If you don't precisely know how many you will need in 29 | % advance, there is no harm (other than execution time) in specifying 30 | % slightly more than you think you will need. 31 | % 32 | % colors = distinguishable_colors(n_colors,bg) 33 | % This syntax allows you to specify the background color, to make sure that 34 | % your colors are also distinguishable from the background. Default value 35 | % is white. bg may be specified as an RGB triple or as one of the standard 36 | % "ColorSpec" strings. You can even specify multiple colors: 37 | % bg = {'w','k'} 38 | % or 39 | % bg = [1 1 1; 0 0 0] 40 | % will only produce colors that are distinguishable from both white and 41 | % black. 42 | % 43 | % colors = distinguishable_colors(n_colors,bg,rgb2labfunc) 44 | % By default, distinguishable_colors uses the image processing toolbox's 45 | % color conversion functions makecform and applycform. Alternatively, you 46 | % can supply your own color conversion function. 47 | % 48 | % Example: 49 | % c = distinguishable_colors(25); 50 | % figure 51 | % image(reshape(c,[1 size(c)])) 52 | % 53 | % Example using the file exchange's 'colorspace': 54 | % func = @(x) colorspace('RGB->Lab',x); 55 | % c = distinguishable_colors(25,'w',func); 56 | 57 | % Copyright 2010-2011 by Timothy E. Holy 58 | 59 | % Parse the inputs 60 | if (nargin < 2) 61 | bg = [1 1 1]; % default white background 62 | else 63 | if iscell(bg) 64 | % User specified a list of colors as a cell aray 65 | bgc = bg; 66 | for i = 1:length(bgc) 67 | bgc{i} = parsecolor(bgc{i}); 68 | end 69 | bg = cat(1,bgc{:}); 70 | else 71 | % User specified a numeric array of colors (n-by-3) 72 | bg = parsecolor(bg); 73 | end 74 | end 75 | 76 | % Generate a sizable number of RGB triples. This represents our space of 77 | % possible choices. By starting in RGB space, we ensure that all of the 78 | % colors can be generated by the monitor. 79 | n_grid = 30; % number of grid divisions along each axis in RGB space 80 | x = linspace(0,1,n_grid); 81 | [R,G,B] = ndgrid(x,x,x); 82 | rgb = [R(:) G(:) B(:)]; 83 | if (n_colors > size(rgb,1)/3) 84 | error('You can''t readily distinguish that many colors'); 85 | end 86 | 87 | % Convert to Lab color space, which more closely represents human 88 | % perception 89 | if (nargin > 2) 90 | lab = func(rgb); 91 | bglab = func(bg); 92 | else 93 | C = makecform('srgb2lab'); 94 | lab = applycform(rgb,C); 95 | bglab = applycform(bg,C); 96 | end 97 | 98 | % If the user specified multiple background colors, compute distances 99 | % from the candidate colors to the background colors 100 | mindist2 = inf(size(rgb,1),1); 101 | for i = 1:size(bglab,1)-1 102 | dX = bsxfun(@minus,lab,bglab(i,:)); % displacement all colors from bg 103 | dist2 = sum(dX.^2,2); % square distance 104 | mindist2 = min(dist2,mindist2); % dist2 to closest previously-chosen color 105 | end 106 | 107 | % Iteratively pick the color that maximizes the distance to the nearest 108 | % already-picked color 109 | colors = zeros(n_colors,3); 110 | lastlab = bglab(end,:); % initialize by making the "previous" color equal to background 111 | for i = 1:n_colors 112 | dX = bsxfun(@minus,lab,lastlab); % displacement of last from all colors on list 113 | dist2 = sum(dX.^2,2); % square distance 114 | mindist2 = min(dist2,mindist2); % dist2 to closest previously-chosen color 115 | [~,index] = max(mindist2); % find the entry farthest from all previously-chosen colors 116 | colors(i,:) = rgb(index,:); % save for output 117 | lastlab = lab(index,:); % prepare for next iteration 118 | end 119 | end 120 | 121 | function c = parsecolor(s) 122 | if ischar(s) 123 | c = colorstr2rgb(s); 124 | elseif isnumeric(s) && size(s,2) == 3 125 | c = s; 126 | else 127 | error('MATLAB:InvalidColorSpec','Color specification cannot be parsed.'); 128 | end 129 | end 130 | 131 | function c = colorstr2rgb(c) 132 | % Convert a color string to an RGB value. 133 | % This is cribbed from Matlab's whitebg function. 134 | % Why don't they make this a stand-alone function? 135 | rgbspec = [1 0 0;0 1 0;0 0 1;1 1 1;0 1 1;1 0 1;1 1 0;0 0 0]; 136 | cspec = 'rgbwcmyk'; 137 | k = find(cspec==c(1)); 138 | if isempty(k) 139 | error('MATLAB:InvalidColorString','Unknown color string.'); 140 | end 141 | if k~=3 || length(c)==1, 142 | c = rgbspec(k,:); 143 | elseif length(c)>2, 144 | if strcmpi(c(1:3),'bla') 145 | c = [0 0 0]; 146 | elseif strcmpi(c(1:3),'blu') 147 | c = [0 0 1]; 148 | else 149 | error('MATLAB:UnknownColorString', 'Unknown color string.'); 150 | end 151 | end 152 | end 153 | -------------------------------------------------------------------------------- /visualization/ellipse.m: -------------------------------------------------------------------------------- 1 | function h=ellipse(ra,rb,ang,x0,y0,C,Nb) 2 | % Ellipse adds ellipses to the current plot 3 | % 4 | % ELLIPSE(ra,rb,ang,x0,y0) adds an ellipse with semimajor axis of ra, 5 | % a semimajor axis of radius rb, a semimajor axis of ang, centered at 6 | % the point x0,y0. 7 | % 8 | % The length of ra, rb, and ang should be the same. 9 | % If ra is a vector of length L and x0,y0 scalars, L ellipses 10 | % are added at point x0,y0. 11 | % If ra is a scalar and x0,y0 vectors of length M, M ellipse are with the same 12 | % radii are added at the points x0,y0. 13 | % If ra, x0, y0 are vectors of the same length L=M, M ellipses are added. 14 | % If ra is a vector of length L and x0, y0 are vectors of length 15 | % M~=L, L*M ellipses are added, at each point x0,y0, L ellipses of radius ra. 16 | % 17 | % ELLIPSE(ra,rb,ang,x0,y0,C) 18 | % adds ellipses of color C. C may be a string ('r','b',...) or the RGB value. 19 | % If no color is specified, it makes automatic use of the colors specified by 20 | % the axes ColorOrder property. For several circles C may be a vector. 21 | % 22 | % ELLIPSE(ra,rb,ang,x0,y0,C,Nb), Nb specifies the number of points 23 | % used to draw the ellipse. The default value is 300. Nb may be used 24 | % for each ellipse individually. 25 | % 26 | % h=ELLIPSE(...) returns the handles to the ellipses. 27 | % 28 | % as a sample of how ellipse works, the following produces a red ellipse 29 | % tipped up at a 45 deg axis from the x axis 30 | % ellipse(1,2,pi/8,1,1,'r') 31 | % 32 | % note that if ra=rb, ELLIPSE plots a circle 33 | % 34 | 35 | % written by D.G. Long, Brigham Young University, based on the 36 | % CIRCLES.m original 37 | % written by Peter Blattner, Institute of Microtechnology, University of 38 | % Neuchatel, Switzerland, blattner@imt.unine.ch 39 | 40 | 41 | % Check the number of input arguments 42 | 43 | if nargin<1, 44 | ra=[]; 45 | end; 46 | if nargin<2, 47 | rb=[]; 48 | end; 49 | if nargin<3, 50 | ang=[]; 51 | end; 52 | 53 | %if nargin==1, 54 | % error('Not enough arguments'); 55 | %end; 56 | 57 | if nargin<5, 58 | x0=[]; 59 | y0=[]; 60 | end; 61 | 62 | if nargin<6, 63 | C=[]; 64 | end 65 | 66 | if nargin<7, 67 | Nb=[]; 68 | end 69 | 70 | % set up the default values 71 | 72 | if isempty(ra),ra=1;end; 73 | if isempty(rb),rb=1;end; 74 | if isempty(ang),ang=0;end; 75 | if isempty(x0),x0=0;end; 76 | if isempty(y0),y0=0;end; 77 | if isempty(Nb),Nb=300;end; 78 | if isempty(C),C=get(gca,'colororder');end; 79 | 80 | % work on the variable sizes 81 | 82 | x0=x0(:); 83 | y0=y0(:); 84 | ra=ra(:); 85 | rb=rb(:); 86 | ang=ang(:); 87 | Nb=Nb(:); 88 | 89 | if isstr(C),C=C(:);end; 90 | 91 | if length(ra)~=length(rb), 92 | error('length(ra)~=length(rb)'); 93 | end; 94 | if length(x0)~=length(y0), 95 | error('length(x0)~=length(y0)'); 96 | end; 97 | 98 | % how many inscribed elllipses are plotted 99 | 100 | if length(ra)~=length(x0) 101 | maxk=length(ra)*length(x0); 102 | else 103 | maxk=length(ra); 104 | end; 105 | 106 | % drawing loop 107 | 108 | for k=1:maxk 109 | 110 | if length(x0)==1 111 | xpos=x0; 112 | ypos=y0; 113 | radm=ra(k); 114 | radn=rb(k); 115 | if length(ang)==1 116 | an=ang; 117 | else 118 | an=ang(k); 119 | end; 120 | elseif length(ra)==1 121 | xpos=x0(k); 122 | ypos=y0(k); 123 | radm=ra; 124 | radn=rb; 125 | an=ang; 126 | elseif length(x0)==length(ra) 127 | xpos=x0(k); 128 | ypos=y0(k); 129 | radm=ra(k); 130 | radn=rb(k); 131 | an=ang(k); 132 | else 133 | rada=ra(fix((k-1)/size(x0,1))+1); 134 | radb=rb(fix((k-1)/size(x0,1))+1); 135 | an=ang(fix((k-1)/size(x0,1))+1); 136 | xpos=x0(rem(k-1,size(x0,1))+1); 137 | ypos=y0(rem(k-1,size(y0,1))+1); 138 | end; 139 | 140 | co=cos(an); 141 | si=sin(an); 142 | the=linspace(0,2*pi,Nb(rem(k-1,size(Nb,1))+1,:)+1); 143 | % x=radm*cos(the)*co-si*radn*sin(the)+xpos; 144 | % y=radm*cos(the)*si+co*radn*sin(the)+ypos; 145 | % h(k)=line(radm*cos(the)*co-si*radn*sin(the)+xpos,radm*cos(the)*si+co*ra 146 | % dn*sin(the)+ypos); 147 | h(k)=line(radm*cos(the)*co-si*radn*sin(the)+xpos,radm*cos(the)*si+co*radn*sin(the)+ypos,'linewidth',2); 148 | set(h(k),'color',C(rem(k-1,size(C,1))+1,:)); 149 | end; --------------------------------------------------------------------------------