Matlab和C++联合编程(基于opencv的特征点检测)

Matlab和C++联合编程(基于opencv)

/**************************************************************
* Copyright(c) 2016 唯疯
* All rights reserved.
*
* Version: 1.0
* Author: 唯疯
* Date: 2016/012/26夜(头痛)
* Address: 北京
**************************************************************/
关于Matlab和c++混合编程的优点不多赘述,这篇博客就Matlab中的mex工具的操作做较为详细的介绍(不同于网上大多数的内容)。mex工具将c++代码编译成Matlab支持调用的可执行文件和函数接口,在Matlab中就可以利用这个编号的c++函数。并且可以在vs中设置断点,进行联合调试(联合调试的内容,可以参考这篇博客http://blog.csdn.net/lcb_coconut/article/details/53887783,使用起来还是很方便的。不过操作过程中会碰到很多问题。我是采用Matlab2012b和vs2010进行联合编译。

网上有很多文章介绍,但是基本上都是同根同源,被反复的转载。个人感觉有几篇不错的,可以值得参考:
http://blog.csdn.net/zouxy09/article/details/20553007

http://www.iloveMatlab.cn/thread-5576-1-1.html

http://wenku.baidu.com/link?url=j_dodJCxbjYOAiP2X3nVe3767lOqlzO2Gf7B-pe24vf7mR6JI6h5tJ42ZjdseaXbbre3k8ITwgPzNm-IyeqkgW3z0eH39WZA13oxTYrig47%20%EF%BC%88%E5%A6%82%E4%BD%95%E5%9C%A8Matlab%E4%B8%AD%E9%85%8D%E7%BD%AE%EF%BC%8C%E8%AE%B2%E7%9A%84%E6%AF%94%E8%BE%83%E5%A5%BD%EF%BC%89

1. 首先,先贴一个网上常用的代码段:

#include "opencv2/opencv.hpp"  
#include "mex.h"  //必须在c++文件开头添加这个头文件

double add(double x, double y)  
{  
    return x + y;  
}  


void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])  //接口函数,相当于c语言中的main函数
{  
    double *a;  
    double b, c;  
    plhs[0] = mxCreateDoubleMatrix(1, 1, mxREAL);  
    a = mxGetPr(plhs[0]);  
    b = *(mxGetPr(prhs[0]));  
    c = *(mxGetPr(prhs[1]));  
    *a = add(b, c);  
}  

在Matlab中调用方式为:

output = add(0.5, 0.8);

mex工具接口的具体使用方法,可以参照以上几篇文章,这里不多做转载。

2. 使用mex进行传入/传出参数的测试,以及矩阵数据交互的方法:

传入传出参数,主要就是靠mexFunction函数的四个参数,分别是返回参数的个数nlhs、返回参数的指针plhs、传入参数的个数nrhs、传入参数的指针prhs。另外需要注意,prhs和plhs都是指向mxArray类型数据的指针,这个类型是在mex.h中定义的。以下的代码是基于SURF特征点检测和FREAK特征描述的算法,首先将最直观的代码给大家就数据的传入传出,和矩阵交互的方法进行解释:

#include "mex.h"
#include "matrix.h"

#include <opencv2/opencv.hpp>
#include <opencv2/nonfree/nonfree.hpp>
#include <opencv2/nonfree/features2d.hpp>
#include <vector>

// input variable order numbers
#define IMG 0
#define SIZE 1  // minimum radius for detected keypoints

// output variable order numbers
#define DESC 0
#define KEYP 1

using namespace std;
using namespace cv;

