现在位置: 首页 > AI 数学基础 > 正文

图像就是矩阵

一张灰度图片本质上就是一个二维 NumPy 数组。对矩阵做转置、翻转、裁剪、卷积——图片就会跟着变。

学完本案例你将理解:图像处理的底层就是线性代数,矩阵就是数字表格。


生活引入

照片的像素方格

把你手机里的一张照片放大、再放大——直到你能看到一个个小方格。每个方格只有一个颜色值:0 代表纯黑,255 代表纯白,中间的数字是不同深浅的灰色。

整张照片就是这些小方格排成的矩形阵列——数学家称之为「矩阵」。


直观理解

矩阵不是什么高深的概念——它就是一张填满数字的表格。

比如下面这个 \(3 \times 3\) 的矩阵,每个数字代表一个像素的亮度(0 最暗,1 最亮):

3x3 灰度图 一个矩阵,9 个数字
\[ M = \begin{bmatrix} 0.0 & 0.5 & 1.0 \\ 0.3 & 0.7 & 0.6 \\ 0.8 & 0.2 & 0.4 \end{bmatrix} \]

有了这个认知,图像处理的各种操作就豁然开朗了:

转置
img.T
沿对角线翻转
水平镜像
img[:, ::-1]
左右翻转
垂直镜像
img[::-1, :]
上下翻转
裁剪
img[10:54, 10:54]
截取子区域
模糊
卷积核滑动均值
邻域平均
旋转
坐标变换
矩阵乘法

你在 Photoshop 里点一个按钮,背后都是 NumPy 在操作一个二维数组。


数学定义

一张 \(H \times W\) 的灰度图像可以表示为一个实数矩阵:

\[ M \in \mathbb{R}^{H \times W}, \quad M_{ij} \in [0, 1] \]

其中 \(M_{ij}\) 代表第 \(i\) 行、第 \(j\) 列的像素亮度值。

矩阵操作的核心概念:

操作数学表达NumPy 写法图像效果
转置\(M^T_{ij} = M_{ji}\)img.T行列互换,沿对角线翻转
水平翻转\(M'_{ij} = M_{i, W-1-j}\)img[:, ::-1]列索引反转
垂直翻转\(M'_{ij} = M_{H-1-i, j}\)img[::-1, :]行索引反转
均值模糊\(M'_{ij} = \frac{1}{k^2}\sum_{p,q} M_{i+p, j+q}\)滑动窗口求 mean()每个像素替换为邻域均值

均值模糊用到的「滑动窗口」操作,本质上就是卷积的雏形——卷积神经网络(CNN)的核心运算。


Python 动手实践

生成一张合成灰度图,用 NumPy 数组操作实现转置、翻转、裁剪和手写卷积模糊。

实例

import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import os

# 创建输出目录
OUT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outputs")
os.makedirs(OUT, exist_ok=True)

# 1. 生成一张合成灰度图:对角渐变 + 中间亮方块
size = 64
img = np.zeros((size, size))
for i in range(size):
    for j in range(size):
        img[i, j] = (i + j) / (2 * size)  # 从暗到亮的对角渐变
img[20:44, 20:44] += 0.6                  # 中间加一个亮方块
img = np.clip(img, 0, 1)                  # 限制在 [0, 1] 范围内

print("RUNOOB 图像矩阵形状 (shape):", img.shape)
print("图像矩阵前 3x3 部分:\n", np.round(img[:3, :3], 3))

# 2. 矩阵运算 = 图像变换
img_transpose = img.T           # 转置:行列互换
img_flip_lr = img[:, ::-1]      # 水平镜像:列索引反转
img_flip_ud = img[::-1, :]      # 垂直镜像:行索引反转
img_crop = img[10:54, 10:54]    # 裁剪:切片取子矩阵

# 3. 手写均值模糊(5x5 卷积核,不调 OpenCV)
def box_blur(mat, k=5):
    """用 k x k 滑动窗口对矩阵做均值模糊"""
    pad = k // 2
    # 边界用边缘值填充(edge padding)
    padded = np.pad(mat, pad, mode="edge")
    out = np.zeros_like(mat)
    for i in range(mat.shape[0]):
        for j in range(mat.shape[1]):
            # 取 k x k 邻域,求均值
            out[i, j] = padded[i:i+k, j:j+k].mean()
    return out

img_blur = box_blur(img, k=5)
print("RUNOOB 模糊后矩阵前 3x3 部分:\n",
      np.round(img_blur[:3, :3], 3))

# 4. 保存可视化对比图
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
titles = ["原图 (矩阵)", "转置 img.T",
          "水平镜像 img[:, ::-1]",
          "垂直镜像 img[::-1, :]",
          "裁剪 img[10:54, 10:54]",
          "均值模糊 (5x5 卷积)"]
images = [img, img_transpose, img_flip_lr,
          img_flip_ud, img_crop, img_blur]

for ax, title, im in zip(axes.flat, titles, images):
    ax.imshow(im, cmap="gray", vmin=0, vmax=1)
    ax.set_title(title, fontsize=11)
    ax.axis("off")

plt.tight_layout()
plt.savefig(os.path.join(OUT, "01_image_as_matrix.png"), dpi=130)
print(f"\nRUNOOB 可视化结果已保存到 {OUT}/01_image_as_matrix.png")
RUNOOB 图像矩阵形状 (shape): (64, 64)
图像矩阵前 3x3 部分:
 [[0.    0.008 0.016]
 [0.008 0.016 0.023]
 [0.016 0.023 0.031]]
RUNOOB 模糊后矩阵前 3x3 部分:
 [[0.009 0.011 0.013]
 [0.01  0.013 0.016]
 [0.011 0.016 0.021]]
RUNOOB 可视化结果已保存到 .../outputs/01_image_as_matrix.png

可以看到,模糊后相邻像素之间的数值差异变小了——这正是「模糊」的数学本质:用邻域均值替代原像素值


AI 中的应用场景

AI 场景如何使用矩阵
卷积神经网络 (CNN)输入是一批图片组成的 4 阶张量 (batch, H, W, C),每一层用卷积核(小矩阵)在输入上滑动
数据增强 (Augmentation)训练时随机翻转、旋转、裁剪图片——全部是矩阵操作,用于扩充训练集
图像预处理送入模型前统一裁剪到固定尺寸(如 224x224)——就是矩阵切片和缩放
风格迁移将图片表示为特征矩阵,通过矩阵运算改变「风格」特征

NumPy 中的 ndarray 和 PyTorch 中的 Tensor,本质上都是同一个东西:多维数字表格。理解了矩阵,就理解了深度学习框架里最基础的数据结构。