MatlabWorkspace.h 15 KB

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