在图像分类、物体识别等人工智能实践中,图片通常需要先完成读取、尺寸调整与颜色模式转换,再转化为模型能够处理的数值数据。模糊、锐化和边缘检测则进一步通过像素计算改变图像表现。

本文使用 Python 图像处理工具,先介绍图像对象与基本操作,再重点说明滤镜、邻域计算和卷积原理,为理解图像预处理与卷积神经网络建立直观基础。

一、PIL 与 Pillow 概述

是 Python 图像库(Python Imaging Library)的简称,用于读取、处理和保存图像。现在通常使用它的后继项目 Pillow,代码中仍然沿用 PIL 作为导入名称。

安装:

pip install Pillow


Image 用于图像读取、创建、转换与保存;ImageDraw 用于绘制图形和文字;ImageOps 提供方向校正、翻转等常用操作;ImageFilter 提供滤镜:

from PIL import Image, ImageDraw, ImageOps, ImageFilter

参考阅读: 《》
二、图像对象的基本属性

一张图片进入程序后,会成为图像对象。理解它,首先需要掌握尺寸、模式与坐标。

1、图像尺寸

图像尺寸(Size)写成:

(width, height)

例如,(800, 600) 表示宽 800 像素、高 600 像素。

读取尺寸:

    

这里的数值是像素数量,不是图片打印出来的厘米尺寸。

2、图像模式

图像模式(Mode)说明像素数据的组织方式。

常见模式包括:

• L:8 位灰度图像,每个像素用一个数值表示,范围为 0~255,0 为黑色,255 为白色。

• RGB:红、绿、蓝三个通道,每个通道的范围为 0~255。

• RGBA:在 RGB 基础上增加透明度通道,0 表示完全透明,255 表示完全不透明。

• P:调色板模式,像素值表示调色板中的索引。

查看模式:

print(img.mode)

若模式为 RGB,则图像中的红色像素可以表示为:

(255, 0, 0)

3、图像坐标

PIL 以图像左上角为原点:

• 左上角像素为 (0, 0)。

• 向右移动,x 增大。

• 向下移动,y 增大。

因此,宽 800、高 600 像素的图像,右下角像素为 (799, 599)。

图像纵坐标向下增大,裁剪和像素定位时需注意这一方向约定。

三、基础图像绘制

Image.new 创建指定尺寸和底色的空白图;ImageDraw.Draw 获取绘图对象,用以绘制矩形、椭圆。

安装所需库:

pip install Pillow numpy

示例代码:

图像对象的 save() 方法用于保存文件,Image.open() 用于读取文件,np.array(img) 将图像转换为 NumPy 数组。img.size 给出图像的(宽,高);本例中,RGB 图像对应的 arr.shape 为(高,宽,通道数)。

四、基础图像操作示例

读取、裁剪、缩放、旋转和模式转换,可以放在同一个示例中理解。

下面假定方向校正后的图像至少宽 500、高 350 像素。运行前,应将 "photo.jpg" 替换为实际图片路径。

示例代码:

    

resize() 返回指定尺寸的新图像;thumbnail() 保持比例并原地修改对象。颜色模式决定像素表示方式,文件格式决定保存时的编码方式。

固定尺寸且需要保持比例时,可使用 ImageOps.fit() 缩放并裁剪,或使用 ImageOps.pad() 缩放并补边。具体选择应与模型的输入要求一致。

五、卷积核与卷积运算

许多基础滤镜通过卷积(Convolution)实现。它的基本思路是:用一组权重,对当前像素及其周围像素进行加权计算,得到输出图像对应位置的新值。这些权重通常排列成一个小矩阵,称为卷积核(Convolution Kernel)。

打开网易新闻 查看精彩图片

图 1:灰度图像单核卷积示意

例如,下面是一个 3 × 3 核:

核中的每个权重对应邻域中的一个像素。权重为 1,表示保留该像素值参与求和;为 0,表示该像素不直接贡献结果;为 −1,表示将该像素值取负后参与求和。

下面先采用“核不翻转,直接对应相乘再求和”的方式计算。这种方式严格来说称为互相关(Cross-correlation),也是许多卷积神经网络接口使用的计算方式。

假设输入的灰度图像中的一个邻域为:

核的中心与像素 100 对齐。首先,将邻域中的像素与对应权重逐元素相乘(也称“”,Hadamard Product),而不是进行普通矩阵乘法:

得到:

再将九个乘积直接相加:

因此,输入图像的中心像素原值为 100,经这组权重计算后,输出图像对应位置的新值为 −80。这个新值由整个邻域共同决定,而不只是由中心像素决定。

