1.图像卷积
![image]()
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageio # 使用新版API避免警告
import os
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] # Windows系统中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# ===================== 1. 定义数据 =====================
# 5*5 原始图像
img = np.array([
[10, 20, 30, 40, 50],
[15, 25, 35, 45, 55],
[20, 30, 40, 50, 60],
[25, 35, 45, 55, 65],
[30, 40, 50, 60, 70]
])
# 3*3 卷积核(垂直边缘检测)
kernel = np.array([
[1, 0, -1],
[1, 0, -1],
[1, 0, -1]
])
img_h, img_w = img.shape
k_h, k_w = kernel.shape
out_h = img_h - k_h + 1
out_w = img_w - k_w + 1
# 存储每一帧画面
frames = []
# ===================== 2. 逐滑动窗口绘制帧 =====================
for y in range(out_h):
for x in range(out_w):
# 截取当前卷积窗口
window = img[y:y+k_h, x:x+k_w]
# 卷积计算:对应相乘求和
conv_val = np.sum(window * kernel)
# 创建画布
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 4))
fig.suptitle("卷积运算滑动过程", fontsize=14)
# 左图:原始图像 + 框选当前窗口
ax1.imshow(img, cmap="gray", vmin=0, vmax=80)
ax1.set_title("原始图像 (5×5)")
# 绘制红色框标记卷积区域
rect = plt.Rectangle((x-0.5, y-0.5), k_w, k_h,
fill=False, color="red", linewidth=3)
ax1.add_patch(rect)
ax1.set_xticks([])
ax1.set_yticks([])
# 中图:当前3×3图像块 + 卷积核
ax2.imshow(window, cmap="gray", vmin=0, vmax=80)
ax2.set_title(f"当前图像块 (3×3)\n卷积值 = {conv_val}")
ax2.set_xticks([])
ax2.set_yticks([])
# 右图:卷积核(特征矩阵)
ax3.imshow(kernel, cmap="gray")
ax3.set_title("卷积核(特征矩阵) (3×3)")
ax3.set_xticks([])
ax3.set_yticks([])
plt.tight_layout()
# 保存单帧为临时图片
temp_path = "temp_frame.png"
plt.savefig(temp_path, dpi=100, bbox_inches="tight")
plt.close(fig)
# 读取帧并加入列表
frames.append(imageio.imread(temp_path))
# ===================== 3. 合成GIF动图 =====================
gif_path = "conv_process.gif"
imageio.mimsave(gif_path, frames, duration=1.2) # 每帧停留1.2秒
# 删除临时文件
os.remove("temp_frame.png")
print(f"✅ 卷积过程GIF已生成:{os.path.abspath(gif_path)}")
![conv_process]()
2.实际图像
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageio # 使用新版API避免警告
import os
from PIL import Image
# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'] # Windows系统中文字体
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# ===================== 配置项(自行修改这里)=====================
# 1. 替换为你的肖像图片路径
IMG_PATH = "portrait.jpg"
# 2. 统一缩放到 200x200(方便观察滑动,可按需改)
RESIZE_W, RESIZE_H = 200, 200
# 3. 卷积核 3×3 垂直边缘检测(特征矩阵)
kernel = np.array([
[1, 0, -1],
[1, 0, -1],
[1, 0, -1]
])
k_h, k_w = kernel.shape
# 每帧停留时长(秒)
FRAME_DURATION = 0.8
# 每隔多少像素滑动一次(步长),越大GIF帧数越少、播放越快
STRIDE = 5
# =================================================================
# 1. 加载图片 → 灰度图 → 转numpy数组
img = Image.open(IMG_PATH).convert("L")
img = img.resize((RESIZE_W, RESIZE_H))
img = np.array(img, dtype=np.float32)
img_h, img_w = img.shape
# 计算输出特征图尺寸
out_h = (img_h - k_h) // STRIDE + 1
out_w = (img_w - k_w) // STRIDE + 1
frames = []
temp_file = "temp_frame.png"
# 2. 逐窗口滑动、绘图、保存帧
print("开始生成GIF帧...")
for y in range(0, img_h - k_h + 1, STRIDE):
for x in range(0, img_w - k_w + 1, STRIDE):
# 截取当前 3×3 图像块
window = img[y:y+k_h, x:x+k_w]
# 卷积计算
conv_val = np.sum(window * kernel)
# 绘图布局:原图 | 当前图像块 | 卷积核
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 5))
fig.suptitle("真实肖像图 + 卷积滑动过程", fontsize=15)
# 左:整张肖像图 + 红色框标记当前卷积区域
ax1.imshow(img, cmap="gray")
rect = plt.Rectangle((x-0.5, y-0.5), k_w, k_h,
fill=False, color="red", linewidth=2.5)
ax1.add_patch(rect)
ax1.set_title("原始肖像图")
ax1.set_xticks([])
ax1.set_yticks([])
# 中:当前 3×3 图像块
ax2.imshow(window, cmap="gray")
ax2.set_title(f"当前 3×3 图像块\n卷积输出值: {conv_val:.2f}")
ax2.set_xticks([])
ax2.set_yticks([])
# 右:卷积核(特征矩阵)
ax3.imshow(kernel, cmap="gray")
ax3.set_title("3×3 卷积核(特征矩阵)")
ax3.set_xticks([])
ax3.set_yticks([])
plt.tight_layout()
plt.savefig(temp_file, dpi=100, bbox_inches="tight")
plt.close(fig) # 及时关闭图形以释放内存
# 加入帧列表
frames.append(imageio.imread(temp_file))
# 显示进度
total_frames = ((img_h - k_h) // STRIDE + 1) * ((img_w - k_w) // STRIDE + 1)
current_frame = len(frames)
if current_frame % 10 == 0: # 每10帧显示一次进度
print(f"已生成 {current_frame}/{total_frames} 帧")
# 3. 合成GIF动图
gif_save_path = "portrait_conv.gif"
imageio.mimsave(gif_save_path, frames, duration=FRAME_DURATION)
# 删除临时文件
if os.path.exists(temp_file):
os.remove(temp_file)
print(f"✅ 肖像图卷积演示GIF生成完成!\n路径:{os.path.abspath(gif_save_path)}")
![portrait]()
![image]()