|
| 1 | +// |
| 2 | +//============================================================================= |
| 3 | +// MEX Compilation example (with OpenCV 2.4.8): |
| 4 | +// mex akaze.cpp -Ilib -L'..\build\lib\Release\' -I'c:\files\libs\opencv\build\include' -L'c:\files\libs\opencv\build\x64\vc10\lib' -lopencv_calib3d248 -lopencv_contrib248 -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lAKAZE |
| 5 | +// |
| 6 | +//============================================================================= |
| 7 | +// |
| 8 | +// AKAZE features MEX wrapper |
| 9 | +// Author: Zohar Bar-Yehuda |
| 10 | +// Date: 09/02/2014 |
| 11 | + |
| 12 | +// |
| 13 | +// AKAZE Features Copyright 2013, Pablo F. Alcantarilla, Jesus Nuevo |
| 14 | +// All Rights Reserved |
| 15 | +// See LICENSE for the license information |
| 16 | +//============================================================================= |
| 17 | +// |
| 18 | +// for help type: |
| 19 | +// akaze |
| 20 | + |
| 21 | + |
| 22 | +#include <math.h> |
| 23 | +#include <matrix.h> |
| 24 | +#include <mex.h> |
| 25 | +#include "AKAZE.h" |
| 26 | +#include "config.h" |
| 27 | + |
| 28 | +using namespace std; |
| 29 | +using namespace cv; |
| 30 | + |
| 31 | +/** |
| 32 | + * @brief This function shows the possible configuration options |
| 33 | + */ |
| 34 | +void show_input_options_help() { |
| 35 | + |
| 36 | + mexPrintf("A-KAZE Features\n"); |
| 37 | + mexPrintf("Usage:\n"); |
| 38 | + mexPrintf("[kps,desc] = akaze(gray_img, param1, value1, ...)\n\n"); |
| 39 | + mexPrintf("Options below are not mandatory. Unless specified, default arguments are used.\n"); |
| 40 | + |
| 41 | + mexPrintf("Scale-space parameters:\n"); |
| 42 | + mexPrintf("soffset - Base scale offset [sigma units] (default=1.6)\n"); |
| 43 | + mexPrintf("omax - Maximum octave of image evolution (default=4)\n"); |
| 44 | + mexPrintf("nsublevels - Number of sublevels per octave (default=4)\n"); |
| 45 | + mexPrintf("diffusivity - Diffusivity function. Possible values:\n"); |
| 46 | + mexPrintf(" 0 -> Perona-Malik, g1 = exp(-|dL|^2/k^2)\n"); |
| 47 | + mexPrintf(" 1 -> Perona-Malik, g2 = 1 / (1 + dL^2 / k^2) (default)\n"); |
| 48 | + mexPrintf(" 2 -> Weickert diffusivity\n"); |
| 49 | + mexPrintf(" 3 -> Charbonnier diffusivity\n"); |
| 50 | + |
| 51 | + mexPrintf("\nFeature detection parameters:\n"); |
| 52 | + mexPrintf("dthreshold - Feature detector threshold response for keypoints (0.001 can be a good value)\n"); |
| 53 | + |
| 54 | + mexPrintf("\nDescriptor parameters:\n"); |
| 55 | + mexPrintf("descriptor - Descriptor Type. Possible values:\n"); |
| 56 | + mexPrintf(" 0 -> SURF_UPRIGHT\n"); |
| 57 | + mexPrintf(" 1 -> SURF\n"); |
| 58 | + mexPrintf(" 2 -> M-SURF_UPRIGHT,\n"); |
| 59 | + mexPrintf(" 3 -> M-SURF\n"); |
| 60 | + mexPrintf(" 4 -> M-LDB_UPRIGHT\n"); |
| 61 | + mexPrintf(" 5 -> M-LDB (default)\n"); |
| 62 | + |
| 63 | + mexPrintf("descriptor_channels - Descriptor Channels for M-LDB. Valid values: \n"); |
| 64 | + mexPrintf(" 1 -> intensity\n"); |
| 65 | + mexPrintf(" 2 -> intensity + gradient magnitude\n"); |
| 66 | + mexPrintf(" 3 -> intensity + X and Y gradients (default)\n"); |
| 67 | + |
| 68 | + mexPrintf("descriptor_size - Descriptor size for M-LDB in bits.\n"); |
| 69 | + mexPrintf(" 0: means the full length descriptor (486) (default=0)\n"); |
| 70 | + mexPrintf("\nMisc:\n"); |
| 71 | + mexPrintf("verbose - Verbose mode. Prints calculation times and stores scale space images in ..\\output\\ folder (if exists)\n\n"); |
| 72 | +} |
| 73 | + |
| 74 | + |
| 75 | +//************************************************************************************* |
| 76 | +//************************************************************************************* |
| 77 | + |
| 78 | +/** |
| 79 | + * @brief This function parses the parameter arguments for setting A-KAZE parameters |
| 80 | + * @param options Structure that contains A-KAZE settings |
| 81 | + */ |
| 82 | +int parse_input_options(AKAZEOptions& options, int nrhs, const mxArray *prhs[]) { |
| 83 | + |
| 84 | + if (nrhs >= 3) { |
| 85 | + |
| 86 | + for (int i = 1; i < nrhs; i+=2) { |
| 87 | + if( !mxIsChar(prhs[i]) || !mxIsNumeric(prhs[i+1])) { |
| 88 | + mexErrMsgIdAndTxt("akaze:badParamTypes", |
| 89 | + "Params must be string,value pairs."); |
| 90 | + return 1; |
| 91 | + } |
| 92 | + |
| 93 | + char *param_name = mxArrayToString(prhs[i]); |
| 94 | + |
| 95 | + if (!strcmp(param_name,"soffset")) { |
| 96 | + options.soffset = mxGetScalar(prhs[i+1]); |
| 97 | + continue; |
| 98 | + } |
| 99 | + if (!strcmp(param_name,"omax")) { |
| 100 | + options.omax = mxGetScalar(prhs[i+1]); |
| 101 | + continue; |
| 102 | + } |
| 103 | + if (!strcmp(param_name,"dthreshold")) { |
| 104 | + options.dthreshold = mxGetScalar(prhs[i+1]); |
| 105 | + continue; |
| 106 | + } |
| 107 | + if (!strcmp(param_name,"sderivatives")) { |
| 108 | + options.sderivatives = mxGetScalar(prhs[i+1]); |
| 109 | + continue; |
| 110 | + } |
| 111 | + if (!strcmp(param_name,"nsublevels")) { |
| 112 | + options.nsublevels = mxGetScalar(prhs[i+1]); |
| 113 | + continue; |
| 114 | + } |
| 115 | + if (!strcmp(param_name,"diffusivity")) { |
| 116 | + options.diffusivity = mxGetScalar(prhs[i+1]); |
| 117 | + continue; |
| 118 | + } |
| 119 | + if (!strcmp(param_name,"descriptor")) { |
| 120 | + options.descriptor = mxGetScalar(prhs[i+1]); |
| 121 | + continue; |
| 122 | + } |
| 123 | + if (!strcmp(param_name,"descriptor")) { |
| 124 | + options.descriptor = mxGetScalar(prhs[i+1]); |
| 125 | + continue; |
| 126 | + } |
| 127 | + if (!strcmp(param_name,"descriptor")) { |
| 128 | + options.descriptor = mxGetScalar(prhs[i+1]); |
| 129 | + continue; |
| 130 | + } |
| 131 | + if (!strcmp(param_name,"descriptor")) { |
| 132 | + options.descriptor = mxGetScalar(prhs[i+1]); |
| 133 | + continue; |
| 134 | + if (options.descriptor < 0 || options.descriptor > MLDB) { |
| 135 | + options.descriptor = MLDB; |
| 136 | + } |
| 137 | + } |
| 138 | + if (!strcmp(param_name,"descriptor_channels")) { |
| 139 | + options.descriptor_channels = mxGetScalar(prhs[i+1]); |
| 140 | + if (options.descriptor_channels <= 0 || options.descriptor_channels > 3) { |
| 141 | + options.descriptor_channels = 3; |
| 142 | + } |
| 143 | + continue; |
| 144 | + } |
| 145 | + if (!strcmp(param_name,"descriptor_size")) { |
| 146 | + options.descriptor_size = mxGetScalar(prhs[i+1]); |
| 147 | + if (options.descriptor_size < 0) { |
| 148 | + options.descriptor_size = 0; |
| 149 | + } |
| 150 | + continue; |
| 151 | + } |
| 152 | + if (!strcmp(param_name,"verbose")) { |
| 153 | + options.verbosity = mxGetScalar(prhs[i+1]); |
| 154 | + continue; |
| 155 | + } |
| 156 | + if (!strcmp(param_name,"save_scale_space")) { |
| 157 | + options.save_scale_space = mxGetScalar(prhs[i+1]); |
| 158 | + continue; |
| 159 | + } |
| 160 | + |
| 161 | + mexPrintf("Bad Param name: %s\n",param_name); |
| 162 | + mexErrMsgIdAndTxt("akaze:badParamName", |
| 163 | + "Bad parameter name."); |
| 164 | + return 1; |
| 165 | + |
| 166 | + } |
| 167 | + } |
| 168 | + return 0; |
| 169 | +} |
| 170 | + |
| 171 | +//************************************************************************************* |
| 172 | +//************************************************************************************* |
| 173 | + |
| 174 | +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){ |
| 175 | + |
| 176 | + // Variables |
| 177 | + AKAZEOptions options; |
| 178 | + |
| 179 | + // Variable for computation times. |
| 180 | + double t1 = 0.0, t2 = 0.0, tcvt = 0.0, tdet = 0.0, tdesc = 0.0; |
| 181 | + |
| 182 | + if (nrhs == 0) { |
| 183 | + show_input_options_help(); |
| 184 | + return; |
| 185 | + } |
| 186 | + |
| 187 | + if( !mxIsUint8(prhs[0])) { |
| 188 | + mexErrMsgIdAndTxt("akaze:notUint8", |
| 189 | + "First Input must be a grayscale image of class UINT8."); |
| 190 | + } |
| 191 | + |
| 192 | + if (nrhs % 2 == 0) |
| 193 | + mexErrMsgIdAndTxt("akaze:badArgNum", |
| 194 | + "First input must be an image, followed by paramaters name,value pairs."); |
| 195 | + |
| 196 | + if (nrhs > 1){ |
| 197 | + if (parse_input_options(options, nrhs, prhs)) { |
| 198 | + return; |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + //Don't forget to specify image dimensions in AKAZE's options. |
| 203 | + options.img_width = mxGetM(prhs[0]); |
| 204 | + options.img_height = mxGetN(prhs[0]); |
| 205 | + |
| 206 | + Mat img = Mat(options.img_height, options.img_width, CV_8U, mxGetPr(prhs[0])); |
| 207 | + // OpenCV image is now a transposed image (because it's treated as row-major). |
| 208 | + |
| 209 | + Mat img_32; |
| 210 | + t1 = getTickCount(); |
| 211 | + img.convertTo(img_32,CV_32F,1.0/255.0,0); // convert to float for descriptor computations |
| 212 | + t2 = getTickCount(); |
| 213 | + tcvt = 1000.0*(t2-t1) / getTickFrequency(); |
| 214 | + |
| 215 | + // Extract features. |
| 216 | + vector<KeyPoint> kpts; |
| 217 | + t1 = getTickCount(); |
| 218 | + AKAZE evolution(options); |
| 219 | + evolution.Create_Nonlinear_Scale_Space(img_32); |
| 220 | + evolution.Feature_Detection(kpts); |
| 221 | + t2 = getTickCount(); |
| 222 | + tdet = 1000.0*(t2-t1) / getTickFrequency(); |
| 223 | + |
| 224 | + if (nlhs > 0){ |
| 225 | + plhs[0] = mxCreateDoubleMatrix(kpts.size(),2,mxREAL); |
| 226 | + double* pts_ptr = mxGetPr(plhs[0]); |
| 227 | + for (int i = 0 ; i < kpts.size() ; i++){ |
| 228 | + // Swap x,y back to get original coordinates |
| 229 | + pts_ptr[i] = kpts[i].pt.y; |
| 230 | + pts_ptr[kpts.size()+i] = kpts[i].pt.x; |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + if (nlhs == 2){ |
| 235 | + // Compute descriptors. |
| 236 | + Mat desc; |
| 237 | + t1 = getTickCount(); |
| 238 | + evolution.Compute_Descriptors(kpts,desc); |
| 239 | + t2 = getTickCount(); |
| 240 | + tdesc = 1000.0*(t2-t1) / getTickFrequency(); |
| 241 | + if (desc.type() == CV_8UC1){ |
| 242 | + plhs[1] = mxCreateNumericMatrix(desc.cols,desc.rows,mxUINT8_CLASS, mxREAL); |
| 243 | + // copy descriptors (desc will be freed on function exit) |
| 244 | + unsigned char* desc_ptr = (unsigned char*) mxGetPr(plhs[1]); |
| 245 | + unsigned char* mat_ptr = desc.ptr(); |
| 246 | + for (int i = 0 ; i < desc.rows * desc.cols ; i++) |
| 247 | + desc_ptr[i] = mat_ptr[i]; |
| 248 | + } |
| 249 | + else if (desc.type() == CV_32FC1){ |
| 250 | + plhs[1] = mxCreateNumericMatrix(desc.cols,desc.rows,mxSINGLE_CLASS, mxREAL); |
| 251 | + // copy descriptors (desc will be freed on function exit) |
| 252 | + float* desc_ptr = (float*) mxGetPr(plhs[1]); |
| 253 | + float* mat_ptr = (float*) desc.ptr(); |
| 254 | + for (int i = 0 ; i < desc.rows * desc.cols ; i++) |
| 255 | + desc_ptr[i] = mat_ptr[i]; |
| 256 | + } |
| 257 | + else{ |
| 258 | + mexErrMsgIdAndTxt("akaze:unknownDescType", |
| 259 | + "Unknown descriptor type."); |
| 260 | + return; |
| 261 | + } |
| 262 | + |
| 263 | + } |
| 264 | + // Summarize the computation times. |
| 265 | + if (options.verbosity){ |
| 266 | + evolution.Show_Computation_Times(); |
| 267 | + evolution.Save_Scale_Space(); |
| 268 | + mexPrintf("Number of points: %d\n", kpts.size()); |
| 269 | + mexPrintf("Time Conversion uint8->float: %.2f ms.\n", tcvt); |
| 270 | + mexPrintf("Time Detector: %.2f ms.\n", tdet); |
| 271 | + if (nlhs == 2) |
| 272 | + mexPrintf("Time Descriptor: %.2f ms.\n", tdesc); |
| 273 | + } |
| 274 | + |
| 275 | +// Mat img_rgb = Mat(Size(img.cols,img.rows),CV_8UC3); |
| 276 | +// cvtColor(img,img_rgb,CV_GRAY2BGR); |
| 277 | +// draw_keypoints(img_rgb,kpts); |
| 278 | +// imshow("",img_rgb); |
| 279 | +// waitKey(0); |
| 280 | +} |
0 commit comments