如何在Matplotlib/Python中显示或保存多幅含多子图的图像
嘿,这事儿我熟!在Jupyter里处理这个需求特别顺手,我给你捋个清晰的流程,再附上可直接用的代码片段,你照着改改就能用啦~
首先咱们把整个流程拆成几块:预处理图像并存入units、提取指定卷积层的激活值、为每幅图像生成子图并展示/保存。
第一步:预处理图像并存储到units
先写个预处理函数,把你的图像做裁剪、归一化这些操作,然后把处理好的图像(还有方便后续处理的辅助信息)存到units里——units可以是个列表,每个元素用字典封装图像数据、文件名,这样后续操作更清晰。
import cv2 import numpy as np from tensorflow.keras.applications import VGG16 from tensorflow.keras.models import Model import matplotlib.pyplot as plt import math import os # 示例:初始化模型(你可以换成自己的模型) base_model = VGG16(weights='imagenet', include_top=False) target_layer = base_model.get_layer('block3_conv1') # 指定你要查看的卷积层 activation_model = Model(inputs=base_model.input, outputs=target_layer.output) # 预处理函数(按需修改裁剪、归一化逻辑) def preprocess_image(img_path, crop_size=(224, 224)): # 读取并转RGB img = cv2.imread(img_path) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 中心裁剪(换成你需要的裁剪方式即可) h, w = img.shape[:2] start_x = (w - crop_size[0]) // 2 start_y = (h - crop_size[1]) // 2 cropped_img = img[start_y:start_y+crop_size[1], start_x:start_x+crop_size[0]] # 归一化(适配VGG16,换成你模型的要求) preprocessed = cropped_img.astype('float32') preprocessed = np.expand_dims(preprocessed, axis=0) preprocessed /= 255.0 return cropped_img, preprocessed # 把图像存入units units = [] # 替换成你的图像路径列表 image_paths = ['img1.jpg', 'img2.jpg', 'img3.jpg'] for path in image_paths: cropped, preprocessed = preprocess_image(path) units.append({ 'cropped_img': cropped, 'preprocessed_input': preprocessed, 'filename': path.split('/')[-1].split('.')[0] # 提取文件名用于保存 })
第二步:为每幅图像生成激活子图并处理
接下来遍历units里的每一个图像,提取激活值,动态计算子图布局(不管激活通道有多少,都能自动排版),然后在Jupyter里显示,或者批量保存到指定目录。
# 配置参数 save_images = True save_dir = './activation_plots/' os.makedirs(save_dir, exist_ok=True) # 自动创建保存目录 # 遍历处理每幅图像 for unit in units: # 提取激活值(去掉batch维度) activations = activation_model.predict(unit['preprocessed_input'])[0] num_channels = activations.shape[-1] # 计算子图网格大小:尽量排成方正的网格 grid_size = math.ceil(math.sqrt(num_channels)) fig, axes = plt.subplots(grid_size, grid_size, figsize=(15, 15)) fig.suptitle(f'Activations for {unit["filename"]} - Layer: {target_layer.name}', fontsize=16) # 逐个绘制通道激活图 for i in range(num_channels): row = i // grid_size col = i % grid_size ax = axes[row, col] ax.imshow(activations[:, :, i], cmap='viridis') ax.axis('off') ax.set_title(f'Channel {i+1}') # 隐藏多余的空白子图 for i in range(num_channels, grid_size*grid_size): row = i // grid_size col = i % grid_size axes[row, col].axis('off') # Jupyter内显示图像 plt.tight_layout() plt.show() # 保存图像到指定目录 if save_images: save_path = os.path.join(save_dir, f'{unit["filename"]}_activations.png') fig.savefig(save_path, bbox_inches='tight', dpi=100) plt.close(fig) # 关闭画布释放内存,处理大量图像时必加
关键细节说明
- 动态布局适配:用
math.ceil(math.sqrt(num_channels))自动计算网格大小,不管你有多少个激活通道,都能生成整齐的子图布局。 - Jupyter显示优化:先运行
%matplotlib inline(经典Notebook)或%matplotlib widget(交互式),图像会直接显示在单元格里。 - 内存管理:处理完每幅图像后用
plt.close(fig)关闭画布,避免处理大量图像时内存溢出。 - 自定义调整:你可以随意替换预处理逻辑、模型、目标层,还有子图的颜色映射(
cmap参数)、画布大小这些,完全适配你的需求。
这样一来,不管你输入多少幅图像,都能自动为每幅生成对应的激活子图,既可以实时查看,也能批量保存到指定目录~
内容的提问来源于stack exchange,提问作者ProddoBaggins
相关产品推荐
相关产品推荐

