| /************************************************************************* |
| * matlab.h |
| * --------- |
| * |
| * matlab.h is a "plugin" for the CImg library that allows to convert |
| * CImg<T> images from/to MATLAB arrays, so that CImg can be used to write |
| * MATLAB mex files. It also swaps the "x" and "y" coordinates when going |
| * from / to MATLAB array, i.e. the usual image-processing annoying MATLAB |
| * behaviour of considering images as matrices. |
| * |
| * Added to the CImg<T> class are: |
| * |
| * - a constructor : CImg(const mxArray *matlabArray, bool vdata = false) |
| * the vdata serves to decide whether a 3D matlab array should give |
| * rise to a 3D CImg object or a "2D vectorial" one. |
| * |
| * - a assignment operator : CImg & operator=(const mxArray *matlabArray) |
| * (I use myself extremely seldom and might remove it in the future). |
| * |
| * - a routine converting a CImg image to a matlab array: |
| * mxArray *toMatlab(mxClassID classID = mxDOUBLE_CLASS, |
| * bool squeeze = false) const |
| * the squeeze argument serves the opposite purpose than the vdata from |
| * the constructor. |
| * |
| * For a bit more documentation, the manual is this header, see the more |
| * detailed comments in the source code (i.e. RTFM) |
| * |
| * |
| * Its usage should be straightforward: |
| * |
| * - file matlab.h must be in a directory that the compiler can locate. |
| * - prior to include CImg.h, mex.h must be included first, else it will |
| * result in a compiler error. |
| * - after the inclusion of mex.h, one must define the macro cimg_plugin as |
| * "matlab.h" or <matlab.h> or <CImg/plugins/matlab.h> or |
| * a variation that matches your local installation of CImg package and |
| * plugins probably via the appropriate specification of the include path |
| * "-Ipath/to/cimg/and/plugins" at mex cmdline. |
| * |
| * You would probably have this kind of declaration: |
| * |
| * // The begining of my fantastic mex file code... |
| * #include <mex.h> |
| * ... |
| * #define cimg_plugin <matlab.h> |
| * #include <CImg.h> |
| * ... |
| * // and now I can implement my new killer MATLAB function! |
| * .... |
| * |
| * |
| * Copyright (c) 2004-2008 Francois Lauze |
| * Licence: the Gnu Lesser General Public License |
| * http://www.gnu.org/licenses/lgpl.html |
| * |
| * MATLAB is copyright of The MathWorks, Inc, http://www.mathworks.com |
| * |
| * Any comments, improvements and potential bug corrections are welcome, so |
| * write to me at francois@diku.dk, or use CImg forums, I promise I'll try |
| * to read them once in a while. BTW who modified the cpMatlabData with the |
| * cimg::type<t>::is_float() test (good idea!) |
| * |
| ***************************************************************************/ |
| |
| #ifndef cimg_plugin_matlab |
| #define cimg_plugin_matlab |
| |
| #define CIMGMATLAB_VER 0102 |
| #ifndef mex_h |
| #error the file mex.h must be included prior to inclusion of matlab.h |
| #endif |
| #ifndef cimg_version |
| #error matlab.h requires that CImg.h is included! |
| #endif |
| |
| /********************************************************** |
| * introduction of mwSize and mwIndex types in relatively * |
| * recent versions of matlab, 7.3.0 from what I gathered. * |
| * here is hopefully a needed fix for older versions * |
| **********************************************************/ |
| #if !defined(MX_API_VER) || MX_API_VER < 0x7030000 |
| typedef int mwSize; |
| #endif |
| |
| /********************************************************* |
| * begin of included methods * |
| * They are just added as member functions / constructor * |
| * for the CImg<T> class. * |
| *********************************************************/ |
| |
| private: |
| |
| /********************************************************************** |
| * internally used to transfer MATLAB array values to CImg<> objects, |
| * check wether the array type is a "numerical" one (including logical) |
| */ |
| static int isNumericalClassID(mxClassID id) { |
| // all these constants are defined in matrix.h included by mex.h |
| switch (id) { |
| case mxLOGICAL_CLASS: |
| case mxDOUBLE_CLASS: |
| case mxSINGLE_CLASS: |
| case mxINT8_CLASS: |
| case mxUINT8_CLASS: |
| case mxINT16_CLASS: |
| case mxUINT16_CLASS: |
| case mxINT32_CLASS: |
| case mxUINT32_CLASS: |
| case mxINT64_CLASS: |
| case mxUINT64_CLASS: return 1; |
| default: return 0; |
| } |
| } |
| |
| /*************************************************** |
| * driving routine that will copy the content of |
| * a MATLAB array to this->_data |
| * The type names used are defined in matlab c/c++ |
| * header file tmwtypes.h |
| */ |
| void makeImageFromMatlabData(const mxArray *matlabArray, mxClassID classID) { |
| if (classID==mxLOGICAL_CLASS) { |
| // logical type works a bit differently than the numerical types |
| mxLogical *mdata = mxGetLogicals(matlabArray); |
| cpMatlabData((const mxLogical *)mdata); |
| } else { |
| void *mdata = (void*)mxGetPr(matlabArray); |
| switch (classID) { |
| case mxDOUBLE_CLASS : cpMatlabData((const real64_T*)mdata); break; |
| case mxSINGLE_CLASS : cpMatlabData((const real32_T*)mdata); break; |
| case mxINT8_CLASS : cpMatlabData((const int8_T*)mdata); break; |
| case mxUINT8_CLASS : cpMatlabData((const uint8_T*)mdata); break; |
| case mxINT16_CLASS : cpMatlabData((const int16_T*)mdata); break; |
| case mxUINT16_CLASS : cpMatlabData((const uint16_T*)mdata); break; |
| case mxINT32_CLASS : cpMatlabData((const int32_T*)mdata); break; |
| case mxUINT32_CLASS : cpMatlabData((const uint32_T*)mdata); break; |
| case mxINT64_CLASS : cpMatlabData((const int64_T*)mdata); break; |
| case mxUINT64_CLASS : cpMatlabData((const uint64_T*)mdata); break; |
| } |
| } |
| } |
| |
| /*********************************************************** |
| * the actual memory copy and base type conversion is then |
| * performed by this routine that handles the annoying x-y |
| * problem of MATLAB when dealing with images: we switch |
| * line and column storage: the MATLAB A(x,y) becomes the |
| * CImg img(y,x) |
| */ |
| template <typename t> void cpMatlabData(const t* mdata) { |
| if (cimg::type<t>::is_float()) { |
| cimg_forXYZC(*this,x,y,z,v) (*this)(x,y,z,v) = (T)(mdata[((v*_depth + z)*_width + x)*_height + y]); |
| } else { |
| cimg_forXYZC(*this,x,y,z,v) (*this)(x,y,z,v) = (T)(int)(mdata[((v*_depth + z)*_width + x)*_height + y]); |
| } |
| } |
| |
| public: |
| |
| /****************************************************************** |
| * Consruct a CImg<T> object from a MATLAB mxArray. |
| * The MATLAB array must be AT MOST 4-dimensional. The boolean |
| * argument vdata is employed in the case the the input mxArray |
| * has dimension 3, say M x N x K. In that case, if vdata is true, |
| * the last dimension is assumed to be "vectorial" and the |
| * resulting CImg<T> object has dimension N x M x 1 x K. Otherwise, |
| * the resulting object has dimension N x M x K x 1. |
| * When MATLAB array has dimension 2 or 4, vdata has no effects. |
| * No shared memory mechanisms are used, it would be the easiest |
| * to crash Matlab (from my own experience...) |
| */ |
| CImg(const mxArray *matlabArray, const bool vdata = false) |
| : _is_shared(false) { |
| mwSize nbdims = mxGetNumberOfDimensions(matlabArray); |
| mxClassID classID = mxGetClassID(matlabArray); |
| if (nbdims>4 || !isNumericalClassID(classID)) { |
| _data = 0; _width = _height = _depth = _spectrum = 0; |
| #if cimg_debug>1 |
| cimg::warn("MATLAB array is more than 4D or/and not numerical, returning an empty image."); |
| #endif |
| } else { |
| const mwSize *dims = mxGetDimensions(matlabArray); |
| _depth = _spectrum = 1; |
| _width = (unsigned)dims[1]; |
| _height = (unsigned)dims[0]; |
| if (nbdims==4) { _depth = (unsigned)dims[2]; _spectrum = (unsigned)dims[3]; } |
| else if (nbdims==3) { |
| if (vdata) _spectrum = (unsigned)dims[2]; else _depth = (unsigned)dims[2]; |
| } |
| _data = new T[size()]; |
| makeImageFromMatlabData(matlabArray,classID); |
| } |
| } |
| |
| /******************************************************************* |
| * operator=(). Copy mxMarray data mArray into the current image |
| * Works as the previous constructor, but without the vdata stuff. |
| * don't know if it is of any use... |
| */ |
| CImg & operator=(const mxArray *matlabArray) { |
| int |
| nbdims = (int)mxGetNumberOfDimensions(matlabArray), |
| classID = mxGetClassID(matlabArray); |
| if (nbdims>4 || !isNumericalClassID(classID)) { |
| delete [] _data; _data = 0; |
| _width = _height = _depth = _spectrum = 0; |
| #if cimg_debug>1 |
| cimg::warn("MATLAB array is more than 4D or/and not numerical, returning an empty image."); |
| #endif |
| } else { |
| const mwSize *dims = mxGetDimensions(matlabArray); |
| _depth = _spectrum = 1; |
| _width = (unsigned)dims[1]; |
| _height = (unsigned)dims[0]; |
| if (nbdims>2) _depth = (unsigned)dims[2]; |
| else if (nbdims>3) _spectrum = (unsigned)dims[3]; |
| delete [] _data; |
| _data = new T[size()]; |
| makeImageFromMatlabData(matlabArray,classID); |
| } |
| } |
| |
| private: |
| |
| /***************************************************************** |
| * private routines used for transfering a CImg<T> to a mxArray |
| * here also, we have to exchange the x and y dims so we get the |
| * expected MATLAB array. |
| */ |
| template <typename c> void populate_maltlab_array(c *const mdata) const { |
| cimg_forXYZC(*this,x,y,z,v) mdata[((v*_depth + z)*_width + x)*_height + y] = (c)(*this)(x,y,z,v); |
| } |
| |
| /************************************************* |
| * the specialized version for "logical" entries |
| */ |
| void populate_maltlab_array(mxLogical *const mdata) const { |
| cimg_forXYZC(*this,x,y,z,v) mdata[((v*_depth + z)*_width + x)*_height + y] = (mxLogical)((*this)(x,y,z,v)!=0); |
| } |
| |
| public: |
| |
| /****************************************** |
| * export a CImg image to a MATLAB array. |
| **/ |
| mxArray *toMatlab(mxClassID classID=mxDOUBLE_CLASS, const bool squeeze=false) const { |
| if (!isNumericalClassID(classID)) { |
| #if cimg_debug>1 |
| cimg::warn("Invalid MATLAB Class Id Specified."); |
| #endif |
| return 0; |
| } |
| mwSize dims[4]; |
| dims[0] = (mwSize)_height; |
| dims[1] = (mwSize)_width; |
| dims[2] = (mwSize)_depth; |
| dims[3] = (mwSize)_spectrum; |
| |
| if (squeeze && _depth == 1) { |
| dims[2] = (mwSize)_spectrum; |
| dims[3] = (mwSize)1; |
| } |
| mxArray *matlabArray = mxCreateNumericArray((mwSize)4,dims,classID,mxREAL); |
| if (classID==mxLOGICAL_CLASS) { |
| mxLogical *mdata = mxGetLogicals(matlabArray); |
| populate_maltlab_array(mdata); |
| } else { |
| void *mdata = mxGetPr(matlabArray); |
| switch (classID) { |
| case mxDOUBLE_CLASS : populate_maltlab_array((real64_T*)mdata); break; |
| case mxSINGLE_CLASS : populate_maltlab_array((real32_T*)mdata); break; |
| case mxINT8_CLASS : populate_maltlab_array((int8_T*)mdata); break; |
| case mxUINT8_CLASS : populate_maltlab_array((uint8_T*)mdata); break; |
| case mxINT16_CLASS : populate_maltlab_array((int16_T*)mdata); break; |
| case mxUINT16_CLASS : populate_maltlab_array((uint16_T*)mdata); break; |
| case mxINT32_CLASS : populate_maltlab_array((int32_T*)mdata); break; |
| case mxUINT32_CLASS : populate_maltlab_array((uint32_T*)mdata); break; |
| case mxINT64_CLASS : populate_maltlab_array((int64_T*)mdata); break; |
| case mxUINT64_CLASS : populate_maltlab_array((uint64_T*)mdata); break; |
| } |
| } |
| return matlabArray; |
| } |
| |
| // end of matlab.h |
| #endif /* cimg_plugin_matlab */ |