1 /*************************************************************************
2 * matlab.h
3 * ---------
4 *
5 * matlab.h is a "plugin" for the CImg library that allows to convert
6 * CImg<T> images from/to MATLAB arrays, so that CImg can be used to write
7 * MATLAB mex files. It also swaps the "x" and "y" coordinates when going
8 * from / to MATLAB array, i.e. the usual image-processing annoying MATLAB
9 * behaviour of considering images as matrices.
10 *
11 * Added to the CImg<T> class are:
12 *
13 * - a constructor : CImg(const mxArray *matlabArray, bool vdata = false)
14 * the vdata serves to decide whether a 3D matlab array should give
15 * rise to a 3D CImg object or a "2D vectorial" one.
16 *
17 * - a assignment operator : CImg & operator=(const mxArray *matlabArray)
18 * (I use myself extremely seldom and might remove it in the future).
19 *
20 * - a routine converting a CImg image to a matlab array:
21 * mxArray *toMatlab(mxClassID classID = mxDOUBLE_CLASS,
22 * bool squeeze = false) const
23 * the squeeze argument serves the opposite purpose than the vdata from
24 * the constructor.
25 *
26 * For a bit more documentation, the manual is this header, see the more
27 * detailed comments in the source code (i.e. RTFM)
28 *
29 *
30 * Its usage should be straightforward:
31 *
32 * - file matlab.h must be in a directory that the compiler can locate.
33 * - prior to include CImg.h, mex.h must be included first, else it will
34 * result in a compiler error.
35 * - after the inclusion of mex.h, one must define the macro cimg_plugin as
36 * "matlab.h" or <matlab.h> or <CImg/plugins/matlab.h> or
37 * a variation that matches your local installation of CImg package and
38 * plugins probably via the appropriate specification of the include path
39 * "-Ipath/to/cimg/and/plugins" at mex cmdline.
40 *
41 * You would probably have this kind of declaration:
42 *
43 * // The begining of my fantastic mex file code...
44 * #include <mex.h>
45 * ...
46 * #define cimg_plugin <matlab.h>
47 * #include <CImg.h>
48 * ...
49 * // and now I can implement my new killer MATLAB function!
50 * ....
51 *
52 *
53 * Copyright (c) 2004-2008 Francois Lauze
54 * Licence: the Gnu Lesser General Public License
55 * http://www.gnu.org/licenses/lgpl.html
56 *
57 * MATLAB is copyright of The MathWorks, Inc, http://www.mathworks.com
58 *
59 * Any comments, improvements and potential bug corrections are welcome, so
60 * write to me at francois@diku.dk, or use CImg forums, I promise I'll try
61 * to read them once in a while. BTW who modified the cpMatlabData with the
62 * cimg::type<t>::is_float() test (good idea!)
63 *
64 ***************************************************************************/
65
66 #ifndef cimg_plugin_matlab
67 #define cimg_plugin_matlab
68
69 #define CIMGMATLAB_VER 0102
70 #ifndef mex_h
71 #error the file mex.h must be included prior to inclusion of matlab.h
72 #endif
73 #ifndef cimg_version
74 #error matlab.h requires that CImg.h is included!
75 #endif
76
77 /**********************************************************
78 * introduction of mwSize and mwIndex types in relatively *
79 * recent versions of matlab, 7.3.0 from what I gathered. *
80 * here is hopefully a needed fix for older versions *
81 **********************************************************/
82 #if !defined(MX_API_VER) || MX_API_VER < 0x7030000
83 typedef int mwSize;
84 #endif
85
86 /*********************************************************
87 * begin of included methods *
88 * They are just added as member functions / constructor *
89 * for the CImg<T> class. *
90 *********************************************************/
91
92 private:
93
94 /**********************************************************************
95 * internally used to transfer MATLAB array values to CImg<> objects,
96 * check wether the array type is a "numerical" one (including logical)
97 */
isNumericalClassID(mxClassID id)98 static int isNumericalClassID(mxClassID id) {
99 // all these constants are defined in matrix.h included by mex.h
100 switch (id) {
101 case mxLOGICAL_CLASS:
102 case mxDOUBLE_CLASS:
103 case mxSINGLE_CLASS:
104 case mxINT8_CLASS:
105 case mxUINT8_CLASS:
106 case mxINT16_CLASS:
107 case mxUINT16_CLASS:
108 case mxINT32_CLASS:
109 case mxUINT32_CLASS:
110 case mxINT64_CLASS:
111 case mxUINT64_CLASS: return 1;
112 default: return 0;
113 }
114 }
115
116 /***************************************************
117 * driving routine that will copy the content of
118 * a MATLAB array to this->_data
119 * The type names used are defined in matlab c/c++
120 * header file tmwtypes.h
121 */
makeImageFromMatlabData(const mxArray * matlabArray,mxClassID classID)122 void makeImageFromMatlabData(const mxArray *matlabArray, mxClassID classID) {
123 if (classID==mxLOGICAL_CLASS) {
124 // logical type works a bit differently than the numerical types
125 mxLogical *mdata = mxGetLogicals(matlabArray);
126 cpMatlabData((const mxLogical *)mdata);
127 } else {
128 void *mdata = (void*)mxGetPr(matlabArray);
129 switch (classID) {
130 case mxDOUBLE_CLASS : cpMatlabData((const real64_T*)mdata); break;
131 case mxSINGLE_CLASS : cpMatlabData((const real32_T*)mdata); break;
132 case mxINT8_CLASS : cpMatlabData((const int8_T*)mdata); break;
133 case mxUINT8_CLASS : cpMatlabData((const uint8_T*)mdata); break;
134 case mxINT16_CLASS : cpMatlabData((const int16_T*)mdata); break;
135 case mxUINT16_CLASS : cpMatlabData((const uint16_T*)mdata); break;
136 case mxINT32_CLASS : cpMatlabData((const int32_T*)mdata); break;
137 case mxUINT32_CLASS : cpMatlabData((const uint32_T*)mdata); break;
138 case mxINT64_CLASS : cpMatlabData((const int64_T*)mdata); break;
139 case mxUINT64_CLASS : cpMatlabData((const uint64_T*)mdata); break;
140 }
141 }
142 }
143
144 /***********************************************************
145 * the actual memory copy and base type conversion is then
146 * performed by this routine that handles the annoying x-y
147 * problem of MATLAB when dealing with images: we switch
148 * line and column storage: the MATLAB A(x,y) becomes the
149 * CImg img(y,x)
150 */
cpMatlabData(const t * mdata)151 template <typename t> void cpMatlabData(const t* mdata) {
152 if (cimg::type<t>::is_float()) {
153 cimg_forXYZC(*this,x,y,z,v) (*this)(x,y,z,v) = (T)(mdata[((v*_depth + z)*_width + x)*_height + y]);
154 } else {
155 cimg_forXYZC(*this,x,y,z,v) (*this)(x,y,z,v) = (T)(int)(mdata[((v*_depth + z)*_width + x)*_height + y]);
156 }
157 }
158
159 public:
160
161 /******************************************************************
162 * Consruct a CImg<T> object from a MATLAB mxArray.
163 * The MATLAB array must be AT MOST 4-dimensional. The boolean
164 * argument vdata is employed in the case the the input mxArray
165 * has dimension 3, say M x N x K. In that case, if vdata is true,
166 * the last dimension is assumed to be "vectorial" and the
167 * resulting CImg<T> object has dimension N x M x 1 x K. Otherwise,
168 * the resulting object has dimension N x M x K x 1.
169 * When MATLAB array has dimension 2 or 4, vdata has no effects.
170 * No shared memory mechanisms are used, it would be the easiest
171 * to crash Matlab (from my own experience...)
172 */
173 CImg(const mxArray *matlabArray, const bool vdata = false)
_is_shared(false)174 : _is_shared(false) {
175 mwSize nbdims = mxGetNumberOfDimensions(matlabArray);
176 mxClassID classID = mxGetClassID(matlabArray);
177 if (nbdims>4 || !isNumericalClassID(classID)) {
178 _data = 0; _width = _height = _depth = _spectrum = 0;
179 #if cimg_debug>1
180 cimg::warn("MATLAB array is more than 4D or/and not numerical, returning an empty image.");
181 #endif
182 } else {
183 const mwSize *dims = mxGetDimensions(matlabArray);
184 _depth = _spectrum = 1;
185 _width = (unsigned)dims[1];
186 _height = (unsigned)dims[0];
187 if (nbdims==4) { _depth = (unsigned)dims[2]; _spectrum = (unsigned)dims[3]; }
188 else if (nbdims==3) {
189 if (vdata) _spectrum = (unsigned)dims[2]; else _depth = (unsigned)dims[2];
190 }
191 _data = new T[size()];
192 makeImageFromMatlabData(matlabArray,classID);
193 }
194 }
195
196 /*******************************************************************
197 * operator=(). Copy mxMarray data mArray into the current image
198 * Works as the previous constructor, but without the vdata stuff.
199 * don't know if it is of any use...
200 */
201 CImg & operator=(const mxArray *matlabArray) {
202 int
203 nbdims = (int)mxGetNumberOfDimensions(matlabArray),
204 classID = mxGetClassID(matlabArray);
205 if (nbdims>4 || !isNumericalClassID(classID)) {
206 delete [] _data; _data = 0;
207 _width = _height = _depth = _spectrum = 0;
208 #if cimg_debug>1
209 cimg::warn("MATLAB array is more than 4D or/and not numerical, returning an empty image.");
210 #endif
211 } else {
212 const mwSize *dims = mxGetDimensions(matlabArray);
213 _depth = _spectrum = 1;
214 _width = (unsigned)dims[1];
215 _height = (unsigned)dims[0];
216 if (nbdims>2) _depth = (unsigned)dims[2];
217 else if (nbdims>3) _spectrum = (unsigned)dims[3];
218 delete [] _data;
219 _data = new T[size()];
220 makeImageFromMatlabData(matlabArray,classID);
221 }
222 }
223
224 private:
225
226 /*****************************************************************
227 * private routines used for transfering a CImg<T> to a mxArray
228 * here also, we have to exchange the x and y dims so we get the
229 * expected MATLAB array.
230 */
populate_maltlab_array(c * const mdata)231 template <typename c> void populate_maltlab_array(c *const mdata) const {
232 cimg_forXYZC(*this,x,y,z,v) mdata[((v*_depth + z)*_width + x)*_height + y] = (c)(*this)(x,y,z,v);
233 }
234
235 /*************************************************
236 * the specialized version for "logical" entries
237 */
populate_maltlab_array(mxLogical * const mdata)238 void populate_maltlab_array(mxLogical *const mdata) const {
239 cimg_forXYZC(*this,x,y,z,v) mdata[((v*_depth + z)*_width + x)*_height + y] = (mxLogical)((*this)(x,y,z,v)!=0);
240 }
241
242 public:
243
244 /******************************************
245 * export a CImg image to a MATLAB array.
246 **/
247 mxArray *toMatlab(mxClassID classID=mxDOUBLE_CLASS, const bool squeeze=false) const {
248 if (!isNumericalClassID(classID)) {
249 #if cimg_debug>1
250 cimg::warn("Invalid MATLAB Class Id Specified.");
251 #endif
252 return 0;
253 }
254 mwSize dims[4];
255 dims[0] = (mwSize)_height;
256 dims[1] = (mwSize)_width;
257 dims[2] = (mwSize)_depth;
258 dims[3] = (mwSize)_spectrum;
259
260 if (squeeze && _depth == 1) {
261 dims[2] = (mwSize)_spectrum;
262 dims[3] = (mwSize)1;
263 }
264 mxArray *matlabArray = mxCreateNumericArray((mwSize)4,dims,classID,mxREAL);
265 if (classID==mxLOGICAL_CLASS) {
266 mxLogical *mdata = mxGetLogicals(matlabArray);
267 populate_maltlab_array(mdata);
268 } else {
269 void *mdata = mxGetPr(matlabArray);
270 switch (classID) {
271 case mxDOUBLE_CLASS : populate_maltlab_array((real64_T*)mdata); break;
272 case mxSINGLE_CLASS : populate_maltlab_array((real32_T*)mdata); break;
273 case mxINT8_CLASS : populate_maltlab_array((int8_T*)mdata); break;
274 case mxUINT8_CLASS : populate_maltlab_array((uint8_T*)mdata); break;
275 case mxINT16_CLASS : populate_maltlab_array((int16_T*)mdata); break;
276 case mxUINT16_CLASS : populate_maltlab_array((uint16_T*)mdata); break;
277 case mxINT32_CLASS : populate_maltlab_array((int32_T*)mdata); break;
278 case mxUINT32_CLASS : populate_maltlab_array((uint32_T*)mdata); break;
279 case mxINT64_CLASS : populate_maltlab_array((int64_T*)mdata); break;
280 case mxUINT64_CLASS : populate_maltlab_array((uint64_T*)mdata); break;
281 }
282 }
283 return matlabArray;
284 }
285
286 // end of matlab.h
287 #endif /* cimg_plugin_matlab */
288