MatlabWorkspace.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #ifndef IGL_WRITE_MATLAB_WORKSPACE
  9. #define IGL_WRITE_MATLAB_WORKSPACE
  10. #include <Eigen/Dense>
  11. #include <Eigen/Sparse>
  12. #include <mat.h>
  13. #include <string>
  14. #include <vector>
  15. namespace igl
  16. {
  17. // Class which contains data of a matlab workspace which can be written to a
  18. // .mat file and loaded from matlab
  19. //
  20. // This depends on matlab at compile time (though it shouldn't necessarily
  21. // have to) but it does not depend on running the matlab engine at run-time.
  22. //
  23. // Known bugs: Treats all matrices as doubles (this may actually be desired
  24. // for some "index" matrices since matlab's sparse command takes doubles
  25. // rather than int class matrices). It is of course not desired when dealing
  26. // with logicals or uint's for images.
  27. class MatlabWorkspace
  28. {
  29. private:
  30. // KNOWN BUG: Why not use a map? Any reason to allow duplicate names?
  31. //
  32. // List of names
  33. std::vector<std::string> names;
  34. // List of data pointers
  35. std::vector<mxArray*> data;
  36. public:
  37. MatlabWorkspace();
  38. ~MatlabWorkspace();
  39. // Clear names and data of variables in workspace
  40. inline void clear();
  41. // Save current list of variables
  42. //
  43. // Inputs:
  44. // path path to .mat file
  45. // Returns true on success, false on failure
  46. inline bool write(const std::string & path) const;
  47. // Load list of variables from .mat file
  48. //
  49. // Inputs:
  50. // path path to .mat file
  51. // Returns true on success, false on failure
  52. inline bool read(const std::string & path);
  53. // Assign data to a variable name in the workspace
  54. //
  55. // Template:
  56. // DerivedM eigen matrix (e.g. MatrixXd)
  57. // Inputs:
  58. // M data (usually a matrix)
  59. // name variable name to save into work space
  60. // Returns true on success, false on failure
  61. //
  62. // Known Bugs: Assumes Eigen is using column major ordering
  63. template <typename DerivedM>
  64. inline MatlabWorkspace& save(
  65. const Eigen::PlainObjectBase<DerivedM>& M,
  66. const std::string & name);
  67. // Template:
  68. // MT sparse matrix type (e.g. double)
  69. template <typename MT>
  70. inline MatlabWorkspace& save(
  71. const Eigen::SparseMatrix<MT>& M,
  72. const std::string & name);
  73. // Templates:
  74. // ScalarM scalar type, e.g. double
  75. template <typename ScalarM>
  76. inline MatlabWorkspace& save(
  77. const std::vector<std::vector<ScalarM> > & vM,
  78. const std::string & name);
  79. // Templates:
  80. // ScalarV scalar type, e.g. double
  81. template <typename ScalarV>
  82. inline MatlabWorkspace& save(
  83. const std::vector<ScalarV> & vV,
  84. const std::string & name);
  85. // NOTE: Eigen stores quaternions coefficients as (i,j,k,1), but most of
  86. // our matlab code stores them as (1,i,j,k) This takes a quaternion and
  87. // saves it as a (1,i,j,k) row vector
  88. //
  89. // Templates:
  90. // Q quaternion type
  91. template <typename Q>
  92. inline MatlabWorkspace& save(
  93. const Eigen::Quaternion<Q> & q,
  94. const std::string & name);
  95. inline MatlabWorkspace& save(
  96. const double d,
  97. const std::string & name);
  98. // Same as save() but adds 1 to each element, useful for saving "index"
  99. // matrices like lists of faces or elements
  100. template <typename DerivedM>
  101. inline MatlabWorkspace& save_index(
  102. const Eigen::PlainObjectBase<DerivedM>& M,
  103. const std::string & name);
  104. template <typename ScalarM>
  105. inline MatlabWorkspace& save_index(
  106. const std::vector<std::vector<ScalarM> > & vM,
  107. const std::string & name);
  108. template <typename ScalarV>
  109. inline MatlabWorkspace& save_index(
  110. const std::vector<ScalarV> & vV,
  111. const std::string & name);
  112. // Find a certain matrix by name.
  113. //
  114. // KNOWN BUG: Outputs the first found (not necessarily unique lists).
  115. //
  116. // Template:
  117. // DerivedM eigen matrix (e.g. MatrixXd)
  118. // Inputs:
  119. // name exact name of matrix as string
  120. // Outputs:
  121. // M matrix
  122. // Returns true only if found.
  123. template <typename DerivedM>
  124. inline bool find(
  125. const std::string & name,
  126. Eigen::PlainObjectBase<DerivedM>& M);
  127. template <typename MT>
  128. inline bool find(
  129. const std::string & name,
  130. Eigen::SparseMatrix<MT>& M);
  131. inline bool find(
  132. const std::string & name,
  133. double & d);
  134. inline bool find(
  135. const std::string & name,
  136. int & v);
  137. // Subtracts 1 from all entries
  138. template <typename DerivedM>
  139. inline bool find_index(
  140. const std::string & name,
  141. Eigen::PlainObjectBase<DerivedM>& M);
  142. };
  143. }
  144. // Implementation
  145. // Be sure that this is not compiled into libigl.a
  146. // http://stackoverflow.com/a/3318993/148668
  147. // IGL
  148. #include "igl/list_to_matrix.h"
  149. // MATLAB
  150. #include "mat.h"
  151. // STL
  152. #include <iostream>
  153. #include <algorithm>
  154. #include <vector>
  155. inline igl::MatlabWorkspace::MatlabWorkspace():
  156. names(),
  157. data()
  158. {
  159. }
  160. inline igl::MatlabWorkspace::~MatlabWorkspace()
  161. {
  162. // clean up data
  163. clear();
  164. }
  165. inline void igl::MatlabWorkspace::clear()
  166. {
  167. for_each(data.begin(),data.end(),&mxDestroyArray);
  168. data.clear();
  169. names.clear();
  170. }
  171. inline bool igl::MatlabWorkspace::write(const std::string & path) const
  172. {
  173. using namespace std;
  174. MATFile * mat_file = matOpen(path.c_str(), "w");
  175. assert(names.size() == data.size());
  176. // loop over names and data
  177. for(int i = 0;i < (int)names.size(); i++)
  178. {
  179. // Put variable as LOCAL variable
  180. int status = matPutVariable(mat_file,names[i].c_str(), data[i]);
  181. if(status != 0)
  182. {
  183. cerr<<"^MatlabWorkspace::save Error: matPutVariable ("<<names[i]<<
  184. ") failed"<<endl;
  185. return false;
  186. }
  187. }
  188. if(matClose(mat_file) != 0)
  189. {
  190. fprintf(stderr,"Error closing file %s\n",path.c_str());
  191. return false;
  192. }
  193. return true;
  194. }
  195. inline bool igl::MatlabWorkspace::read(const std::string & path)
  196. {
  197. using namespace std;
  198. MATFile * mat_file;
  199. mat_file = matOpen(path.c_str(), "r");
  200. if (mat_file == NULL)
  201. {
  202. cerr<<"Error: failed to open "<<path<<endl;
  203. return false;
  204. }
  205. int ndir;
  206. const char ** dir = (const char **)matGetDir(mat_file, &ndir);
  207. if (dir == NULL) {
  208. cerr<<"Error reading directory of file "<< path<<endl;
  209. return false;
  210. }
  211. mxFree(dir);
  212. // Must close and reopen
  213. if(matClose(mat_file) != 0)
  214. {
  215. cerr<<"Error: failed to close file "<<path<<endl;
  216. return false;
  217. }
  218. mat_file = matOpen(path.c_str(), "r");
  219. if (mat_file == NULL)
  220. {
  221. cerr<<"Error: failed to open "<<path<<endl;
  222. return false;
  223. }
  224. /* Read in each array. */
  225. for (int i=0; i<ndir; i++)
  226. {
  227. const char * name;
  228. mxArray * mx_data = matGetNextVariable(mat_file, &name);
  229. if (mx_data == NULL)
  230. {
  231. cerr<<"Error: matGetNextVariable failed in "<<path<<endl;
  232. return false;
  233. }
  234. const int dims = mxGetNumberOfDimensions(mx_data);
  235. assert(dims == 2);
  236. if(dims != 2)
  237. {
  238. fprintf(stderr,"Variable '%s' has %d ≠ 2 dimensions. Skipping\n",
  239. name,dims);
  240. mxDestroyArray(mx_data);
  241. continue;
  242. }
  243. // don't destroy
  244. names.push_back(name);
  245. data.push_back(mx_data);
  246. }
  247. if(matClose(mat_file) != 0)
  248. {
  249. cerr<<"Error: failed to close file "<<path<<endl;
  250. return false;
  251. }
  252. return true;
  253. }
  254. // Treat everything as a double
  255. template <typename DerivedM>
  256. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  257. const Eigen::PlainObjectBase<DerivedM>& M,
  258. const std::string & name)
  259. {
  260. using namespace std;
  261. const int m = M.rows();
  262. const int n = M.cols();
  263. mxArray * mx_data = mxCreateDoubleMatrix(m,n,mxREAL);
  264. data.push_back(mx_data);
  265. names.push_back(name);
  266. // Copy data immediately
  267. // Q: Won't this be incorrect for integers?
  268. copy(M.data(),M.data()+M.size(),mxGetPr(mx_data));
  269. return *this;
  270. }
  271. // Treat everything as a double
  272. template <typename MT>
  273. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  274. const Eigen::SparseMatrix<MT>& M,
  275. const std::string & name)
  276. {
  277. using namespace std;
  278. const int m = M.rows();
  279. const int n = M.cols();
  280. // THIS WILL NOT WORK FOR ROW-MAJOR
  281. assert(n==M.outerSize());
  282. const int nzmax = M.nonZeros();
  283. mxArray * mx_data = mxCreateSparse(m, n, nzmax, mxREAL);
  284. data.push_back(mx_data);
  285. names.push_back(name);
  286. // Copy data immediately
  287. double * pr = mxGetPr(mx_data);
  288. mwIndex * ir = mxGetIr(mx_data);
  289. mwIndex * jc = mxGetJc(mx_data);
  290. // Iterate over outside
  291. int k = 0;
  292. for(int j=0; j<M.outerSize();j++)
  293. {
  294. jc[j] = k;
  295. // Iterate over inside
  296. for(typename Eigen::SparseMatrix<MT>::InnerIterator it (M,j); it; ++it)
  297. {
  298. pr[k] = it.value();
  299. ir[k] = it.row();
  300. k++;
  301. }
  302. }
  303. jc[M.outerSize()] = k;
  304. return *this;
  305. }
  306. template <typename ScalarM>
  307. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  308. const std::vector<std::vector<ScalarM> > & vM,
  309. const std::string & name)
  310. {
  311. Eigen::MatrixXd M;
  312. list_to_matrix(vM,M);
  313. return this->save(M,name);
  314. }
  315. template <typename ScalarV>
  316. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  317. const std::vector<ScalarV> & vV,
  318. const std::string & name)
  319. {
  320. Eigen::MatrixXd V;
  321. list_to_matrix(vV,V);
  322. return this->save(V,name);
  323. }
  324. template <typename Q>
  325. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  326. const Eigen::Quaternion<Q> & q,
  327. const std::string & name)
  328. {
  329. Eigen::Matrix<Q,1,4> qm;
  330. qm(0,0) = q.w();
  331. qm(0,1) = q.x();
  332. qm(0,2) = q.y();
  333. qm(0,3) = q.z();
  334. return save(qm,name);
  335. }
  336. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  337. const double d,
  338. const std::string & name)
  339. {
  340. Eigen::VectorXd v(1);
  341. v(0) = d;
  342. return save(v,name);
  343. }
  344. template <typename DerivedM>
  345. inline igl::MatlabWorkspace&
  346. igl::MatlabWorkspace::save_index(
  347. const Eigen::PlainObjectBase<DerivedM>& M,
  348. const std::string & name)
  349. {
  350. DerivedM Mp1 = M;
  351. Mp1.array() += 1;
  352. return this->save(Mp1,name);
  353. }
  354. template <typename ScalarM>
  355. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save_index(
  356. const std::vector<std::vector<ScalarM> > & vM,
  357. const std::string & name)
  358. {
  359. Eigen::MatrixXd M;
  360. list_to_matrix(vM,M);
  361. return this->save_index(M,name);
  362. }
  363. template <typename ScalarV>
  364. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save_index(
  365. const std::vector<ScalarV> & vV,
  366. const std::string & name)
  367. {
  368. Eigen::MatrixXd V;
  369. list_to_matrix(vV,V);
  370. return this->save_index(V,name);
  371. }
  372. template <typename DerivedM>
  373. inline bool igl::MatlabWorkspace::find(
  374. const std::string & name,
  375. Eigen::PlainObjectBase<DerivedM>& M)
  376. {
  377. using namespace std;
  378. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  379. if(i>=(int)names.size())
  380. {
  381. return false;
  382. }
  383. assert(i<=(int)data.size());
  384. mxArray * mx_data = data[i];
  385. assert(!mxIsSparse(mx_data));
  386. assert(mxGetNumberOfDimensions(mx_data) == 2);
  387. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  388. const int m = mxGetM(mx_data);
  389. const int n = mxGetN(mx_data);
  390. // Handle vectors
  391. if(DerivedM::IsVectorAtCompileTime)
  392. {
  393. assert(m==1 || n==1 || (m==0 && n==0));
  394. M.resize(m*n,1);
  395. }else
  396. {
  397. M.resize(m,n);
  398. }
  399. copy(
  400. mxGetPr(mx_data),
  401. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  402. M.data());
  403. return true;
  404. }
  405. template <typename MT>
  406. inline bool igl::MatlabWorkspace::find(
  407. const std::string & name,
  408. Eigen::SparseMatrix<MT>& M)
  409. {
  410. using namespace std;
  411. using namespace Eigen;
  412. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  413. if(i>=(int)names.size())
  414. {
  415. return false;
  416. }
  417. assert(i<=(int)data.size());
  418. mxArray * mx_data = data[i];
  419. // Handle boring case where matrix is actually an empty dense matrix
  420. if(mxGetNumberOfElements(mx_data) == 0)
  421. {
  422. M.resize(0,0);
  423. return true;
  424. }
  425. assert(mxIsSparse(mx_data));
  426. assert(mxGetNumberOfDimensions(mx_data) == 2);
  427. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  428. const int m = mxGetM(mx_data);
  429. const int n = mxGetN(mx_data);
  430. // Copy data immediately
  431. double * pr = mxGetPr(mx_data);
  432. mwIndex * ir = mxGetIr(mx_data);
  433. mwIndex * jc = mxGetJc(mx_data);
  434. vector<Triplet<MT> > MIJV;
  435. MIJV.reserve(mxGetNumberOfElements(mx_data));
  436. // Iterate over outside
  437. int k = 0;
  438. for(int j=0; j<n;j++)
  439. {
  440. // Iterate over inside
  441. while(k<(int)jc[j+1])
  442. {
  443. //cout<<ir[k]<<" "<<j<<" "<<pr[k]<<endl;
  444. assert((int)ir[k]<m);
  445. assert((int)j<n);
  446. MIJV.push_back(Triplet<MT >(ir[k],j,pr[k]));
  447. k++;
  448. }
  449. }
  450. M.resize(m,n);
  451. M.setFromTriplets(MIJV.begin(),MIJV.end());
  452. return true;
  453. }
  454. inline bool igl::MatlabWorkspace::find(
  455. const std::string & name,
  456. int & v)
  457. {
  458. using namespace std;
  459. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  460. if(i>=(int)names.size())
  461. {
  462. return false;
  463. }
  464. assert(i<=(int)data.size());
  465. mxArray * mx_data = data[i];
  466. assert(!mxIsSparse(mx_data));
  467. assert(mxGetNumberOfDimensions(mx_data) == 2);
  468. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  469. assert(mxGetNumberOfElements(mx_data) == 1);
  470. copy(
  471. mxGetPr(mx_data),
  472. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  473. &v);
  474. return true;
  475. }
  476. inline bool igl::MatlabWorkspace::find(
  477. const std::string & name,
  478. double & d)
  479. {
  480. using namespace std;
  481. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  482. if(i>=(int)names.size())
  483. {
  484. return false;
  485. }
  486. assert(i<=(int)data.size());
  487. mxArray * mx_data = data[i];
  488. assert(!mxIsSparse(mx_data));
  489. assert(mxGetNumberOfDimensions(mx_data) == 2);
  490. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  491. assert(mxGetNumberOfElements(mx_data) == 1);
  492. copy(
  493. mxGetPr(mx_data),
  494. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  495. &d);
  496. return true;
  497. }
  498. template <typename DerivedM>
  499. inline bool igl::MatlabWorkspace::find_index(
  500. const std::string & name,
  501. Eigen::PlainObjectBase<DerivedM>& M)
  502. {
  503. if(!find(name,M))
  504. {
  505. return false;
  506. }
  507. M.array() -= 1;
  508. return true;
  509. }
  510. //template <typename Data>
  511. //bool igl::MatlabWorkspace::save(const Data & M, const std::string & name)
  512. //{
  513. // using namespace std;
  514. // // If I don't know the type then I can't save it
  515. // cerr<<"^MatlabWorkspace::save Error: Unknown data type. "<<
  516. // name<<" not saved."<<endl;
  517. // return false;
  518. //}
  519. #endif