Skip to content

图像就是矩阵

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

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


生活引入

照片的像素方格

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

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


直观理解

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

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

3x3 灰度图 一个矩阵,9 个数字

M=[0.00.51.00.30.70.60.80.20.4]

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

转置

img.T

沿对角线翻转

水平镜像

img[:, ::-1]

左右翻转

垂直镜像

img[::-1, :]

上下翻转

裁剪

img[10:54, 10:54]

截取子区域

模糊

卷积核滑动均值

邻域平均

旋转

坐标变换

矩阵乘法

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


数学定义

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

MRH×W,M_ij\[0,1\]

其中 M_ij 代表第 i 行、第 j 列的像素亮度值。

矩阵操作的核心概念:

操作数学表达NumPy 写法图像效果
转置MT_ij=M_jiimg.T行列互换,沿对角线翻转
水平翻转M_ij=M_i,W1jimg[:, ::-1]列索引反转
垂直翻转M_ij=M_H1i,jimg[::-1, :]行索引反转
均值模糊M_ij=1k2_p,qM_i+p,j+q滑动窗口求 mean()每个像素替换为邻域均值

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


Python 动手实践

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

实例

python

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")
python
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,本质上都是同一个东西:多维数字表格。理解了矩阵,就理解了深度学习框架里最基础的数据结构。

AI 思考中...

反向传播的数学本质

余弦相似度做文本推荐

基于 VitePress 构建,部署于 GitHub Pages