核移动到下一位置后,覆盖新的邻域,重复“逐元素相乘之后再求和”的过程,逐步生成输出图像。计算读取输入图像,并将新值写入输出图像,避免刚得到的结果影响后续计算。

不同的核权重可以产生平滑、锐化或边缘检测等效果。

本例使用含负权重的核,因此结果可能为负,也可能超过 255。若需要保留完整计算结果,应使用能够容纳这些数值的数组;保存为普通 8 位灰度图像时,还需要处理取值范围。

严格的数学卷积与上述计算略有不同:它需要先将核旋转 180°,再逐元素相乘并求和。

本例旋转后的核为:

对同一个邻域计算,得到:

因此,不对称核在卷积与互相关中可能得到不同结果;使用具体工具时,应确认其方向约定。两者的共同基础仍然是局部像素与权重对应相乘,再求和。

卷积神经网络(Convolutional Neural Network,CNN)也使用这种局部加权计算。与人为指定滤镜权重不同,神经网络中的核权重通常通过训练学习;卷积层还可以加入偏置(Bias),在加权总和上增加一个可学习的数值。

例如,在前面的互相关结果上加入偏置 20:

其基本关系为:

输 出 值 邻 域 加 权 总 和 偏 置

偏置用于调整输出的基准,并不保证结果为正。同一个输出通道的核权重和偏置在各空间位置共享,因此移动到不同位置时,仍使用同一套参数。

对于多通道输入,卷积层会汇总各通道的加权结果,再加上偏置,形成一个输出通道。

多个输出通道可以生成不同的特征图(Feature Map),再经过非线性处理与后续层的计算,逐步形成用于图像分类、物体识别等任务的特征表示。

六、常用滤镜的原理与实现

模糊、锐化和边缘检测都可以通过邻域加权计算理解:使用不同的核权重,逐元素相乘后求和,得到输出图像对应位置的新值。中值滤波也使用邻域,但采用排序取中值的规则,不属于卷积运算。

下面分别介绍这些滤镜的原理,并给出 Pillow 实现。

1、均值模糊

均值模糊(Mean Blur)对邻域中的像素取平均值,使局部变化更加平缓。

一个 3 × 3 均值核为:

九个像素的权重均为九分之一,因此输出值就是邻域平均值。对于第五章中的邻域,计算结果为:

中心像素原值为 100,输出对应位置的新值为 40。较亮的中心像素被周围像素拉近,因此局部突变减弱,画面变得平滑,但边缘与细节也可能变模糊。

核的权重和为 1,因此对于像素值相同的区域,其内部数值保持不变。

例如,九个像素都是 100,平均值仍为 100。

Pillow 可以通过自定义核实现均值模糊:

    

这里先使用权重 1 计算总和,再通过 scale=9 除以 9,等价于使用上述均值核。

2、高斯模糊

高斯模糊(Gaussian Blur)同样进行邻域加权计算,但各位置的权重不同,通常越靠近中心,权重越大。

下面的离散加权平滑核,可用于直观理解高斯平滑:

对于第五章中的邻域,计算过程为:

与均值模糊相比,中心像素的影响更大。高斯模糊通过随距离平缓变化的权重进行平滑,但仍可能削弱边缘与细节。

Pillow 提供了直接调用方式:

    

radius

3 × 3

3、锐化

锐化(Sharpening)通过增强局部像素差异,使轮廓和细节更加明显。

一个简单的锐化核为:

它对中心像素赋予较大的正权重,对上下左右四个邻居赋予负权重:

中 心 上 下 左 右

如果中心和四个邻居都为 100:

均匀区域保持不变。如果中心为 110,四个邻居都是 100:

中心比周围更亮的差异被增强了,因此局部轮廓更加突出。

Pillow 实现:

    

也可以使用内置滤镜:

result = gray.filter(ImageFilter.SHARPEN)

SHARPEN

锐化增强的是已有差异,不能恢复所有丢失的细节;过度使用还可能放大噪声或产生边缘光晕。

4、边缘检测

边缘检测(Edge Detection)用于突出像素值发生明显变化的位置。

例如,使用下面的核:

它计算中心像素的八倍,再减去周围八个像素之和。

对于第五章中的邻域:

这组核的权重和为 0。如果邻域中的像素值相同,输出为 0;存在明显变化时,则可能产生较大的正或负响应。因此,它会削弱均匀区域,突出局部变化。

Pillow 的内置 FIND_EDGES 使用了这组权重:

    

