如何用Matplotlib将超长EEG折线图保存为高分辨率单张图片
问题描述
我需要保存一份时长超100小时、每5分钟一个数据点的EEG折线图,要求生成约10×10000px分辨率的单张图,放大后仍能清晰可读。但遇到以下问题:
- 设置
figsize=(10000,10)时直接报错,提示图片尺寸过大 - 用合理范围内的
figsize生成的图片会被压缩,根本没法用来判读EEG - 用MNE库只能在交互窗口正常显示,保存后的全EEG图还是压缩状态
我想要的效果是能清晰放大查看的长图,局部放大后能看清每一个数据点的细节。
更新尝试
我试过提高dpi并保存为SVG格式,代码如下:
def visualize_eeg(mat_path): mat = scipy.io.loadmat(mat_path) fs = mat['fs'][0][0] ch_labels = [x[0].upper() for x in mat['ch_labels'][0]] eeg_data = list(list(x) for x in mat['eeg_data']) dict_data = dict(zip(ch_labels,eeg_data)) df = pd.DataFrame(dict_data) df = df.T.sort_index().T fig = plt.figure(figsize=(50, 10)) gs = gridspec.GridSpec(len(df.columns), 1,wspace=0.0, hspace=0.0) hours = get_hours(df,fs) # plot each column (channel) in a subplot for i in tqdm(range(len(df.columns))): ch = df.columns[i] ax = plt.subplot(gs[i]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.set_yticks([0]) ax.set_yticklabels([ch]) if i == len(df.columns)-1: ax.tick_params(left = True, right = False , labelleft = True , labelbottom = True, bottom = True) ax.set_xticks(hours.index,hours) else: ax.spines['bottom'].set_visible(False) ax.tick_params(left = True, right = False , labelleft = True , labelbottom = False, bottom = False) ax.plot(df[ch].apply(lambda x : x*1000), label=ch) fig.set_dpi(1000) fig.savefig('test.svg')
但生成的图片缩放效果还是很差,局部放大后细节模糊不清。
解决方案
1. 关闭SVG自动路径简化
SVG本身是矢量格式,但Matplotlib为了减小文件体积,会自动简化大量数据点的路径,导致放大后失真。你可以在保存时强制关闭这个优化:
# 绘图代码不变,修改savefig部分 fig.savefig('eeg_full.svg', bbox_inches='tight', pad_inches=0, dpi=300, rasterized=False)
同时可以指定SVG后端,避免不必要的优化:
import matplotlib matplotlib.use('SVG') # 放在绘图代码最开头
2. 用底层SVG后端直接输出
如果默认保存还是有压缩,直接调用Matplotlib的SVG后端底层接口,强制保留所有数据点:
from matplotlib.backends.backend_svg import FigureCanvasSVG # 完成绘图后替换savefig canvas = FigureCanvasSVG(fig) canvas.print_svg('eeg_full.svg', dpi=300)
3. 分块生成后拼接(适合超大规模数据)
100小时的EEG数据点数量极大,直接生成单张图容易触发尺寸限制或自动压缩。可以按时间段分块生成子图,再用PIL拼接成完整长图:
from PIL import Image import matplotlib.pyplot as plt import gridspec from tqdm import tqdm import scipy.io import pandas as pd def visualize_eeg_chunk(chunk, ch_labels, chunk_idx, fs): df = pd.DataFrame(chunk) df = df.T.sort_index().T fig = plt.figure(figsize=(50, 10), dpi=100) gs = gridspec.GridSpec(len(df.columns), 1, wspace=0.0, hspace=0.0) hours = get_hours(df, fs) for i in tqdm(range(len(df.columns))): ch = df.columns[i] ax = plt.subplot(gs[i]) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.set_yticks([0]) ax.set_yticklabels([ch]) if i == len(df.columns)-1: ax.tick_params(left=True, right=False, labelleft=True, labelbottom=True, bottom=True) ax.set_xticks(hours.index, hours) else: ax.spines['bottom'].set_visible(False) ax.tick_params(left=True, right=False, labelleft=True, labelbottom=False, bottom=False) ax.plot(df[ch].apply(lambda x: x*1000), label=ch) fig.savefig(f'chunk_{chunk_idx}.png', bbox_inches='tight', pad_inches=0) plt.close(fig) return Image.open(f'chunk_{chunk_idx}.png') # 加载原始数据 mat_path = "your_data.mat" mat = scipy.io.loadmat(mat_path) fs = mat['fs'][0][0] ch_labels = [x[0].upper() for x in mat['ch_labels'][0]] eeg_data = list(list(x) for x in mat['eeg_data']) dict_data = dict(zip(ch_labels, eeg_data)) df = pd.DataFrame(dict_data) df = df.T.sort_index().T # 按每10小时分块(可根据需求调整) chunk_duration_hours = 10 chunk_size = chunk_duration_hours * (60 // 5) # 每5分钟1个点,1小时12个点 chunks = [df[i:i+chunk_size] for i in range(0, len(df), chunk_size)] # 生成所有子图 subfigs = [] for idx, chunk in enumerate(chunks): subfig = visualize_eeg_chunk(chunk, ch_labels, idx, fs) subfigs.append(subfig) # 拼接子图 width = subfigs[0].width total_height = sum(img.height for img in subfigs) final_img = Image.new('RGB', (width, total_height)) current_y = 0 for img in subfigs: final_img.paste(img, (0, current_y)) current_y += img.height final_img.save('eeg_full.png')
4. 禁用MNE的自动降采样
MNE在保存图片时默认会对数据降采样,你可以在绘图时指定完整时长,并关闭降采样:
import mne # 加载你的EEG数据(根据实际格式调整) raw = mne.io.read_raw_mat(mat_path, preload=True) # 设置绘图时长为总时长(秒),禁用降采样 fig = raw.plot(duration=100*3600, scalings='auto', show=False, use_opengl=False) fig.savefig('mne_eeg.svg', dpi=300, bbox_inches='tight', rasterized=False)
内容的提问来源于stack exchange,提问作者Fabio Magarelli
相关产品推荐
相关产品推荐

