写点什么

记一次 java 读取 matlab 数据方式

用户头像
Lazy
关注
发布于: 2021 年 05 月 14 日
记一次java读取matlab数据方式

背景,了解一下

目前在做一个有点科研性质的软件项目,应用程序难免要涉及到一些科学计算上相关的东西,而 matlab 是科学计算领域最常用的工具,这不,刚接到产品经理描述的需求,用户会将 matlab 产生的结果文件(.mat)上传到系统,系统解析这个结果,然后在网页上展示。虽说最近哈工大被禁用 matlab 风波一时,大家隐忧不解,但是没有好的替代品前,还是得硬着头皮继续用着(客户不是哈工大,还可以用)。 在接到这样的需求时,首先去确认了下能否实现,随手 baidu,出来不少神仙们写的 java 读取 matlab 数据的博客,内心放心了一大半,能解决就好。产品经理一听能够解决,心里美滋滋,以最快的速度进行了需求确认->产品设计->评审、并向客户要了份测试数据交了过来,于是那时那刻手里攥着一份.mat 数据,对它还一所知。

问题解决思路

接下来就得以科学严谨的态度来解决问题了,大概分这样几步:


  • 找研究算法同事(他们的电脑一般都装有 matlab)帮忙打开这个.mat 文件,看看它长什么样子(何方妖怪),结果,清秀的像个 excel 表格。



  • 先了解下 matlab,所谓磨刀不误砍柴工。有些道友可能会直接施展 CV 大法,轻松搞定。但是如果根基薄弱的话,还是不建议这样做,提前了解多一些,还是多少可以减少点后期的问题,而且也能更加从容的应对后续的变化。

  • 动手、尝试与封装

matlab 扫盲学习,简单总结

Matlab 可以说是一个计算平台,也可以说是一门高级语言,它有着自定义的数据类型,和其他语言相似,也有数值、字符、map 等。下图是在后续实现中看到的封装 matlab 数据类型类的部分截图:



比较疑惑的可能是 sparse 和 cell 。sparse 是稀疏矩阵,cell 是索引数据容器的数据类型,它是一个数组,只是数组里存放的是其他类型数据的索引。数组和矩阵是 matlab 总信息和数据的基本表示形式。


通过官网学习是才是扫盲的第一步, 从matlab语言基础知识可以了解到更多数据类型的介绍,比如 table 类型,还记的.mat 打开后清秀的样子吗,大胆猜测产品经理拿来的数据可能就和 table 有关。


扫盲方式: 先从官网学习,一知半解,然后再实践中对照印证。

动手,尝试过程记录

百度一下清一色 都是使用 ujmp-jmatio 来实.mat 文件内容的读写,

1. 尝试使用 jmatio, 结果失败了。
  • 首先在项目中引入


<dependency>  <groupId>net.sourceforge.jmatio</groupId>  <artifactId>jmatio</artifactId>  <version>1.0</version></dependency>
复制代码


  • 实在读取 mat 文件,结果报错了


MatFileReader reader = new MatFileReader("QA_table.mat");
Exception in thread "main" java.lang.ClassCastException: com.jmatio.types.MLUInt32 cannot be cast to com.jmatio.types.MLUInt8
复制代码


ujmp-jmatio 实际上也是引用了 jmatio, 再进行了一层封装,尝试使用 ujmp-jmatio 结果抛了同样的错误,怀疑是不是版本的问题,看了 mvnrepository, 只有 1.0 版本,matlab 不同版本数据定义有差异,估计我拿到的文件版本确实不兼容了。 这条路没有再深究下去。

2. 转换为 mfl,结果成功了

在 github 搜索了一番,找到项目 https://github.com/HebiRobotics/MFL, 引入项目尝试


<dependency>        <groupId>us.hebi.matlab.mat</groupId>        <artifactId>mfl-core</artifactId>        <version>0.5.6</version></dependency>
复制代码


Mat5File file = Mat5.readFromFile("QA_table.mat");System.out.println(file);
复制代码


很庆幸,没有再报错了,看看打印出来的结果:


Mat5File{description='MATLAB 5.0 MAT-file, Platform: PCWIN64, Created on: Wed Apr 01 14:52:21 2020', subsysOffset=213, byteOrder=LITTLE_ENDIAN, version=1}QA_table = 1x1 table    ndims: 2.0    nrows: 3.0    rownames: {}    nvars: 16.0    varnames: 1x16 cell    data: 1x16 cell    props: 1x1 struct        Description: ''        VariableDescriptions: {}        VariableUnits: {}        DimensionNames: 1x2 cell        UserData: []"" = 1x6600 uint8
复制代码


从结果可以看出文件有两个主体,一个是 name 为 QA_table 的 table, 另一个是 name 为空串的 unit8, 第一个应该就是要读取的主体内容(1x6600 unit8 一时没弄明白是什么含义),MFL 库中没有 table 结构相关的封装类,但是可以直接使用 getStruct 来获取。


// 使用name 或者索引都可以Struct struct = file.getStruct("QA_table");// Struct struct = file.getStruct(0); 
复制代码


假如要读取图 1 中 filename 列中的三行分别的值,先简单分析下 file 打印的信息(以下没有任何官方说明,仅为个人推测和实践):


  • ndims: 表示维度,当前为 2 维数据

  • nrow3: 数据行数,3 行,和图 1 是一致的。

  • nvars: 变量个数,等同于列数

  • varnames: 存放列名

  • data: 存放数据

  • props:表的属性


以下是具体读取信息的代码:


// 首先获取数据, data: 1x16 cellCell data = struct.getCell("data");// 读取cell中第二列 ,就是filename列Cell filenameValues = data.get(0, 1);// 打印所有filename 值for (int i=0; i<3; i++) {    System.out.println(filenameValues.get(i, 0) .toString());}
复制代码


之所以像上面这般读取,是因为给的数据结构是 116 cell 数组,数组内元素 是 31 的 cell 数组;



接来下就是封装工具,将表格转为 json 数据,提供给业务方调用,具体代码就不贴了。整个过程就是如此,感谢抽空阅读。

参考文档

1、《MatLab数据结构》BlueHeart0621

发布于: 2021 年 05 月 14 日阅读数: 13
用户头像

Lazy

关注

某知名软件上市公司基层软件开发。 2019.10.17 加入

助力于脑科学软件工程,为中国脑计划舔砖加瓦。 脑科学软件工程包含多种模态数据的处理分析(如脑影像、基因、行为评测等)、脑相关科研平台、研究成果转化。其应用可覆盖到医疗、教育、生活娱乐等各个领域。

评论

发布
暂无评论
记一次java读取matlab数据方式