// entry point
void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] )     
{
    // SURF keypoint detector settings
    int minHessian = 400;

    // FREAK's extractor's settings
    bool orientationNormalized = true;
    bool scaleNormalized = true;
    int nOctaves = 4;

    // check input argument number
    if ( nrhs < 2 )
        mexErrMsgTxt( "Two input arguments are required." );

    // first argument must be UINT8 matrix
    if ( !(mxGetClassID(prhs[IMG])==mxUINT8_CLASS) )
        mexErrMsgTxt( "Input image must be UINT8 type." );

    // get interest point size (radius in pixels)
    double *tmp = mxGetPr( prhs[SIZE] );
    float patternScale = static_cast<float> ( *tmp );

    if ( patternScale <= 0 )
        mexErrMsgTxt( "Keypoint size should be a positive value." );

    // get image dimensions
    int nrows = static_cast<int>( mxGetM(prhs[IMG]) );
    int ncols = static_cast<int>( mxGetN(prhs[IMG]) );

    if ( nrows <= 1 || ncols <= 1 )
        mexErrMsgTxt( "Input is not an image?" );

    // get pointer to the image data,实际上img_data指向的图像数据是按照
    //0 2 4 6
    //1 3 5 7这种顺序排列的,img_data指向一个一维数组。要把它转换成一个可以在vs中处理的矩阵
        uchar * img_data = (uchar*) mxGetData( prhs[IMG] );

    //创建一个二维数组,将img_data指针中的一维数据拷贝出来*************
    Mat img2=Mat(nrows,ncols,CV_8U);
    for(int i=0;i<nrows;i++)
    {
        for(int j=0;j<ncols;j++)
        {
            img2.at<uchar>(i,j)=img_data[j*nrows+i];
        }
    }
    Mat img = Mat(img2);
   //******************************************

    // create instance for keypoint storage
    vector<KeyPoint> keypoints;

    // create instance of dense feature detector
    SurfFeatureDetector detector(minHessian);


    // detect features
    detector.detect( img, keypoints );  

    // create descriptor extractor instance
    FREAK extractor( orientationNormalized, scaleNormalized, 
                     patternScale, nOctaves );

    // create instance for descriptor storage
    Mat descriptors;

    // compute descriptors (descriptors are stored row-wise in the matrix)
    extractor.compute( img, keypoints, descriptors );

    // get number of keypoints and their dimensionality
    const int numKeypoints = keypoints.size();
    const int dimsKeypoints = 4;

    // allocate output array
    mxArray* keyp = mxCreateDoubleMatrix( dimsKeypoints, numKeypoints, mxREAL );

    // get pointer to keypoint data for writing
    double *keyp_data = mxGetPr( keyp );

    // copy the keypoint data
    for ( int i = 0 ; i < numKeypoints ; i++ ) {
        keyp_data[4*i] = keypoints[i].pt.x;
        keyp_data[4*i+1] = keypoints[i].pt.y;
        keyp_data[4*i+2] = keypoints[i].size;
        keyp_data[4*i+3] = keypoints[i].angle;
    }

    // assign output
    plhs[KEYP] = keyp;

    // get descriptor dimensionality and number
    mwSize dim[2];
    dim[0] = descriptors.cols;
    dim[1] = descriptors.rows;

    // allocate output array
    mxArray* desc = mxCreateNumericArray( 2, dim, mxUINT8_CLASS, mxREAL );

    // get pointer to descriptor data
    uchar *desc_data = (uchar*) mxGetData( desc );

    // copy the descriptor data (should be line by line copy from "descriptors" matrix)
    memcpy( desc_data, descriptors.data, dim[0] * dim[1] );

    // assign output
    plhs[DESC] = desc;

}
  • 可以看到,接口函数共传入两个参数,分别是一个矩阵,和一个字符(FREAK算法的尺度参数patternScale)。传入传出都是依靠mxArray类型数据的指针,这个类型在mex.h中定义。字符比较好理解,矩阵的传入传出需要注意一点,就是Matlab里的矩阵元素(i,j),经过mxGetData函数后,对应一维数组中的[j*M+i],其中M是每列元素的个数。这个函数是按照一列一列的方式获取矩阵中的元素,所以要这么表示。得到一个一维数组的指针img_data之后,如果要想再次转换为数组供c++处理,就需要重新创建一个Mat类的矩阵,然后将元素逐个赋值出来。
  • 其实也可以直接在mexFunction函数中读取图片,这样就不用再传入矩阵这么麻烦了,不过在mexFunction函数中读取图片的时候,采用imread()这个函数总是崩溃(我也愁究竟怎么回事,如果有人知道欢迎分享),不过倒是可以采用一种替代的方法:
    IpiImage* img=cvLoadImage();
    Mat img2=Mat(img);
  • 输入数据是在函数调用之前已经在Matlab里申请了内存的,由于mex函数与Matlab公用同一个地址空间,因而在prhs[]里传递指针就可以达到参数传递的目的。但是输出参数却需要在mex函数内申请内存空间,才能将指针放在plhs[]中传递出去。由于返回指针类型必须是mxArray,所以Matlab专门提供了一个函数:mxCreateDoubleMatrix来实现内存的申请,函数原型如下:
    mxArray *mxCreateDoubleMatrix(int m, int n, mxComplexity ComplexFlag)
    m:待申请矩阵的行数
    n:待申请矩阵的列数
    为矩阵申请内存后,得到的是mxArray类型的指针,就可以放在plhs[]里传递回去了。但是对这个新矩阵的处理要在函数内完成,通过mxGetPr创建一个指向这个矩阵中数据区的指针,然后就可以对指向这个一维数组的指针进行操作,来改变矩阵的元素,最后将plhs[]和mxCreateDoubleMatrix申请到的矩阵建立关联即可实现数据传出。具体可以参考上面的代码。
  • 本代码实际上在最后运行的时候总是出现崩溃的问题。崩溃的地方在detector.detect( img, keypoints );也就是特征点检测的这个语句这里,总是显示“vector iterators incompatible”。我查了很久,始终没有解决这个问题,实际上在vs里面运行时没有问题的,但是一旦使用mex工具就总是出故障,大概耗费了我两天的时间,始终没有解决。也希望碰到相似问题或者已经解决问题的大神们多多赐教。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值