MatlabWorkspace.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. #ifndef IGL_WRITE_MATLAB_WORKSPACE
  2. #define IGL_WRITE_MATLAB_WORKSPACE
  3. #include <string>
  4. #include <vector>
  5. #include <Eigen/Dense>
  6. #include <Eigen/Sparse>
  7. #include "mat.h"
  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. {
  150. }
  151. inline igl::MatlabWorkspace::~MatlabWorkspace()
  152. {
  153. // clean up data
  154. clear();
  155. }
  156. inline void igl::MatlabWorkspace::clear()
  157. {
  158. for_each(data.begin(),data.end(),&mxDestroyArray);
  159. data.clear();
  160. names.clear();
  161. }
  162. inline bool igl::MatlabWorkspace::write(const std::string & path) const
  163. {
  164. using namespace std;
  165. MATFile * mat_file = matOpen(path.c_str(), "w");
  166. assert(names.size() == data.size());
  167. // loop over names and data
  168. for(int i = 0;i < (int)names.size(); i++)
  169. {
  170. // Put variable as LOCAL variable
  171. int status = matPutVariable(mat_file,names[i].c_str(), data[i]);
  172. if(status != 0)
  173. {
  174. cerr<<"^MatlabWorkspace::save Error: matPutVariable ("<<names[i]<<
  175. ") failed"<<endl;
  176. return false;
  177. }
  178. }
  179. if(matClose(mat_file) != 0)
  180. {
  181. fprintf(stderr,"Error closing file %s\n",path.c_str());
  182. return false;
  183. }
  184. return true;
  185. }
  186. inline bool igl::MatlabWorkspace::read(const std::string & path)
  187. {
  188. using namespace std;
  189. MATFile * mat_file;
  190. mat_file = matOpen(path.c_str(), "r");
  191. if (mat_file == NULL)
  192. {
  193. cerr<<"Error: failed to open "<<path<<endl;
  194. return false;
  195. }
  196. int ndir;
  197. const char ** dir = (const char **)matGetDir(mat_file, &ndir);
  198. if (dir == NULL) {
  199. cerr<<"Error reading directory of file "<< path<<endl;
  200. return false;
  201. }
  202. mxFree(dir);
  203. // Must close and reopen
  204. if(matClose(mat_file) != 0)
  205. {
  206. cerr<<"Error: failed to close file "<<path<<endl;
  207. return false;
  208. }
  209. mat_file = matOpen(path.c_str(), "r");
  210. if (mat_file == NULL)
  211. {
  212. cerr<<"Error: failed to open "<<path<<endl;
  213. return false;
  214. }
  215. /* Read in each array. */
  216. for (int i=0; i<ndir; i++)
  217. {
  218. const char * name;
  219. mxArray * mx_data = matGetNextVariable(mat_file, &name);
  220. if (mx_data == NULL)
  221. {
  222. cerr<<"Error: matGetNextVariable failed in "<<path<<endl;
  223. return false;
  224. }
  225. const int dims = mxGetNumberOfDimensions(mx_data);
  226. assert(dims == 2);
  227. if(dims != 2)
  228. {
  229. fprintf(stderr,"Variable '%s' has %d ≠ 2 dimensions. Skipping\n",
  230. name,dims);
  231. mxDestroyArray(mx_data);
  232. continue;
  233. }
  234. // don't destroy
  235. names.push_back(name);
  236. data.push_back(mx_data);
  237. }
  238. if(matClose(mat_file) != 0)
  239. {
  240. cerr<<"Error: failed to close file "<<path<<endl;
  241. return false;
  242. }
  243. return true;
  244. }
  245. // Treat everything as a double
  246. template <typename DerivedM>
  247. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  248. const Eigen::PlainObjectBase<DerivedM>& M,
  249. const std::string & name)
  250. {
  251. using namespace std;
  252. const int m = M.rows();
  253. const int n = M.cols();
  254. mxArray * mx_data = mxCreateDoubleMatrix(m,n,mxREAL);
  255. data.push_back(mx_data);
  256. names.push_back(name);
  257. // Copy data immediately
  258. // Q: Won't this be incorrect for integers?
  259. copy(M.data(),M.data()+M.size(),mxGetPr(mx_data));
  260. return *this;
  261. }
  262. // Treat everything as a double
  263. template <typename MT>
  264. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  265. const Eigen::SparseMatrix<MT>& M,
  266. const std::string & name)
  267. {
  268. using namespace std;
  269. const int m = M.rows();
  270. const int n = M.cols();
  271. // THIS WILL NOT WORK FOR ROW-MAJOR
  272. assert(n==M.outerSize());
  273. const int nzmax = M.nonZeros();
  274. mxArray * mx_data = mxCreateSparse(m, n, nzmax, mxREAL);
  275. data.push_back(mx_data);
  276. names.push_back(name);
  277. // Copy data immediately
  278. double * pr = mxGetPr(mx_data);
  279. mwIndex * ir = mxGetIr(mx_data);
  280. mwIndex * jc = mxGetJc(mx_data);
  281. // Iterate over outside
  282. int k = 0;
  283. for(int j=0; j<M.outerSize();j++)
  284. {
  285. jc[j] = k;
  286. // Iterate over inside
  287. for(typename Eigen::SparseMatrix<MT>::InnerIterator it (M,j); it; ++it)
  288. {
  289. pr[k] = it.value();
  290. ir[k] = it.row();
  291. k++;
  292. }
  293. }
  294. jc[M.outerSize()] = k;
  295. return *this;
  296. }
  297. template <typename ScalarM>
  298. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  299. const std::vector<std::vector<ScalarM> > & vM,
  300. const std::string & name)
  301. {
  302. Eigen::MatrixXd M;
  303. list_to_matrix(vM,M);
  304. return this->save(M,name);
  305. }
  306. template <typename ScalarV>
  307. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  308. const std::vector<ScalarV> & vV,
  309. const std::string & name)
  310. {
  311. Eigen::MatrixXd V;
  312. list_to_matrix(vV,V);
  313. return this->save(V,name);
  314. }
  315. template <typename Q>
  316. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  317. const Eigen::Quaternion<Q> & q,
  318. const std::string & name)
  319. {
  320. Eigen::Matrix<Q,1,4> qm;
  321. qm(0,0) = q.w();
  322. qm(0,1) = q.x();
  323. qm(0,2) = q.y();
  324. qm(0,3) = q.z();
  325. return save(qm,name);
  326. }
  327. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save(
  328. const double d,
  329. const std::string & name)
  330. {
  331. Eigen::VectorXd v(1);
  332. v(0) = d;
  333. return save(v,name);
  334. }
  335. template <typename DerivedM>
  336. inline igl::MatlabWorkspace&
  337. igl::MatlabWorkspace::save_index(
  338. const Eigen::PlainObjectBase<DerivedM>& M,
  339. const std::string & name)
  340. {
  341. DerivedM Mp1 = M;
  342. Mp1.array() += 1;
  343. return this->save(Mp1,name);
  344. }
  345. template <typename ScalarM>
  346. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save_index(
  347. const std::vector<std::vector<ScalarM> > & vM,
  348. const std::string & name)
  349. {
  350. Eigen::MatrixXd M;
  351. list_to_matrix(vM,M);
  352. return this->save_index(M,name);
  353. }
  354. template <typename ScalarV>
  355. inline igl::MatlabWorkspace& igl::MatlabWorkspace::save_index(
  356. const std::vector<ScalarV> & vV,
  357. const std::string & name)
  358. {
  359. Eigen::MatrixXd V;
  360. list_to_matrix(vV,V);
  361. return this->save_index(V,name);
  362. }
  363. template <typename DerivedM>
  364. inline bool igl::MatlabWorkspace::find(
  365. const std::string & name,
  366. Eigen::PlainObjectBase<DerivedM>& M)
  367. {
  368. using namespace std;
  369. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  370. if(i>=(int)names.size())
  371. {
  372. return false;
  373. }
  374. assert(i<=(int)data.size());
  375. mxArray * mx_data = data[i];
  376. assert(!mxIsSparse(mx_data));
  377. assert(mxGetNumberOfDimensions(mx_data) == 2);
  378. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  379. const int m = mxGetM(mx_data);
  380. const int n = mxGetN(mx_data);
  381. // Handle vectors
  382. if(DerivedM::IsVectorAtCompileTime)
  383. {
  384. assert(m==1 || n==1 || (m==0 && n==0));
  385. M.resize(m*n);
  386. }else
  387. {
  388. M.resize(m,n);
  389. }
  390. copy(
  391. mxGetPr(mx_data),
  392. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  393. M.data());
  394. return true;
  395. }
  396. template <typename MT>
  397. inline bool igl::MatlabWorkspace::find(
  398. const std::string & name,
  399. Eigen::SparseMatrix<MT>& M)
  400. {
  401. using namespace std;
  402. using namespace Eigen;
  403. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  404. if(i>=(int)names.size())
  405. {
  406. return false;
  407. }
  408. assert(i<=(int)data.size());
  409. mxArray * mx_data = data[i];
  410. // Handle boring case where matrix is actually an empty dense matrix
  411. if(mxGetNumberOfElements(mx_data) == 0)
  412. {
  413. M.resize(0,0);
  414. return true;
  415. }
  416. assert(mxIsSparse(mx_data));
  417. assert(mxGetNumberOfDimensions(mx_data) == 2);
  418. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  419. const int m = mxGetM(mx_data);
  420. const int n = mxGetN(mx_data);
  421. // Copy data immediately
  422. double * pr = mxGetPr(mx_data);
  423. mwIndex * ir = mxGetIr(mx_data);
  424. mwIndex * jc = mxGetJc(mx_data);
  425. vector<Triplet<MT> > MIJV;
  426. MIJV.reserve(mxGetNumberOfElements(mx_data));
  427. // Iterate over outside
  428. int k = 0;
  429. for(int j=0; j<n;j++)
  430. {
  431. // Iterate over inside
  432. while(k<(int)jc[j+1])
  433. {
  434. //cout<<ir[k]<<" "<<j<<" "<<pr[k]<<endl;
  435. assert((int)ir[k]<m);
  436. assert((int)j<n);
  437. MIJV.push_back(Triplet<MT >(ir[k],j,pr[k]));
  438. k++;
  439. }
  440. }
  441. M.resize(m,n);
  442. M.setFromTriplets(MIJV.begin(),MIJV.end());
  443. return true;
  444. }
  445. inline bool igl::MatlabWorkspace::find(
  446. const std::string & name,
  447. int & v)
  448. {
  449. using namespace std;
  450. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  451. if(i>=(int)names.size())
  452. {
  453. return false;
  454. }
  455. assert(i<=(int)data.size());
  456. mxArray * mx_data = data[i];
  457. assert(!mxIsSparse(mx_data));
  458. assert(mxGetNumberOfDimensions(mx_data) == 2);
  459. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  460. assert(mxGetNumberOfElements(mx_data) == 1);
  461. copy(
  462. mxGetPr(mx_data),
  463. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  464. &v);
  465. return true;
  466. }
  467. inline bool igl::MatlabWorkspace::find(
  468. const std::string & name,
  469. double & d)
  470. {
  471. using namespace std;
  472. const int i = std::find(names.begin(), names.end(), name)-names.begin();
  473. if(i>=(int)names.size())
  474. {
  475. return false;
  476. }
  477. assert(i<=(int)data.size());
  478. mxArray * mx_data = data[i];
  479. assert(!mxIsSparse(mx_data));
  480. assert(mxGetNumberOfDimensions(mx_data) == 2);
  481. //cout<<name<<": "<<mxGetM(mx_data)<<" "<<mxGetN(mx_data)<<endl;
  482. assert(mxGetNumberOfElements(mx_data) == 1);
  483. copy(
  484. mxGetPr(mx_data),
  485. mxGetPr(mx_data)+mxGetNumberOfElements(mx_data),
  486. &d);
  487. return true;
  488. }
  489. template <typename DerivedM>
  490. inline bool igl::MatlabWorkspace::find_index(
  491. const std::string & name,
  492. Eigen::PlainObjectBase<DerivedM>& M)
  493. {
  494. if(!find(name,M))
  495. {
  496. return false;
  497. }
  498. M.array() -= 1;
  499. return true;
  500. }
  501. //template <typename Data>
  502. //bool igl::MatlabWorkspace::save(const Data & M, const std::string & name)
  503. //{
  504. // using namespace std;
  505. // // If I don't know the type then I can't save it
  506. // cerr<<"^MatlabWorkspace::save Error: Unknown data type. "<<
  507. // name<<" not saved."<<endl;
  508. // return false;
  509. //}
  510. #endif