本例输出仍是 8 位灰度图像,因此负响应会被截为 0,超过 255 的响应会被截为 255。例如,上面的计算结果 540 最终会显示为 255,不能完整保留原始响应大小。

边缘也可能来自纹理或噪声,因此检测到局部变化,并不等于已经识别出物体。

5、中值滤波

中值滤波(Median Filtering)不使用固定核权重进行加权求和,而是将邻域像素排序,取中间位置的数值作为输出。

例如,九个像素排序后为:

[10, 10, 10, 10, 10, 10, 10, 10, 255]

它们的平均值约为 37,中值则为 10。因此,孤立的亮点不会像均值滤波那样明显拉高结果。

中值滤波常用于减弱椒盐噪声,也就是图像中零散出现的亮点与暗点。它属于非线性滤波,不能用一个固定卷积核等价表示。

Pillow 实现:

    

size=3 表示使用 3 × 3 邻域。增大邻域会改变处理范围,也可能损失更多细节,应根据噪声情况选择。

七、自定义滤镜的参数与使用要点
Pillow 的 ImageFilter.Kernel() 可以将自定义权重应用于图像。理解它的参数,有助于把前面的加权计算转化为实际代码。
1、核尺寸与权重

size 指定核的宽度和高度,目前支持 (3, 3) 和 (5, 5)。kernel 按行排列权重,元素数量必须与核尺寸一致。

例如,3 × 3 核需要九个权重:

    

RGB

2、缩放与偏移

scale 是加权总和的除数,offset 是除法完成后增加的偏移量。其计算关系为:

输 出 值 邻 域 加 权 总 和

scale=1

省略 scale 时,Pillow 默认使用核权重之和。对于权重和为 0 的边缘核,应显式设置 scale=1;对于由九个 1 组成的均值核,可设置 scale=9。为明确计算意图,教学示例建议显式指定非零的 scale。

offset 可以调整输出的基准。例如,加权总和为 −80,设置 scale=1、offset=128:

输 出 值

这种偏移可以让部分负响应进入可显示范围,但不能保证所有结果都落在 0~255,也不能完整保留超出范围的数值。它与第五章的加偏置形式相似,但这里由人指定,并不通过训练学习。
3、输出范围与图像边界

L 和 RGB 都采用每通道 8 位数据,输出范围为 0~255。自定义核产生的结果会转换为该范围内的整数,负值和超过 255 的值会被截断。

因此,Pillow 适合生成可显示的滤镜结果;如果需要保留带正负号或小数的完整计算结果,应使用浮点数组进行计算。

边界位置无法覆盖完整邻域。Pillow 的 ImageFilter.Kernel() 保留无法完整应用核的边界像素,不提供补零、镜像等边界方式的选择。比较滤镜效果时,应注意图像边缘与内部区域的处理差异。

4、核方向与处理顺序

Pillow 在应用自定义核前,会将传入的权重矩阵上下翻转。均值核、常见的对称平滑核,以及本文的锐化核和边缘核不受影响;第五章三行相同的核也不受影响。使用其他方向性核时,需要核对这一约定。

连续使用滤镜时,应将上一步的结果传给下一步:

这表示先减弱孤立噪点,再进行锐化。交换顺序可能得到不同结果,因为锐化可能先放大噪声,而中值滤波采用非线性规则。

在人工智能实践中,滤镜和参数应根据任务选择,并保持训练与推理时的预处理一致。

八、综合示例:滤镜效果比较

下面使用同一张 RGB 彩色图像,比较均值模糊、高斯模糊、锐化、边缘检测和中值滤波。各滤镜分别处理颜色通道,并保留彩色输出。

打开网易新闻 查看精彩图片

图 2:彩色图像多核卷积示意

安装所需库:

pip install Pillow matplotlib

示例代码:

观察时可以重点比较:

• 均值模糊与高斯模糊如何平滑细节和颜色过渡。

• 锐化如何增强轮廓,以及是否放大噪声或产生光晕。

• 边缘检测如何突出各颜色通道的局部变化,其结果不一定是白色轮廓。

• 原图存在孤立亮点、暗点或彩色噪点时,中值滤波如何改变这些区域。

不同滤镜会改变图像的细节和颜色表现,应结合处理任务选择,不能仅以画面是否更鲜艳或更清晰判断效果。

小结

PIL 图像处理通常通过 Pillow 完成。滤镜的关键是理解邻域计算:卷积核通过加权求和实现平滑、锐化与边缘响应,中值滤波则使用排序规则。掌握这些基础,有助于理解图像预处理与卷积神经网络。

“点赞有美意,赞赏是鼓励”