如果钙成像成像一次成像几千上万个细胞,一次成像上万帧,Tiff文件会特别大,就算提取钙信号得到的矩阵也会很大,
如果自己的电脑内存小,就很难跑得动代码。
为了解决这个问题,可以使用matlab的memmapfile函数,它可以将一个文件的内容映射到 MATLAB 的虚拟内存地址空间中,而不是将整个文件一次性读入物理内存(RAM)。这样做可以让你像访问内存中的数组一样访问文件的内容,特别适用于处理那些远大于可用 RAM 的大型文件。
这篇笔记介绍了memmapfile函数的基本用法,还用一个例子介绍如何用memmapfile保存和读取几十GB的钙信号二维数据
memmapfile介绍
memmapfile
它允许你将一个文件(通常是二进制文件)的内容**映射到 MATLAB 的虚拟内存地址空间**中,而不是将整个文件一次性读入物理内存(RAM)。这样做可以让你像访问内存中的数组一样访问文件的内容,特别适用于处理那些远大于可用 RAM 的大型文件。
memmapfile
的主要优点:
-
处理超大文件: 可以访问远超物理内存大小的文件,因为只有实际被访问的部分才会被加载到内存。
-
高效的访问: 对于需要频繁读取或修改文件中不同位置小块数据的场景,内存映射通常比反复使用
fseek
和fread
/fwrite
更高效,因为它避免了重复的文件 I/O 开销和系统调用开销。 -
代码简洁性: 一旦映射创建成功,你可以像操作普通 MATLAB 数组一样访问文件数据(通过
memmapfile
对象的Data
属性),代码更直观。 -
潜在的共享内存: 不同的 MATLAB 进程或甚至其他支持内存映射的程序可以映射同一个文件,从而实现一种形式的进程间数据共享(需要注意同步问题)。
**memmapfile
**如何使用
memmapfile
的基本语法:
1 | m = memmapfile(filename, Name, Value, ...) |
filename
: 你要映射的文件的完整路径和名称(字符串)。文件必须存在。Name, Value
pairs: 一系列可选的参数,用于控制映射的行为。
关键的可选参数 (Name-Value Pairs):
-
Writable
: (逻辑值true
或false
)false
(默认): 只读映射。你只能读取文件数据,不能修改。尝试写入会报错。true
: 可写映射。你可以读取和修改文件数据。修改会最终写回磁盘文件。注意: 可写映射通常会对文件进行独占锁定,其他程序可能无法同时写入该文件。
-
Offset
: (非负整数)- 指定从文件开头算起的字节偏移量,映射从这里开始。默认是
0
(文件开头)。 - 这允许你只映射文件的一部分,比如如果文件会在开头放置一些基础的metadata数据,就可以通过Offset跳过文件头的metadata,获取真正的数据。
- 指定从文件开头算起的字节偏移量,映射从这里开始。默认是
-
Format
: (字符向量或元胞数组)-
极其重要的参数!它定义了如何解释文件中的原始字节数据。
-
简单形式 (字符向量): 指定一个 MATLAB 的基本数据类型,例如:
'uint8'
,'int8'
,'uint16'
,'int16'
,'uint32'
,'int32'
,'uint64'
,'int64'
(整数类型)'single'
(单精度浮点数, 对应 C 的float
)'double'
(双精度浮点数, 对应 C 的double
)'logical'
(逻辑值, 通常是 1 字节)'char'
(字符, 通常是 1 或 2 字节,取决于系统编码)
整个映射区域的数据都会被解释为这种类型的连续数组。
例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21myData = (1:10);
fileID = fopen('records.dat','w');
fwrite(fileID,myData,'double');
fclose(fileID);
m = memmapfile('records.dat','Format','double') ;
m.Data
ans =
1
2
3
4
5
6
7
8
9
10 -
多维矩阵 : 每个多维矩阵元素定义结构体的一个字段:
1
{'DataType', [Dimensions], 'FieldName'}
-
DataType
: 该字段的数据类型 (同上)。 -
Dimensions
: 该字段的维度 (一个行向量,例如[1 1]
表示标量,[1 10]
表示 1x10 的数组)。 -
FieldName
: 该字段在memmapfile
对象的Data
结构体中的名称。 -
例如,把文件里存放到从1到20的数组,解析为4×5的二维矩阵
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16myData = (1:20);
fileID = fopen('records.dat','w');
fwrite(fileID,myData,'double');
fclose(fileID);
m = memmapfile('records.dat','Format',{'double',[4,5],'value'}) ;
m.Data.value
ans =
1 5 9 13 17
2 6 10 14 18
3 7 11 15 19
4 8 12 16 20 -
例如,如果文件包含一系列记录,每个记录包含一个 double 和一个长度为10的 int16 数组:
1
2
3
4format = {'double', [1 1], 'value'; 'int16', [1 10], 'samples'};
m = memmapfile('mydata.bin', 'Format', format);
% 访问第一个记录的 value 字段: m.Data(1).value
% 访问第三个记录的 samples 字段: m.Data(3).samples
-
-
-
Repeat
: (正整数 或inf
)- 指定
Format
定义的数据结构在文件中重复多少次。 - 默认是
inf
,表示从Offset
开始,一直映射到文件末尾,有多少个完整的Format
结构就映射多少个。 - 如果你知道确切的记录数量,可以指定一个具体的整数。
- 指定
memmapfile的使用注意事项
-
如何关闭memmapfile对象?
- 方法1:给
memmapfile
重新赋一个值 - 方法2:用clear命令清除
memmapfile
变量 - 方法3:在函数使用,函数使用完毕,会自动删除
memmapfile
对象 - 注意:如果使用了
d = m.Data
, 把memmapfile对象的Data字段赋值给一个变量时,MATLAB会创建一个共享数据副本,这很高效,因为实际上没有内存被复制。如果先清除m对象,文件和相关变量之间的数据共享就会断开,如果后面还要使用相关数据,必须在清除对象前把这些变量的数据复制到内存里。
- 方法1:给
-
不能直接
memmapfile('mydata.bin').Data
,这样相当于读取全部数据会非常卡,可以1
2m = memmapfile('mydata.bin');
m.Data; % 这样就不会卡 -
使用memmapfile如何修改数据
- 如果使用memmapfile要直接修改文件的数据,不要用
data = m.Data
,然后用data进行修改数据data.x=ones(300,8,'uint16')
,这个时候data只是一个副本,修改数据不会影响到原数据,需要m.Data.x = ones(300,8,'uint16')
这样才能正确修改
- 如果使用memmapfile要直接修改文件的数据,不要用
示例:保存和读取几十GB的钙信号二维数据
假设一个钙信号数据,有10000个神经元,每个神经元有1000000帧的数据,我的电脑是无法直接在内存生成10000×1000000的矩阵的

我们可以使用fwrite,一行一行写入,保存数据为文件
1 | % 定义元数据 |
生成的文件有74.5GB大小

之后使用memmapfile来读取这个文件,就可以想读哪个ROI的数据,就读哪个ROI的数据了
1 | % 文件名 |
注意,这里读取的矩阵格式格式是nframes×nROI,这是因为写入二进制的时候,就是
nROI
(int32)nFrames
(int32)- ROI 1 的所有帧数据 (double * nFrames)
- ROI 2 的所有帧 数据(double * nFrames)
MATLAB 默认就是按列读取和存储矩阵的,所以读取的时候,就成了nframes×nROI
也可以改为memapfile读取的format只有帧这个单一维度,同时设置repeat=nROI数目
1 | signalDataFormat = {'double', [nFrames], 'SignalData'}; |
这样的话,读取单个ROI的数据代码就是下面这样:
1 | mmf.Data(roiIdx).SignalData |