MatlabWorkspace.h 14 KB

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