LeNet-5
LeNet-5被认为是卷积神经网络(CNN)的开山之作,主要用于手写数字识别。它包含了卷积层、池化层和全连接层,是现代CNN的雏形。
卷积
卷积神经网络(convolutional neural network)简称CNN。卷积神经网络的核心是卷积核,卷积核在图像处理领域可以用来提取图像的纵向和横向特征。
卷积核的大小一般为奇数,如3x3,5x5,7x7等,卷积核通常与图像处理(over padding)后的图像进行卷积操作,卷积核在图像上滑动,每次滑动一个像素,对应位置的像素值与卷积核对应位置的值相乘,然后求和,最后将求和的结果作为卷积核中心像素的值,这样就得到了一个新的图像。
新的图像可以用更少的数据反应出图像的特征。这个过程就是特征提取。
我们从一个6x6的矩阵开始:
我们的卷积核是一个3x3的矩阵:
我们假设卷积核位于原始矩阵的左上角,覆盖的区域如下:
此时,输出矩阵的第一个元素的计算为:
整个输出矩阵
卷积核在整个6x6矩阵上滑动(从左至右,从上至下),生成一 个4x4的输出矩阵。输出矩阵的每个元素都按照上述方式计算。
点击查看卷积核动画
// 你可以尝试更改矩阵尺寸与卷积核的尺寸来感受卷积过程
function example(props) {
// 使用 XPath 查询选择输出框
const xpathSelector =
"/html/body/div/div[2]/div/div/main/div/div/div/div/article/div[2]/div[1]/div[4]";
const myElement = document.evaluate(
xpathSelector,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
// 矩阵尺寸
const matrixSize = 6;
// 卷积核尺寸
const kernelSize = 3;
const matrix = Array.from({ length: matrixSize }, (_, i) =>
Array.from({ length: matrixSize }, (_, j) => `a${i + 1}${j + 1}`)
);
const [position, setPosition] = useState([0, 0]);
useEffect(() => {
const positions = [];
for (let i = 0; i <= matrixSize - kernelSize; i++) {
for (let j = 0; j <= matrixSize - kernelSize; j++) {
positions.push([i, j]);
}
}
let index = 0;
const interval = setInterval(() => {
setPosition(positions[index]);
index = (index + 1) % positions.length;
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh', backgroundColor: '#f0f0f0' }}>
<div style={{ display: 'grid', gridTemplateColumns: `repeat(${matrixSize}, 50px)`, gridGap: '5px', position: 'relative' }}>
{matrix.map((row, i) =>
row.map((cell, j) => (
<div
key={`${i}-${j}`}
style={{
width: '50px',
height: '50px',
backgroundColor: '#fff',
border: '1px solid #ccc',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: '18px',
backgroundColor: i >= position[0] && i < position[0] + kernelSize && j >= position[1] && j < position[1] + kernelSize ? 'yellow' : '#fff'
}}
>
{cell}
</div>
))
)}
</div>
</div>
);
}
最终输出矩阵为:
每个的具体计算方法如前所述,通过卷积核在原始矩阵上的滑动和计算得到。
通过这个例子,可以清晰地看到卷积核是如何对矩阵进行操作并生成输出的。
常见卷积核
-
水平边缘检测:
用途:检测水平边缘。
-
垂直边缘检测:
用途:检测垂直边缘。
-
Sobel算子(水平):
用途:检测水平边缘和梯度。
-
Sobel算子(垂直):
用途:检测垂直边缘和梯度。
-
拉普拉斯算子:
用途:检测图像的二阶导数,强调边缘。
-
锐化:
用途:提高图像的清晰度。
-
高斯模糊(3x3):
用途:平滑图像,减少噪声。
-
高斯模糊(5x5):
用途:更强的平滑效果。
-
边缘增强:
用途:增强边缘,使图像轮廓更加明显。
-
均值滤波:
用途:均匀地平滑图像。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import cv2
# 设置中文字体
# 替换为你系统中支持中文的字体路径(windows)
font_path = r'C:\Windows\Fonts\simhei.ttf'
# mac(如果有的话)
# font_path = '/System/Library/Fonts/STHeiti Light.ttc'
font_prop = FontProperties(fname=font_path)
# 读取灰度图像
image = np.array(cv2.imread('data/people.bmp',cv2.IMREAD_GRAYSCALE))
# 定义卷积核
kernels = {
'水平边缘': np.array([[-1, -1, -1], [0, 0, 0], [1, 1, 1]]),
'垂直边缘': np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]]),
'Sobel水平': np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]),
'Sobel垂直': np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]]),
'拉普拉斯': np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]]),
'锐化': np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]),
'高斯模糊3x3': np.array([[1, 2, 1], [2, 4, 2], [1, 2, 1]]) / 16,
'高斯模糊5x5': np.array([[1, 4, 6, 4, 1], [4, 16, 24, 16, 4], [6, 24, 36, 24, 6], [4, 16, 24, 16, 4], [1, 4, 6, 4, 1]]) / 256,
'边缘增强': np.array([[-1, -1, -1], [-1, 9, -1], [-1, -1, -1]]),
'均值滤波': np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]) / 9
}
# 使用NumPy实现卷积操作
def convolve2d(image, kernel):
# 获取图像和卷积核的尺寸
i_height, i_width = image.shape
k_height, k_width = kernel.shape
# 计算输出图像的尺寸
o_height = i_height - k_height + 1
o_width = i_width - k_width + 1
# 创建输出图像
output = np.zeros((o_height, o_width))
# 执行卷积操作
for y in range(o_height):
for x in range(o_width):
# 提取图像区域
region = image[y:y+k_height, x:x+k_width]
# 计算卷积值
output[y, x] = np.sum(region * kernel)
return output
# 应用卷积核
results = {}
for name, kernel in kernels.items():
# 为了处理边界,先对图像进行填充
if kernel.shape[0] == 5: # 对于5x5卷积核
pad_width = 2
else: # 对于3x3卷积核
pad_width = 1
padded_image = np.pad(image, pad_width, mode='constant')
filtered_image = convolve2d(padded_image, kernel)
# 归一化处理,确保像素值在有效范围内
filtered_image = np.clip(filtered_image, 0, 255).astype(np.uint8)
results[name] = filtered_image
# 显示结果
plt.figure(figsize=(15, 8))
for i, (name, result) in enumerate(results.items()):
plt.subplot(3, 4, i + 1)
plt.imshow(result, cmap='gray')
plt.title(name, fontproperties=font_prop)
plt.axis('off')
plt.tight_layout()
plt.show()