Matplotlib imshow绘制子图导出UV时像素尺寸不统一、缩放不一致问题
统一像素尺度对齐的Matplotlib实现方案
核心问题成因
之前使用均等分1行6列的plt.subplot()布局时,会给每个子图分配完全相等的轴占位区域,完全不匹配每个图像的原始分辨率宽高比,自然会出现拉伸变形、非正方形像素、视觉尺寸不匹配的问题;手动调整aspect、extent、figsize的方式只是凑比例,无法做到跨子图的单个像素物理尺度1:1对齐。
实现步骤
- 全局定义统一的单像素物理尺度,保证所有子图的单个像素对应相同的物理长度,从根源上避免像素拉伸
- 放弃均等分的自动subplot布局,使用
fig.add_axes()手动定义每个子图的轴位置、宽高参数,严格匹配每个图像的原始分辨率对应的物理尺寸,垂直方向对短高度的图像做居中对齐 - 所有
imshow调用统一设置aspect='equal',强制轴范围匹配图像真实宽高比,关闭所有坐标轴
可直接复用的代码
import matplotlib.pyplot as plt # 定义全局参数:单像素对应物理尺寸(英寸) s = 0.1 # 各图像原始分辨率对应的物理宽高(英寸) w1, h1 = 160 * s, 100 * s # front、back 160x100 w2, h2 = 100 * s, 45 * s # left、right 100x45 w3, h3 = 160 * s, 45 * s # top、bottom 160x45 gap = 0.2 # 子图水平间距(英寸) # 计算总画布尺寸 total_w = w1*2 + w2*2 + w3*2 + gap*5 total_h = h1 fig = plt.figure(figsize=(total_w, total_h), dpi=100) # 手动定义每个子图的归一化位置 [left, bottom, width, height] ax1 = fig.add_axes([0, 0, w1/total_w, h1/total_h]) # front ax2 = fig.add_axes([(w1 + gap)/total_w, 0, w1/total_w, h1/total_h]) # back ax3 = fig.add_axes([(w1*2 + gap*2)/total_w, (total_h - h2)/(2*total_h), w2/total_w, h2/total_h]) # left ax4 = fig.add_axes([(w1*2 + w2 + gap*3)/total_w, (total_h - h2)/(2*total_h), w2/total_w, h2/total_h]) # right ax5 = fig.add_axes([(w1*2 + w2*2 + gap*4)/total_w, (total_h - h2)/(2*total_h), w3/total_w, h2/total_h]) # top ax6 = fig.add_axes([(w1*2 + w2*2 + w3 + gap*5)/total_w, (total_h - h2)/(2*total_h), w3/total_w, h2/total_h]) # bottom # 绘制图像,统一设置等比例无拉伸 for ax, img in zip([ax1, ax2, ax3, ax4, ax5, ax6], [front, back, left, right, top, bottom]): ax.imshow(img, aspect='equal') ax.axis('off') plt.savefig(f"{directory}{num}.png", dpi=100, bbox_inches=None, pad_inches=0) plt.close()
PIL兼容NaN值的处理方案
PIL本身不直接识别浮点NaN值,但可以通过RGBA通道映射的方式,全程不将NaN转换为0值完成图像处理:
- 先计算数据中非NaN区域的数值上下限,作为彩色映射的归一化范围
- 单独创建Alpha通道,将原始数据中NaN对应的位置标记为透明(Alpha值0),非NaN位置标记为不透明(Alpha值255)
- 将彩色映射后的RGB数组与Alpha通道拼接为RGBA格式的uint8数组,即可直接传入PIL保存,透明区域会在PNG导出时正确保留,不会强制将NaN位置填充为0值
对应代码片段
import numpy as np from PIL import Image # 假设data是含NaN的气象数组 valid_mask = ~np.isnan(data) dmin, dmax = data[valid_mask].min(), data[valid_mask].max() # 归一化到0-255范围 norm_data = 255 * (data - dmin) / (dmax - dmin) # 生成Alpha通道:NaN位置透明,其余不透明 alpha = np.where(valid_mask, 255, 0).astype(np.uint8) # 拼接为RGBA图像(可根据需求替换为自定义colormap生成的RGB三通道) rgba = np.stack([norm_data.astype(np.uint8)]*3 + [alpha], axis=-1) img = Image.fromarray(rgba, mode='RGBA') img.save("output.png")
内容的提问来源于stack exchange,提问作者TheJeran
相关产品推荐
相关产品推荐

