Skip to content

Commit 292532e

Browse files
committed
MATLAB MEX interface added, together with compiling instruction in the README file.
MEX interface written and tested on Windows 7 with MSVC 2010 x64.
1 parent b137bc0 commit 292532e

File tree

2 files changed

+293
-0
lines changed

2 files changed

+293
-0
lines changed

README.md

+13
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ Please check the Library dependencies section.
6767
Examples:
6868
To see how the code works, examine the three examples provided.
6969

70+
## MATLAB interface
71+
A mex interface for computing AKAZE features is supplied, in the file `src/akaze.cpp`.
72+
To be able to use it, first compile the library as explained above. Then, you will need to compile the mex from matlab.
73+
The following is an example for compiling the mex on Windows 64 bit, Visual Studio 10 and OpenCV 2.4.8. from the `src` folder, type in MATLAB:
74+
mex akaze.cpp -Ilib -L'..\build\lib\Release\' -I'<path_to_opencv>\build\include' -L'<path_to_opencv>\build\x64\vc10\lib' -lopencv_calib3d248 -lopencv_contrib248 -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lAKAZE
75+
For other platforms / compilers / OpenCV versions, change the above line accordingly.
76+
On Windows, you'll need to make sure that the corresponding OpenCV bin folder is added to your system path before staring MATLAB. e.g.:
77+
PATH=<path_to_opencv>\build\x64\vc10\bin
78+
79+
Once the mex file is compiled successfully, type:
80+
akaze
81+
to display function help.
82+
7083
## Documentation
7184
In the working folder, type: `doxygen`
7285

src/akaze.cpp

+280
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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

Comments
 (0)